C# WPF 可拖动用户控件在 Canvas 上的 ListBox 中
Posted
技术标签:
【中文标题】C# WPF 可拖动用户控件在 Canvas 上的 ListBox 中【英文标题】:C# WPF Draggable UserControls in ListBox on Canvas 【发布时间】:2011-07-29 22:38:33 【问题描述】:我有一个列表框中的用户控件,然后将它们托管在画布上。然后,我使用 DraggableExtender(Dragging an image in WPF) 使 UserControls 在画布上可拖动。
但是自从我从 ItemsControl 更改为 ListBox 之后,为了能够使控件可选择,Dragging 的工作真的很糟糕。例如,如果我将一个控件拖动到另一个控件上,则另一个控件将获得焦点并坚持拖动。此外,如果我将鼠标移到画布外,用户控件会卡在边缘并且我失去了拖动焦点,就像 CaptureMouse 不起作用一样。
ListBox 看起来像这样(当它在成为 ItemsControl 之前工作时):
<ListBox ItemsSource="Binding Components" SelectedItem="Binding SelectedItem" Background="Transparent">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<Canvas ClipToBounds="True" Height="Binding CurrentProject.Height, Converter=StaticResource SizeConverter"
Width="Binding CurrentProject.Width, Converter=StaticResource SizeConverter"
HorizontalAlignment="Left" VerticalAlignment="Top">
<Canvas.Background>
<SolidColorBrush Color="DynamicResource x:Static SystemColors.WindowFrameColorKey"/>
</Canvas.Background>
</Canvas>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="x:Type ListBoxItem">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="x:Type ListBoxItem">
<Grid>
<Border Background="TemplateBinding Background" />
<ContentPresenter/>
</Grid>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True" />
<Condition Property="IsSelected" Value="False"/>
</MultiTrigger.Conditions>
<Setter Property="Background" Value="#8868D5FD" />
</MultiTrigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="#4468D5FD" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Utils:DraggableExtender.CanDrag" Value="True" />
<Setter Property="Canvas.Top" Value="Binding Path=Conveyor.Y, Converter=StaticResource SizeConverter,Mode=TwoWay" />
<Setter Property="Canvas.Left" Value="Binding Path=Conveyor.X, Converter=StaticResource SizeConverter,Mode=TwoWay" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
我的 DraggableExtender 看起来像这样:
public class DraggableExtender : DependencyObject
public static readonly DependencyProperty CanDragProperty =
DependencyProperty.RegisterAttached("CanDrag", typeof(bool), typeof(DraggableExtender),
new UIPropertyMetadata(false, OnChangeCanDragProperty));
private static bool isDragging = false;
private static Point offset;
public static void SetCanDrag(UIElement element, bool o)
element.SetValue(CanDragProperty, o);
public static bool GetCanDrag(UIElement element, bool o)
return (bool)element.GetValue(CanDragProperty);
private static void OnChangeCanDragProperty(DependencyObject d, DependencyPropertyChangedEventArgs e)
UIElement element = d as UIElement;
if (element == null) return;
if (e.NewValue != e.OldValue)
if ((bool)e.NewValue)
element.PreviewMouseDown += element_PreviewMouseDown;
element.PreviewMouseUp += element_PreviewMouseUp;
element.PreviewMouseMove += element_PreviewMouseMove;
else
element.PreviewMouseDown -= element_PreviewMouseDown;
element.PreviewMouseUp -= element_PreviewMouseUp;
element.PreviewMouseMove -= element_PreviewMouseMove;
private static void element_PreviewMouseDown(object sender, MouseButtonEventArgs e)
FrameworkElement element = sender as FrameworkElement;
if (element == null) return;
Debug.WriteLine(element);
isDragging = true;
element.CaptureMouse();
offset = e.GetPosition(element);
private static void element_PreviewMouseMove(object sender, MouseEventArgs e)
if (!isDragging) return;
FrameworkElement element = sender as FrameworkElement;
if (element == null) return;
Canvas canvas = element.FindAncestor<Canvas>();
if (canvas == null) return;
Point mousePoint = e.GetPosition(canvas);
mousePoint.Offset(-offset.X, -offset.Y);
element.SetValue(Canvas.LeftProperty, mousePoint.X);
element.SetValue(Canvas.TopProperty, mousePoint.Y);
private static void element_PreviewMouseUp(object sender, MouseButtonEventArgs e)
FrameworkElement element = sender as FrameworkElement;
if (element == null) return;
element.ReleaseMouseCapture();
isDragging = false;
我一直在努力寻找解决方案,但到目前为止我没有成功。有谁知道为什么会发生这种情况以及我该如何解决?
【问题讨论】:
【参考方案1】:我终于能够弄清楚如何解决这个问题,或者至少如何解决这个问题。问题似乎是 ListBox 还在其选择代码中捕获了鼠标。所以我改变的是添加 e.Handled = true 这样 ListBox 就不会得到事件,从而覆盖鼠标捕获。这确实使选择机制无法使用,但为了解决这个问题,我将代码移动到仅监听 MouseRight 事件,这意味着当我左键单击时我选择并且当我右键单击时我可以拖动。
private static void element_PreviewMouseDown(object sender, MouseButtonEventArgs e)
FrameworkElement element = sender as FrameworkElement;
if (element == null) return;
Debug.WriteLine(element);
isDragging = true;
element.CaptureMouse();
offset = e.GetPosition(element);
e.Handled = true;
【讨论】:
以上是关于C# WPF 可拖动用户控件在 Canvas 上的 ListBox 中的主要内容,如果未能解决你的问题,请参考以下文章