C# 将WinForms拖放移植到WPF拖放

C# 将WinForms拖放移植到WPF拖放,c#,.net,wpf,winforms,C#,.net,Wpf,Winforms,我正在将我的程序从WinForms移植到WPF,在拖放方面遇到了一些问题。它应该允许从树视图(类似于文件浏览器)拖动到打开文件的文本框。但是,WPF版本的作用类似于自动复制并粘贴treevieItem的标题文本。我想我只是把事情弄混了?可能是DataObject东西 完整功能的相关WinForms代码: private void treeView1_MouseMove(object sender, MouseEventArgs e) { if (e.Button != MouseButt

我正在将我的程序从WinForms移植到WPF,在拖放方面遇到了一些问题。它应该允许从树视图(类似于文件浏览器)拖动到打开文件的文本框。但是,WPF版本的作用类似于自动复制并粘贴
treevieItem
的标题文本。我想我只是把事情弄混了?可能是
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版本中,我将文本框的文本设置为空(作为测试),这会发生,但之后会粘贴树视图项的标题文本,这不是我想要的


问题:将此WinForms代码移植到WPF的正确方法是什么?为什么要在WPF版本中粘贴文本?我如何防止这种情况?我是否使用了正确的事件?如何访问
textbox\u Drop
中的
DataObject
,以便像在WinForms版本中一样打开文件?为什么在WPF版本中TreeViewItem节点始终为空?

我使用的事件是否正确: 我认为您使用的事件是正确的,但我认为您的代码中有几个问题。 我假设您已经将treeview的DataContext设置为真实项目,并使用绑定

  • 如何访问文本框中的数据对象?--> 要获取数据对象,您必须通过递归获取实际项(其他解决方案可能)

  • 为什么要在WPF版本中粘贴文本?--> 显示标题是因为(我假设)它类似于项目上的tostring方法。如果没有为复杂项指定绑定,则执行ToString方法。 尽量不要直接在drop事件的处理程序中设置文本。将数据上下文设置为项目(在1.point中找到的项目),然后通过XAML指定绑定路径。(用于显示)

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

    我认为父UI元素正在处理(并因此覆盖)Drop事件,因此无法获得预期的结果。事实上,当试图重新创建您的问题时,我甚至无法获取我的文本框。将事件拖放到激发。然而,使用TextBox的PreviewDrop事件,我能够得到(我认为)您期望的结果。试试这个:

        private void textBox1_PreviewDrop(object sender, DragEventArgs e)
        {
            TextBox tb = sender as TextBox;
            if (tb != null)
            {
                // If the DataObject contains string data, extract it.
                if (e.Data.GetDataPresent(DataFormats.StringFormat))
                {
                    string fileName = e.Data.GetData(DataFormats.StringFormat) as string;
                    using (StreamReader s = File.OpenText(fileName))
                    {
                        ((TextBox)sender).Text = s.ReadToEnd();
                    }
                }
            }
            e.Handled = true; //be sure to set this to true
        }
    
    我认为这段代码应该回答您提出的大多数问题,除了以下问题:

    为什么在WPF版本中TreeViewItem节点总是空的


    您在DragDrop事件中传递的
    DataObject
    不支持传递
    TreeViewItem
    。在您的代码(和我的代码)中,我们指定数据格式为
    DataFormats。无法转换为
    treevieItem

    GetFullPath
    的StringFormat
    似乎输出了错误的值。您要拖放的是
    标题
    ,您可以直接从
    中获取。还要记住,下面的方法与
    TreeView
    MouseMove事件相关

    private void TreeView_MouseMove(object sender, MouseButtonEventArgs e)
    {
        if (e.LeftButton != MouseButtonState.Pressed) return;
        TreeViewItem item = e.Source as TreeViewItem;
        if (item != null)
        {
            DataObject dataObject = new DataObject();
            dataObject.SetData(DataFormats.StringFormat, item.Header);
            DragDrop.DoDragDrop(item, dataObject, DragDropEffects.Move);
        }
    }
    

    我的确是基于文本而不是基于
    treevieItem
    e.Data.GetData(typeof(string)).ToString()
    )创建了drop部分,但最令人惊讶的是它甚至不是必需的。如果您打开一个新的C#WPF项目,在其上放置一个
    TreeView
    和一个
    TextBox
    (,更新XAML部分)并复制上面的代码,您可以将
    TreeView
    中的文本放入
    TextBox
    ,而无需执行任何其他操作!!文本被复制到
    文本框中
    ,而不考虑
    拖放处理

    啊,见鬼,我将扩展我的评论以获得答案:

    如前所述,要阅读的链接如下:

    简而言之,
    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);
    
    这里有一个快速而肮脏的测试应用程序,纯粹是为了展示它对drag start的反应式扩展(Rx)的使用:

    XAML:

    
    
    令人讨厌的代码隐藏(太懒了,无法使用MVVM):

    使用系统;
    使用System.Collections.ObjectModel;
    使用系统组件模型;
    使用System.Reactive.Linq;
    使用System.Runtime.CompilerServices;
    使用System.Windows;
    使用System.Windows.Controls;
    使用System.Windows.Input;
    使用System.Windows.Media;
    命名空间WpfApplication1
    {
    /// 
    ///MainWindow.xaml的交互逻辑
    /// 
    公共部分类主窗口:窗口,INotifyPropertyChanged
    {
    公共主窗口()
    {
    初始化组件();
    TreeStuff=新的ObservableCollection()
    {
    新建树字符串(){Description=“file 1”,Path=@“c:\temp\test.txt”},
    新建树字符串(){Description=“file 2”,Path=@“c:\temp\test2.txt”},
    新建树字符串(){Description=“file 3”,Path=@“c:\temp\test3.txt”},
    };
    var dragStart=
    来自华盛顿的穆斯敦
    可观察的。从事件模式(
    h=>tree.PreviewMouseDown+=h,
    h=>tree.PreviewMouseDown-=h)
    让startPosition=mouseDown.EventArgs.GetPosition(null)
    从中的mouseMove
    可观察的。从事件模式(
    h=>tree.MouseMove+=h,
    h=
    
    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);
    
    <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>
    
    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; }
        }
    }
    
        private Point MouseDownPos;
    
        private void treeView1_PreviewMouseDown(object sender, MouseButtonEventArgs e) {
            MouseDownPos = e.GetPosition(treeView1);
        }
    
        private void treeView1_PreviewMouseMove(object sender, MouseEventArgs e) {
            if (e.LeftButton == MouseButtonState.Released) return;
            var pos = e.GetPosition(treeView1);
            if (Math.Abs(pos.X - MouseDownPos.X) >= SystemParameters.MinimumHorizontalDragDistance ||
                Math.Abs(pos.Y - MouseDownPos.Y) >= SystemParameters.MinimumVerticalDragDistance) {
                TreeViewItem item = e.Source as TreeViewItem;
                if (item != null) DragDrop.DoDragDrop(item, item, DragDropEffects.Copy);
            }
        }
    
        private void textBox1_PreviewDragEnter(object sender, DragEventArgs e) {
            if (e.Data.GetDataPresent(typeof(TreeViewItem))) e.Effects = e.AllowedEffects;
            e.Handled = true;
        }
    
        private void textBox1_PreviewDrop(object sender, DragEventArgs e) {
            var item = (TreeViewItem)e.Data.GetData(typeof(TreeViewItem));
            textBox1.Text = item.Header.ToString();   // Replace this with your own code
            e.Handled = true;
        }
    
        private void textBox1_PreviewDragOver(object sender, DragEventArgs e) {
            e.Handled = true;
        }