代码之家  ›  专栏  ›  技术社区  ›  Misiu

WPF使用鼠标和触摸与Adorner拖放

  •  4
  • Misiu  · 技术社区  · 9 年前

    我希望这是一个很好的问题,所以我会详细地写下我想实现的目标,我在互联网上发现的东西,并展示我迄今为止所做的以及我所做的努力。

    我需要在应用程序中添加拖放功能。我有图像(基本上是控件),我想将其拖到列表框的项目中。

    以下是示例UI:

    enter image description here

    下面是我现在的用法:

    enter image description here

    正如您所看到的,我可以拖动四个图像中的一个,并将其放到列表框项目上。 若我将图像移动到正确的目标(列表框图像)上,光标附近的图像就会消失,一切都正常,但当我不将图像放在列表项上时(我松开鼠标),图像会留在屏幕上。

    我的解决方案基于 this 问题,我无法删除不需要的窗口(光标附近的图像)

    我的XAML如下所示:

    <Window x:Class="DragDrop.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="Drag'n'Drop" Height="350" Width="525"
            DataContext="{Binding RelativeSource={RelativeSource Self}}">
        <Grid>
            <ListBox HorizontalAlignment="Right" HorizontalContentAlignment="Stretch" Height="300" Margin="0,10,10,0" VerticalAlignment="Top" Width="234" ItemsSource="{Binding People}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Vertical" AllowDrop="True" PreviewDrop="UIElement_OnPreviewDrop">
                            <TextBlock Text="{Binding Name}" FontWeight="Bold" />
                            <ProgressBar Height="20" Value="{Binding Points}" Margin="0,0,0,0"/>
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
    
            <Image HorizontalAlignment="Left" Height="72" Margin="10,10,0,0" VerticalAlignment="Top" Width="72" Source="Images/coins-60000-icon.png" RenderTransformOrigin="0.5,0.5"
                   PreviewMouseLeftButtonDown="OnMouseTouchDown"
                   PreviewTouchDown="OnMouseTouchDown"
                   PreviewGiveFeedback="UIElement_OnPreviewGiveFeedback" Tag="10"/>
            <Image HorizontalAlignment="Left" Height="72" Margin="87,10,0,0" VerticalAlignment="Top" Width="72" Source="Images/coins-700000-icon.png" RenderTransformOrigin="0.5,0.5"
                   PreviewMouseLeftButtonDown="OnMouseTouchDown"
                   PreviewTouchDown="OnMouseTouchDown"
                   PreviewGiveFeedback="UIElement_OnPreviewGiveFeedback" Tag="20"/>
            <Image HorizontalAlignment="Left" Height="72" Margin="10,87,0,0" VerticalAlignment="Top" Width="72" Source="Images/coins-7000-icon.png" RenderTransformOrigin="0.5,0.5"
                   PreviewMouseLeftButtonDown="OnMouseTouchDown"
                   PreviewTouchDown="OnMouseTouchDown"
                   PreviewGiveFeedback="UIElement_OnPreviewGiveFeedback" Tag="30"/>
            <Image HorizontalAlignment="Left" Height="72" Margin="87,87,0,0" VerticalAlignment="Top" Width="72" Source="Images/coins-700-icon.png" RenderTransformOrigin="0.5,0.5"
                   PreviewMouseLeftButtonDown="OnMouseTouchDown"
                   PreviewTouchDown="OnMouseTouchDown"
                   PreviewGiveFeedback="UIElement_OnPreviewGiveFeedback" Tag="40"/>
        </Grid>
    </Window>
    

    以及后面的代码:

    public partial class MainWindow
    {
        private readonly ObservableCollection<Person> _people = new ObservableCollection<Person>();
    
        public ObservableCollection<Person> People
        {
            get { return _people; }
        }
    
        public MainWindow()
        {
            InitializeComponent();
    
            _people.Add(new Person() {Name = "Person1", Points = 10});
            _people.Add(new Person() {Name = "Person2", Points = 0});
            _people.Add(new Person() {Name = "Person3", Points = 40});
        }
    
        private void OnMouseTouchDown(object sender, InputEventArgs e)
        {
            var item = sender as Image;
            if (item == null) return;
    
            var draggedItem = item;
            var points = Convert.ToInt32(draggedItem.Tag);
            CreateDragDropWindow(draggedItem);
            System.Windows.DragDrop.DoDragDrop(draggedItem, points, DragDropEffects.Move);
        }
    
        private Window _dragdropWindow;
    
        private void CreateDragDropWindow(Visual dragElement)
        {
            _dragdropWindow = new Window
            {
                WindowStyle = WindowStyle.None,
                AllowsTransparency = true,
                AllowDrop = false,
                Background = null,
                IsHitTestVisible = false,
                SizeToContent = SizeToContent.WidthAndHeight,
                Topmost = true,
                ShowInTaskbar = false
            };
    
            Rectangle r = new Rectangle
            {
                Width = ((FrameworkElement) dragElement).ActualWidth/2,
                Height = ((FrameworkElement) dragElement).ActualHeight/2,
                Fill = new VisualBrush(dragElement)
            };
            _dragdropWindow.Content = r;
    
    
            Win32Point w32Mouse = new Win32Point();
            GetCursorPos(ref w32Mouse);
    
    
            _dragdropWindow.Left = w32Mouse.X;
            _dragdropWindow.Top = w32Mouse.Y;
            _dragdropWindow.Show();
        }
    
    
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool GetCursorPos(ref Win32Point pt);
    
        [StructLayout(LayoutKind.Sequential)]
        internal struct Win32Point
        {
            public Int32 X;
            public Int32 Y;
        };
    
        private void UIElement_OnPreviewGiveFeedback(object sender, GiveFeedbackEventArgs e)
        {
            Win32Point w32Mouse = new Win32Point();
            GetCursorPos(ref w32Mouse);
    
            _dragdropWindow.Left = w32Mouse.X;
            _dragdropWindow.Top = w32Mouse.Y;
        }
    
        private void UIElement_OnPreviewDrop(object sender, DragEventArgs e)
        {
            //var droppedData = e.Data.GetData(typeof(Image)) as Image;
            var droppedData = (Int32) e.Data.GetData(typeof (Int32));
            var stackPanel = sender as StackPanel;
            if (stackPanel != null)
            {
                var student = stackPanel.DataContext as Person;
    
                //int targetIndex = _people.IndexOf(student);
    
    
                if (student != null) student.Points += droppedData;
            }
            if (_dragdropWindow != null)
            {
                _dragdropWindow.Close();
                _dragdropWindow = null;
            }
        }
    }
    
    public class Person : INotifyPropertyChanged
    {
        private string _name;
        private int _points;
    
        public string Name
        {
            get { return _name; }
            set
            {
                if (value == _name) return;
                _name = value;
                OnPropertyChanged();
            }
        }
    
        public int Points
        {
            get { return _points; }
            set
            {
                if (value == _points) return;
                _points = value;
                if (_points >= 100)
                {
                    _points -= 100;
                    Debug.WriteLine("100!");
                }
                OnPropertyChanged();
            }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            var handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    

    我在互联网上发现,我可以使用扩展Adorner的类,我找到了一些示例:

    但所有这些都显示了如何从集合(ItemsControls)中拖动项。第三个环节很有希望,但我没能适应我的需求。

    所以我的问题是:

    1. 当我取消拖动(鼠标向上或不正确的拖动目标)时,如何隐藏示例中的小图像窗口
    2. 演示我使用Adorner,以及如何在代码中使用它?当我正确放置图像或取消拖放目标不正确时,我需要在开始拖放和隐藏时显示它

    我是从WPF开始的,所以请试着理解我的沮丧——我已经花了最后两个晚上和晚上的时间试图让这个工作。

    1 回复  |  直到 6 年前
        1
  •  4
  •   netaholic    9 年前

    1) 修改OnMouseTouchDown处理程序,包括在开始拖动之前将ContinueDragHandler分配给拖动的项目,如下所示

     private void OnMouseTouchDown(object sender, InputEventArgs e)
            {
                var item = sender as FrameworkElement;
                if (item == null) return;
    
                var draggedItem = item;
                var points = Convert.ToInt32(draggedItem.Tag);
                CreateDragDropWindow(draggedItem);
                System.Windows.DragDrop.AddQueryContinueDragHandler(draggedItem, DragContrinueHandler);
                System.Windows.DragDrop.DoDragDrop(draggedItem, points, DragDropEffects.Move);
            }
    

    以及处理程序本身:

    public void DragContrinueHandler(object sender, QueryContinueDragEventArgs e)
            {
                if (e.Action == DragAction.Continue && e.KeyStates != DragDropKeyStates.LeftMouseButton)
                {
                    _dragdropWindow.Close();
                }
            }
    

    2) 我认为,创建一个新窗口来显示光标旁边的图像是一种肮脏的手段。有很多关于将装饰物与拖放一起使用的文章。尽管你的方法有效,不需要很多代码。另一方面,装饰品确实如此。我认为,如果您未能遵循某些教程,您应该创建另一个问题,包括代码示例和您采取的步骤