簡體   English   中英

WPF帆布兒童收納盒

[英]WPF Canvas children rect box

有什么方法可以將新畫布作為第一個畫布的子元素框創建畫布副本?

第一畫布

第一畫布

第二畫布

第二畫布

我想要類似的東西在第二個畫布中顯示。

您可以通過從Canvas派生並重寫OnVisualChildrenChanged

您必須公開更改,以便可以正確更新復制的Canvas (將其稱為目標Canvas )。

如果您需要偵聽源畫布包含其屬性已更新的子項時的更改,那么您想要做的事情就相當復雜。 在這種情況下,您必須再次通知您的目標畫布更改,這可以通過添加綁定來完成。

XAML

<Window x:Class="StackOverflow.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:StackOverflow"
    Title="MainWindow" Height="437.042" Width="525">
    <Grid>
        <Border Height="213" Margin="10,10,10,0" VerticalAlignment="Top" BorderBrush="Black" BorderThickness="1">
            <local:ObservableCanvas x:Name="CanvasSource"
                VisualChildrenChanged="CanvasSource_VisualChildrenChanged">

                <!-- Add some elements -->
                <Ellipse Width="10" Height="10" Fill="Red"/>
                <Ellipse Canvas.Left="10" Canvas.Top="10"
                         Width="20" Height="20" Fill="Green"/>
                <Ellipse Canvas.Left="30" Canvas.Top="30"
                         Width="30" Height="30" Fill="Blue"/>
                <Ellipse Canvas.Left="60" Canvas.Top="60"
                         Width="40" Height="40" Fill="Yellow"/>
            </local:ObservableCanvas>
        </Border>

        <Border Margin="148,228,148,10" BorderBrush="Black" BorderThickness="1">
            <Canvas x:Name="CanvasTarget" Loaded="CanvasTarget_Loaded"/>
        </Border>
    </Grid>
</Window>

在設計時,這是窗口: 窗口-設計時間

特別要注意VisualChildrenChanged="CanvasSource_VisualChildrenChanged" 這是將更改公開的方式。 我們將在MainWindow的后台代碼中處理這些更改。

可觀察的畫布

//This class notifies listeners when child elements are added/removed & changed.
public class ObservableCanvas : Canvas, INotifyPropertyChanged
{
    //Implement INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;

    //This function should be called when a child element has a property updated.
    protected virtual void RaisePropertyChanged(string name)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(name));
        }
    }

    //Create a routed event to indicate when the ObservableCanvas has changes to its child collection.
    public static readonly RoutedEvent VisualChildrenChangedEvent = EventManager.RegisterRoutedEvent(
        "VisualChildrenChanged",
        RoutingStrategy.Bubble,
        typeof(RoutedEventHandler),
        typeof(ObservableCanvas));

    //Create CLR event handler.
    public event RoutedEventHandler VisualChildrenChanged
    {
        add { AddHandler(VisualChildrenChangedEvent, value); }
        remove { RemoveHandler(VisualChildrenChangedEvent, value); }
    }

    //This function should be called to notify listeners
    //to changes to the child collection.
    protected virtual void RaiseVisualChildrenChanged()
    {
        RaiseEvent(new RoutedEventArgs(VisualChildrenChangedEvent));
    }

    //Override to make the changes public.
    protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved)
    {
        base.OnVisualChildrenChanged(visualAdded, visualRemoved);

        //Create bindings here to properties you need to monitor for changes.
        //This example shows how to listen for changes to the Fill property.
        //You may need to add more bindings depending on your needs.
        Binding binding = new Binding("Fill");
        binding.Source = visualAdded;
        binding.NotifyOnTargetUpdated = true;
        binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
        SetBinding(Shape.FillProperty, binding);

        //Instruct binding target updates to cause a global property
        //update for the ObservableCanvas.
        //This will make child property changes visible to the outside world.
        Binding.AddTargetUpdatedHandler(this,
            new EventHandler<DataTransferEventArgs>((object sender, DataTransferEventArgs e) =>
        {
            RaisePropertyChanged("Fill");
        }));

        //Notify listeners that the ObservableCanvas had an item added/removed.
        RaiseVisualChildrenChanged();
    }
}

如上所示,此類主要處理將子元素添加到ObservableCanvas /從ObservableCanvas移除子元素的操作。 您可能需要為添加的元素保留一個全局的綁定列表,並且可能需要考慮在刪除元素時銷毀綁定。

