用新画布创建画布副本的方法是作为第一个画布的子元素的盒子吗?
第一画布
第二画布
我想要我在第二张画布上展示的东西。
发布于 2016-01-05 13:26:10
当我还是孩子的时候,我有一个有台词的答案,但是我想要一个更好的解决方案。
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);
}
发布于 2016-01-05 02:41:51
您可以通过从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
代码隐藏中的这些更改。
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
添加/移除子元素。您可能需要为添加的元素保留一个绑定的全局列表,并且在删除元素时可能需要考虑销毁绑定。
代码隐藏
最后,我们需要处理公开发布的通知,以便更新目标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秒发生属性更改(请参阅代码隐藏),下面是更改发生后的窗口:
https://stackoverflow.com/questions/34602079
复制相似问题