C# 将WinForms拖放移植到WPF拖放
我正在将我的程序从WinForms移植到WPF,在拖放方面遇到了一些问题。它应该允许从树视图(类似于文件浏览器)拖动到打开文件的文本框。但是,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
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设置为真实项目,并使用绑定
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;
}