2016-01-05 5 views
0

Есть ли способ создать копию холста с новым холстом, будет представлять собой коробку дочерних элементов первого холста?WPF Canvas children rect box

first canvas

первый холст

second canvas

второй холст

я хочу что-то вроде, что я показываю во втором холсте.

+0

Немного неясно, что вам нужно. Вам нужны два холста, но одна (копия) содержит копии детей в первом полотне? В чем разница между двумя полотнами? –

+0

размер холста и (canvas.top, canvas.left каждого дочернего элемента) – gmetax

+0

Если я могу спросить, что вы планируете делать с этими холстами? Я реализовал решение, но мне интересно, как его использовать. Я не верю, что есть простое решение вашей проблемы, если проблема не решена из-за другого подхода. –

ответ

0

У меня есть ответьте на строки как дети, но я хочу лучшее решение.

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); 
} 
0

Вы можете сделать это путем получения 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> 

Во время разработки, это окно: Window - Design Time

В частности, обратите внимание на VisualChildrenChanged="CanvasSource_VisualChildrenChanged". Так публикуются изменения. Мы обрабатываем эти изменения в коде для MainWindow.

ObservableCanvas
//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. Возможно, вам потребуется сохранить глобальный список привязок для добавленных элементов, и вам может потребоваться уничтожить привязки при удалении элементов.

Code-Behind

Наконец, мы должны обрабатывать публично сделанные уведомления, так что целевой 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.

Window - Run Time

И поскольку мы имеем изменение свойств происходит через 2 секунды после инициализации (см код-за), вот это окно после того, как происходит изменение:

Window - Run Time After 2 Seconds