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 中的主要内容,如果未能解决你的问题,请参考以下文章

WPF 窗口中的可拖动和自动调整用户控件

WPF C#如何使用鼠标使控件可拖动

如何在画布内拖动用户控件

如何使用户控件像窗口一样在屏幕上可拖动

WPF - 创建要在画布上使用的简单控件

WPF 滑块仅可拖动