使用鼠标和触摸使用Adorner进行WPF拖放

时间:2015-09-15 23:15:42

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

我希望这是一个很好的问题,所以我会详细写下我想要实现的内容,我在互联网上找到的内容,并展示我迄今为止所做的工作以及我曾经尝试过的。

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

以下是示例用户界面:

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. 当我取消拖动(MouseUp或不正确的拖动目标)时,如何隐藏我的示例中的小图像窗口
  2. 显示我使用Adorner,如何在我的代码中使用它?当我正确拖放图像或者取消拖放目标不正确时,我需要在开始拖动和隐藏时显示它
  3. 我从WPF开始,所以请尽量了解我的挫败感 - 我已经花了最后两个晚上试图让这个工作。

1 个答案:

答案 0 :(得分:1)

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

GROUP BY

处理程序本身:

 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);
        }

2)我认为创建一个新窗口来显示光标旁边的图像是一个肮脏的黑客。关于使用带有拖放功能的装饰器,有很多不同的文章。虽然你的方法有效,但不需要很多代码。另一方面,装饰者也这样做。我认为你应该创建另一个问题,如果你没有按照某些教程,使用代码示例和你采取的步骤