【问题标题】:WPF Drag and Drop with Adorner using mouse and touch使用鼠标和触摸使用 Adorner 拖放 WPF
【发布时间】:2015-12-12 08:47:26
【问题描述】:

我希望这是一个很好的问题,所以我会详细写下我想要实现的目标、我在互联网上找到的内容,并展示我到目前为止所做的事情和尝试过的事情。

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

这是示例 UI:

这是我现在的用法:

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

我的解决方案基于对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. 当我取消拖动(MouseUp 或不正确的拖动目标)时,如何在我的示例中隐藏那个小图像窗口
  2. 显示我使用 Adorner 以及如何在我的代码中使用它?我需要在开始拖放时显示它,当我正确放置图像或取消拖放时,我需要显示它或放置目标不正确

我从 WPF 开始,所以请试着理解我的挫败感 - 我已经花了最后两个晚上和晚上试图让它工作。

【问题讨论】:

  • 我已经创建了像您的解决方案一样的装饰器窗口。但是拖动时装饰器窗口不会放置在鼠标指针附近。请给出任何建议来做到这一点。我还提到了您在问题中提到的链接。但这对我没有帮助。
  • @Achu_L 也许您计算 _dragdropWindow 位置错误。将其样式更改为不透明,然后查看它的外观/工作方式。不看代码我也说不出口。
  • 请从下面提到的链接中参考我的示例项目。 drive.google.com/open?id=17CNm2nKQ8-801Advc8yEfG-oGbSwZdeh
  • @Achu_L 我没有发现任何问题。我可以从列表中拖动项目,它们出现在光标的右下角(拖动时)。
  • 当拖动装饰器窗口时,鼠标指针需要一些距离。但是我想在拖动时将装饰器窗口放置在非常靠近鼠标指针的位置。

标签: c# wpf drag-and-drop multi-touch


【解决方案1】:

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) 我认为创建一个新窗口以在光标旁边显示图像是一种肮脏的黑客行为。有很多关于通过拖放使用装饰器的各种文章。尽管您的方法有效并且不需要大量代码。另一方面,装饰者会这样做。我认为您应该创建另一个问题,如果您按照某些教程失败,请提供代码示例以及您采取的步骤

【讨论】:

  • 很抱歉,我不能说触摸。从未使用过它。
  • 非常感谢您的帮助,我回家后会检查该解决方案。我对 Adorners 有疑问,因为他们与 Mouse 一起工作,我需要触控支持。 MouseEventArgs 有 GetPosition 方法,但是 InputEventArgs 没有。任何关于如何创建触摸友好装饰器的建议都非常受欢迎:)
  • 完美运行 :) 谢谢!
  • UIElement_OnPreviewDrop 在放置时未触发
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-05
  • 1970-01-01
相关资源
最近更新 更多