c# - 移植WinForms拖拽到WPF拖拽

标签 c# .net wpf winforms

我正在将我的程序从 WinForms 移植到 WPF,但在拖放过程中遇到了一些问题。它应该允许从 TreeView(就像文件资源管理器)拖动到打开文件的文本框。但是,WPF 版本就像是自动复制和粘贴 TreeViewItem 的标题文本。我想我只是混淆了什么?可能是 DataObject 的东西。

功能齐全的相关 WinForms 代码:

private void treeView1_MouseMove(object sender, MouseEventArgs e)
{
    if (e.Button != MouseButtons.Left) return;
    TreeNode node = treeView1.GetNodeAt(e.Location);
    if (node != null) treeView1.DoDragDrop(node, DragDropEffects.Move);
}

textbox[i].DragDrop += (o, ee) =>
{
     if (ee.Data.GetDataPresent(typeof(TreeNode)))
     {
         TreeNode node = (TreeNode)ee.Data.GetData(typeof(TreeNode));   
         ((Textbox)o).Text = File.ReadAllLines(pathRoot + node.Parent.FullPath);
         ...

应该做同样事情的 WPF 代码:

private void TreeView_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    TreeViewItem item = e.Source as TreeViewItem;
    if (item != null)
    {
        DataObject dataObject = new DataObject();
        dataObject.SetData(DataFormats.StringFormat, GetFullPath(item));
        DragDrop.DoDragDrop(item, dataObject, DragDropEffects.Move);
    }
}

//textbox[i].PreviewDrop += textbox_Drop;
private void textbox_Drop(object sender, DragEventArgs e)
{
    TreeViewItem node = (TreeViewItem)e.Data.GetData(typeof(TreeViewItem)); //null?
    ((Textbox)sender).Text = ""; 
    //this is being executed BUT then the node's header text is being pasted
    //also, how do I access the DataObject I passed?
}

问题:在我的 WPF 版本中,我将文本框的文本设置为空(作为测试),这发生了,但之后粘贴了 TreeViewItem 的标题文本,这不是我想要的。

问题:将此 WinForms 代码移植到 WPF 的正确方法是什么?为什么文本被粘贴在 WPF 版本中?我该如何防止呢?我使用的是正确的事件吗?如何访问 textbox_Drop 中的 DataObject 以便我可以像在 WinForms 版本中那样打开文件?为什么 TreeViewItem 节点在 WPF 版本中总是 null?

最佳答案

啊,搞什么鬼,我会把我的评论扩展成一个答案:

如前所述,阅读链接是这样的: http://msdn.microsoft.com/en-us/library/hh144798.aspx

简而言之,TextBox 派生的控件已经实现了基本拖放操作的大部分“功能”,建议您扩展它而不是提供显式的 DragEnter/DragOver/Drop 处理程序。

假设树状“数据”结构如下:

public class TreeThing
{
   public string Description { get; set; }
   public string Path { get; set; }
}

处理程序可能看起来像这样:

this.tb.AddHandler(UIElement.DragOverEvent, new DragEventHandler((sender, e) =>
    {
        e.Effects = !e.Data.GetDataPresent("treeThing") ? 
            DragDropEffects.None : 
            DragDropEffects.Copy;                    
    }), true);

this.tb.AddHandler(UIElement.DropEvent, new DragEventHandler((sender, e) =>
{
    if (e.Data.GetDataPresent("treeThing"))
    {
        var item = e.Data.GetData("treeThing") as TreeThing;
        if (item != null)
        {
            tb.Text = item.Path;
            // TODO: Actually open up the file here
        }
    }
}), true);

只是为了开玩笑,这里有一个快速而肮脏的测试应用程序,它纯粹是在炫耀,因为它使用 Reactive Extensions (Rx) 作为拖动开始的东西:

XAML:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <TreeView x:Name="tree" Grid.Column="0" ItemsSource="{Binding TreeStuff}" DisplayMemberPath="Description"/>
        <TextBox x:Name="tb" Grid.Column="1" AllowDrop="True" Text="Drop here" Height="30"/>
    </Grid>
</Window>

讨厌的代码隐藏(对 MVVM 太懒了):

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Reactive.Linq;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public MainWindow()
        {
            InitializeComponent();
            TreeStuff = new ObservableCollection<TreeThing>()
                {
                    new TreeThing() { Description="file 1",  Path = @"c:\temp\test.txt" },
                    new TreeThing() { Description="file 2", Path = @"c:\temp\test2.txt" },
                    new TreeThing() { Description="file 3", Path = @"c:\temp\test3.txt" },
                };

            var dragStart = 
                from mouseDown in 
                    Observable.FromEventPattern<MouseButtonEventHandler, MouseEventArgs>(
                        h => tree.PreviewMouseDown += h, 
                        h => tree.PreviewMouseDown -= h)
                let startPosition = mouseDown.EventArgs.GetPosition(null)
                from mouseMove in 
                    Observable.FromEventPattern<MouseEventHandler, MouseEventArgs>(
                        h => tree.MouseMove += h, 
                        h => tree.MouseMove -= h)
                let mousePosition = mouseMove.EventArgs.GetPosition(null)
                let dragDiff = startPosition - mousePosition
                where mouseMove.EventArgs.LeftButton == MouseButtonState.Pressed && 
                    (Math.Abs(dragDiff.X) > SystemParameters.MinimumHorizontalDragDistance ||
                    Math.Abs(dragDiff.Y) > SystemParameters.MinimumVerticalDragDistance)
                select mouseMove;

            dragStart.ObserveOnDispatcher().Subscribe(start =>
                {
                    var nodeSource = this.FindAncestor<TreeViewItem>(
                        (DependencyObject)start.EventArgs.OriginalSource);
                    var source = start.Sender as TreeView;
                    if (nodeSource == null || source == null)
                    {
                        return;
                    }
                    var data = (TreeThing)source
                        .ItemContainerGenerator
                        .ItemFromContainer(nodeSource);
                    DragDrop.DoDragDrop(nodeSource, new DataObject("treeThing", data), DragDropEffects.All);
                });

            this.tb.AddHandler(UIElement.DragOverEvent, new DragEventHandler((sender, e) =>
                {
                    e.Effects = !e.Data.GetDataPresent("treeThing") ? 
                        DragDropEffects.None : 
                        DragDropEffects.Copy;                    
                }), true);

            this.tb.AddHandler(UIElement.DropEvent, new DragEventHandler((sender, e) =>
            {
                if (e.Data.GetDataPresent("treeThing"))
                {
                    var item = e.Data.GetData("treeThing") as TreeThing;
                    if (item != null)
                    {
                        tb.Text = item.Path;
                       // TODO: Actually open up the file here
                    }
                }
            }), true);
            this.DataContext = this;
        }


        private T FindAncestor<T>(DependencyObject current)
            where T:DependencyObject
        {
            do
            {
                if (current is T)
                {
                    return (T)current;
                }
                current = VisualTreeHelper.GetParent(current);
            }
            while (current != null);
            return null;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public ObservableCollection<TreeThing> TreeStuff { get; set; }

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

    public class TreeThing
    {
        public string Description { get; set; }
        public string Path { get; set; }
    }
}

关于c# - 移植WinForms拖拽到WPF拖拽,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17667894/

相关文章:

c# - 如何以正确的方式将网格绑定(bind)到基于 EF 的 View 模型?

java - Java 8 java.util.function.Consumer<> 的 c# 等价物是什么?

c# - 使用派生的属性实现接口(interface)

c# - 有没有办法在不统一使用 Update() 函数的情况下从按钮单击启动方法

.net - 无法加载文件或程序集 microsoft.sqlserver.sqlclrprovider 13.100.0.0

.net - 用于拼写单词导师的良好的免费文本到语音 API

c# - 如何将 VB.net 常量转换为 C#?

c# - 如何在混合应用程序中获取有关缓冲区溢出异常的信息?

c# - 在 wpf 中调用 Required

c# - 在 ASP.Net c# 中执行多个读取器