WPF:在运行时具有可拖动控件的水合画布 [英] WPF: Hydrate Canvas with Draggable Controls at Runtime
问题描述
这是我要完成的工作:
我想要一个支持Microsoft Visio风格的任意数据拖放的画布.我发现此链接可以做到: http://www.codeproject.com/KB/WPF/WPFDiagramDesigner_Part1.aspx
I want to have a canvas that supports dragging and dropping arbitrary data in the style of Microsoft Visio. I've found this link that does that: http://www.codeproject.com/KB/WPF/WPFDiagramDesigner_Part1.aspx
However, I also want to hydrate the canvas at run-time. I found this post: https://stackoverflow.com/questions/889825/wpf-is-it-possible-to-bind-a-canvass-children-property-in-xaml
其中一种解决方案建议在我自己的Canvas上绘制每个项目.但是,我无法通过将数据绑定到画布的ItemControl子项来进行拖放操作.理想情况下,我希望避免使用后台代码来执行此操作.有使用我所有东西的XAML解决方案吗?我的代码如下:
One of the solutions suggests drawing each item on it's own Canvas, which I've gotten to work. But I can't get it drag and drop to work by binding the data to an ItemControl child of the canvas. Ideally, I'd like to avoid having to use code-behind to do this. Is there a XAML solution using everything I have? My code follows:
这是我创建的MoveThumb,它是实际被移动的控件.
This is the MoveThumb that I created to be the control that actually is moved.
class MoveThumb : Thumb
{
public MoveThumb()
{
DragDelta += new DragDeltaEventHandler(this.MoveThumb_DragDelta);
}
private void MoveThumb_DragDelta(object sender, DragDeltaEventArgs e)
{
Control item = this.DataContext as Control;
if (item != null)
{
double left = Canvas.GetLeft(item);
double top = Canvas.GetTop(item);
Canvas.SetLeft(item, left + e.HorizontalChange);
Canvas.SetTop(item, top + e.VerticalChange);
}
}
}
这是我在第一个链接中定义的xaml模板.
This are the xaml templates I defined per the first link.
<ControlTemplate x:Key="MoveThumbTemplate" TargetType="{x:Type t:MoveThumb}">
<Rectangle Fill="Gray"/>
</ControlTemplate>
<ControlTemplate x:Key="NodeItemTemplate">
<Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
<t:MoveThumb Template="{StaticResource MoveThumbTemplate}"
Cursor="SizeAll"/>
<ContentPresenter Content="{TemplateBinding ContentControl.Content}"/>
</Grid>
</ControlTemplate>
<DataTemplate x:Key="nodeTemplate" DataType="Node">
<ContentControl Name="NodeItem"
Width="50"
Height="50"
Canvas.Top="50"
Canvas.Left="50"
Template="{StaticResource NodeItemTemplate}">
<Ellipse Fill="Black" IsHitTestVisible="False"/>
</ContentControl>
</DataTemplate>
这是在画布上创建并绑定我的ItemsControl的xaml.
This is the xaml to create and bind my ItemsControl in the canvas.
<ItemsControl ItemsSource="{Binding Path=NodeCollection}" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="Yellow" Width="auto" Height="auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Name="NodeItem"
Width="50"
Height="50"
Canvas.Top="100"
Canvas.Left="100"
Template="{StaticResource NodeItemTemplate}">
<Ellipse Fill="Black" IsHitTestVisible="False"/>
</ContentControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Top" Value="{Binding Path=Y}" />
<Setter Property="Canvas.Left" Value="{Binding Path=X}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
推荐答案
我在执行类似操作(水平可拖动时间轴)时发现的问题是,每个项目在放置在画布上之前都被包裹在ContentPresenter中,这防止了拇指无法正常工作.如果您想要只读显示,则可以,但是如果要拖动它将成为一个问题.
The problem I found when I was doing something similar (horizontal dragable timeline) was that every item was wrapped in a ContentPresenter before it was placed on the canvas which prevented the thumb from working. Its OK if you want a readonly display, but if you want to drag it becomes a problem.
解决方案是重写GetContainerForItemOverride()方法.我已经为时间轴本身添加了控件模板,样式和XAML.
该链接确实对我有所帮助.如何更改ItemsControl的默认项目容器?
The solution was to override the GetContainerForItemOverride() method. I have included my control template, styles and the XAML for the Timeline itself.
This link really helped me. How to change the default item container for ItemsControl?
public class Timeline : ItemsControl
{
protected override System.Windows.DependencyObject GetContainerForItemOverride()
{
return new ContentControl();
}
protected override void PrepareContainerForItemOverride(System.Windows.DependencyObject element, object item)
{
var control = element as Control;
if (control != null)
{
var myUri = new Uri("/ResourceDictionary.xaml", UriKind.Relative);
var dictionary = Application.LoadComponent(myUri) as ResourceDictionary;
if (dictionary != null)
{
control.Template = (ControlTemplate)dictionary["TimelineElementTemplate"];
}
control.DataContext = item;
}
base.PrepareContainerForItemOverride(element, item);
}
}
<ControlTemplate x:Key="TimelineElementTemplate" TargetType="{x:Type Controls:ContentControl}">
<Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
<TextBlock Text="{Binding Path=DataContext.DebugData}"
ToolTip="{Binding Path=DataContext.DebugData}"
Background="{Binding Path=DataContext.Background}"/>
<Control Template="{StaticResource ResizeDecoratorTemplate}" />
</Grid>
</ControlTemplate>
<Style x:Key="TimelineElementStyle" TargetType="Controls:ContentControl">
<Setter Property="Canvas.Left">
<Setter.Value>
<Binding Path="LeftPos" Mode="TwoWay"/>
</Setter.Value>
</Setter>
<Setter Property="Width">
<Setter.Value>
<Binding Path="Width" Mode="TwoWay"/>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="TimelineStyle" TargetType="ItemsControl">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<Canvas Background="LightGray"></Canvas>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
<Controls:Timeline Style="{StaticResource TimelineStyle}"
DataContext="{StaticResource timelineViewModel}"
ItemContainerStyle="{StaticResource TimelineElementStyle}"
ItemsSource="{Binding ListOfTimelineElements}"
HelperClasses:DragDropHelper.IsDragSource="False"
HelperClasses:DragDropHelper.IsDropTarget="True"
HelperClasses:SizeObserver.Observe="True"
HelperClasses:SizeObserver.ObservedWidth="{Binding TotalWidth, Mode=OneWayToSource}"
BorderBrush="Black"
BorderThickness="1">
</Controls:Timeline>
这篇关于WPF:在运行时具有可拖动控件的水合画布的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!