代碼隱藏

最后,我們需要處理公開發出的通知,以便可以更新目標Canvas 為簡單起見,我們在MainWindow的后台代碼中執行此操作。

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        //Demonstrate that children added from inside the code-behind are reflected.
        Rectangle newRectangle = new Rectangle() { Width = 50, Height = 50, Fill = Brushes.Orange };
        CanvasSource.Children.Add(newRectangle);
        Canvas.SetLeft(newRectangle, 100);
        Canvas.SetTop(newRectangle, 100);

        CanvasSource.PropertyChanged += CanvasSource_PropertyChanged;

        //Also, demonstrate that child property changes can be seen.
        DispatcherTimer timer = new DispatcherTimer() { Interval = TimeSpan.FromSeconds(2) };
        timer.Tick += (sender, args) =>
        {
            timer.Stop();
            newRectangle.Fill = Brushes.Brown;
        };
        timer.Start();
    }

    //This event handler is called when a child is added to or removed from
    //the ObservableCanvas.
    private void CanvasSource_VisualChildrenChanged(object sender, RoutedEventArgs e)
    {
        ObservableCanvas source = sender as ObservableCanvas;
        if (source == null) return;
        CopyElements(source);
    }

    //This event handler is called when a child element of the ObservableCanvas
    //has a property that changes.
    void CanvasSource_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        ObservableCanvas source = sender as ObservableCanvas;
        if (source == null) return;
        CopyElements(source);
    }

    //This event handler is used to ensure that the target Canvas
    //has the elements copied from the source when initialized.
    private void CanvasTarget_Loaded(object sender, RoutedEventArgs e)
    {
        CopyElements(CanvasSource);
    }

    //This function creates a brand new copy of the ObservableCanvas's
    //children and puts it into the target Canvas.
    private void CopyElements(ObservableCanvas source)
    {
        if (CanvasTarget == null) return;

        CanvasTarget.Children.Clear();  //Start from scratch.
        foreach (UIElement child in source.Children)
        {
            //We need to create a deep clone of the elements to they copy.
            //This is necessary since we can't add the same child to two different
            //UIlements.
            UIElement clone = (UIElement)XamlReader.Parse(XamlWriter.Save(child));
            CanvasTarget.Children.Add(clone);
        }
    }
}

最終結果

這是啟動時的窗口。 它具有目標Canvas元素的副本。

窗口-運行時間

而且由於我們在初始化后2秒鍾發生了屬性更改(請參見后面的代碼),因此發生更改后的窗口如下:

窗口-2秒后運行時間

我有一個關於兒童的台詞的答案,但是我想要一個更好的解決方案。

Point firstPoint = new Point(DrawCanvas.ActualWidth, DrawCanvas.ActualHeight);
Point endPoint = new Point(0, 0);
foreach (Line element in DrawCanvas.Children)
{

    double x = element.X1;
    double y = element.Y1;


    if (x < firstPoint.X)
        firstPoint.X = x;

    if (y < firstPoint.Y)
        firstPoint.Y = y;

    if (element.X2 > endPoint.X)
        endPoint.X = element.X2;

    if (element.Y2 > endPoint.Y)
        endPoint.Y = element.Y2;
}

double offsetX = firstPoint.X - 5;
double offsetY = firstPoint.Y - 5;


var children = DrawCanvas.Children.Cast<Line>().ToArray();
DrawCanvas.Children.Clear();

Rectangle rect = new Rectangle();
rect.Stroke = new SolidColorBrush(Color.FromRgb(0, 111, 0));
rect.Fill = new SolidColorBrush(Color.FromRgb(0, 111, 111));
rect.Width = endPoint.X - offsetX + 5;
rect.Height = endPoint.Y - offsetY + 5;
Canvas.SetLeft(rect, 0);
Canvas.SetTop(rect, 0);

newCanvas.Children.Add(rect);
newCanvas.Width = rect.Width;
newCanvas.Height = rect.Height;

foreach (Line element in children)
{
    Line newLine = element;
    newLine.X1 = element.X1 - offsetX;
    newLine.X2 = element.X2 - offsetX;
    newLine.Y1 = element.Y1 - offsetY;
    newLine.Y2 = element.Y2 - offsetY;

    newCanvas.Children.Add(newLine);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM