I've played a bit with WPF. What I want to do is drag a custom control from a template palette window to the canvas of another window and create a clone of that custom control there. This is very much like what you would see in Visio.
In order to clone the template control from the palette I abuse the serialization and deserialization features of XamlWriter and XamlReader.
In my drop-callback I have the following code:
private void drawingCanvas_Drop(object sender, DragEventArgs e)
{
DrawingShape shape = (DrawingShape)e.Data.GetData(typeof(DrawingShape));
if (shape != null)
{
Canvas target = this.drawingCanvas;
Point p = e.GetPosition(target);
string xamlString = System.Windows.Markup.XamlWriter.Save(shape);
DrawingShape c = (DrawingShape)System.Windows.Markup.XamlReader.Parse(xamlString);
Canvas.SetLeft(c, p.X - c.Width / 2);
Canvas.SetTop(c, p.Y - c.Height / 2);
target.Children.Add(c);
this.shapesPalette.shapesGallery.ClearValue(ListView.SelectedIndexProperty);
}
}
My User Control looks like this:
<UserControl x:Class="MyNamespace.Graphics.DrawingShape"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="60" d:DesignWidth="60">
<Grid Width="Auto" Height="Auto" >
<Grid.RowDefinitions>
<RowDefinition Height="38"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Canvas HorizontalAlignment="Center" Grid.Row="0" Width="38" Height="38" Canvas.Left="0" Canvas.Top="0">
<Path x:Name="Path" Width="35.8774" Height="31.2047" Canvas.Left="1.0613" Canvas.Top="3.29528" Stretch="Fill" StrokeLineJoin="Round" Stroke="#FF230FD2" Fill="#FF0096FF" Data="F1 M 19,3.79528L 1.5613,34L 36.4387,34L 19,3.79528 Z "/>
</Canvas>
<Label x:Name="Label" HorizontalAlignment="Center" Grid.Row="1">MyLabel</Label>
</Grid>
</UserControl>
However, Canvas.SetLeft()
and Canvas.SetTop()
don't do anything. The clone is always added to the top left corner of the target canvas. If I use any other WPF-Control instead of my User Control this code works fine.
I guess, there is a problem with the cloned user control not being part of any visual hierarchy, yet. However, I'm wondering what the MS controls do better in order to make the above code work. Since I'm quite new to WPF I'm not exactly sure what I should be looking for in order to fix my user control. Any pointers are greatly appreciated.
I've moved away from cloning via XamlWriter
/XamlReader
(which was only used for prototyping anyway), and now use ActualWidth
/ActualHeight
after enforcing a layout update:
private void drawingCanvas_Drop(object sender, DragEventArgs e)
{
DrawingShape templateShape = (DrawingShape)e.Data.GetData(typeof(DrawingShape));
if (templateShape != null)
{
Canvas target = this.drawingCanvas;
Point p = e.GetPosition(target);
DrawingShape addedShape = new DrawingShape();
addedShape.IsDraggingEnabled = true;
target.Children.Add(addedShape);
addedShape.UpdateLayout();
addedShape.RenderTransform = new TranslateTransform(p.X - addedShape.ActualWidth / 2, p.Y - addedShape.ActualHeight / 2);
this.shapesPalette.shapesGallery.ClearValue(ListView.SelectedIndexProperty);
}
}
However, I would expect this solution to work with XamlWriter/XamlReader-cloning.