使用 VirtualFileDataObject 在 WPF 中实现虚拟文件放置
Implementing Virtual File Drop in WPF using VirtualFileDataObject
我正在使用这个项目的解决方案:http://dlaa.me/blog/post/9913083#comment-2930777923
我想让我的 WPF 程序能够处理放入其中的虚拟文件,但我无法从 MemoryStream 读取相关数据,我从 DragEventArgs e.Data.GetData() 接收到的数据。
我读了很多帖子。最接近的是:
- https://blogs.msdn.microsoft.com/delay/2009/11/04/creating-something-from-nothing-asynchronously-developer-friendly-virtual-file-implementation-for-net-improved/#10040454
- https://www.codeproject.com/Articles/23139/Transferring-Virtual-Files-to-Windows-Explorer-in
- 当然还有上面提到的
- 和许多其他拖放本地文件的人,这显然没有帮助
但它们都只处理从我的应用程序到文件系统的情况。
没有任何花哨的 mvvm 东西...
我没有进一步了解的地方是
private void Label_Drop(object sender, DragEventArgs e){...}
方法在我后面的代码中:
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using System.Windows.Input;
using Delay;
using System.Runtime.Serialization.Formatters.Binary;
namespace VirtualFileDataObjectDemo
{
public partial class Window1 : Window
{
private static TextWriterTraceListener traceListener;
private string logPath;
private string filePath;
// From Windows SDK header files
private const string CFSTR_INETURLA = "UniformResourceLocator";
public Window1()
{
//logPath = Path.Combine(@"d:\Temp", @"Log\TextWriterOutput.log");
logPath = Path.Combine(Directory.GetCurrentDirectory(), @"Log\TextWriterOutput.log");
filePath = Path.Combine(Directory.GetCurrentDirectory(), @"Log");
traceListener = new TextWriterTraceListener(logPath, "traceListener");
InitializeComponent();
// Attach to interesting events
Text.MouseLeftButtonDown += new MouseButtonEventHandler(Text_MouseButtonDown);
Text.MouseRightButtonDown += new MouseButtonEventHandler(Text_MouseButtonDown);
TextUrl.MouseLeftButtonDown += new MouseButtonEventHandler(TextUrl_MouseButtonDown);
TextUrl.MouseRightButtonDown += new MouseButtonEventHandler(TextUrl_MouseButtonDown);
VirtualFile.MouseLeftButtonDown += new MouseButtonEventHandler(VirtualFile_MouseButtonDown);
VirtualFile.MouseRightButtonDown += new MouseButtonEventHandler(VirtualFile_MouseButtonDown);
MoreVirtualFiles.MouseLeftButtonDown += new MouseButtonEventHandler(TextUrlVirtualFile_MouseButtonDown);
MoreVirtualFiles.MouseRightButtonDown += new MouseButtonEventHandler(TextUrlVirtualFile_MouseButtonDown);
}
private void Text_MouseButtonDown(object sender, MouseButtonEventArgs e) //TEXT
{
var virtualFileDataObject = new VirtualFileDataObject();
// Provide simple text (in the form of a NULL-terminated ANSI string)
virtualFileDataObject.SetData(
(short)(DataFormats.GetDataFormat(DataFormats.Text).Id),
Encoding.Default.GetBytes("This is some sample text[=11=]"));
DoDragDropOrClipboardSetDataObject(e.ChangedButton, Text, virtualFileDataObject, DragDropEffects.Copy);
}
private void TextUrl_MouseButtonDown(object sender, MouseButtonEventArgs e) //WEBADDRESS
{
var virtualFileDataObject = new VirtualFileDataObject();
// Provide simple text and an URL in priority order
// (both in the form of a NULL-terminated ANSI string)
virtualFileDataObject.SetData(
(short)(DataFormats.GetDataFormat(CFSTR_INETURLA).Id),
Encoding.Default.GetBytes("http://blogs.msdn.com/delay/[=11=]"));
virtualFileDataObject.SetData(
(short)(DataFormats.GetDataFormat(DataFormats.Text).Id),
Encoding.Default.GetBytes("http://blogs.msdn.com/delay/[=11=]"));
DoDragDropOrClipboardSetDataObject(e.ChangedButton, TextUrl, virtualFileDataObject, DragDropEffects.Copy);
}
private void VirtualFile_MouseButtonDown(object sender, MouseButtonEventArgs e) //VIRTUALFILE
{
var virtualFileDataObject = new VirtualFileDataObject(
null,
(vfdo) =>
{
if (DragDropEffects.Move == vfdo.PerformedDropEffect)
{
// Hide the element that was moved (or cut)
// BeginInvoke ensures UI operations happen on the right thread
Dispatcher.BeginInvoke((Action)(() => VirtualFile.Visibility = Visibility.Hidden));
}
});
// Provide a virtual file (generated on demand) containing the letters 'a'-'z'
virtualFileDataObject.SetData(new VirtualFileDataObject.FileDescriptor[]
{
new VirtualFileDataObject.FileDescriptor
{
Name = "Alphabet.txt",
Length = 26,
ChangeTimeUtc = DateTime.Now.AddDays(-1),
StreamContents = stream =>
{
var contents = Enumerable.Range('a', 26).Select(i => (byte)i).ToArray();
stream.Write(contents, 0, contents.Length);
}
},
});
DoDragDropOrClipboardSetDataObject(e.ChangedButton, TextUrl, virtualFileDataObject, DragDropEffects.Move | DragDropEffects.Copy);
}
private void TextUrlVirtualFile_MouseButtonDown(object sender, MouseButtonEventArgs e) //ALL THREE TOGETHER
{
var virtualFileDataObject = new VirtualFileDataObject(
// BeginInvoke ensures UI operations happen on the right thread
(vfdo) => Dispatcher.BeginInvoke((Action)(() => BusyScreen.Visibility = Visibility.Visible)),
(vfdo) => Dispatcher.BeginInvoke((Action)(() => BusyScreen.Visibility = Visibility.Collapsed)));
virtualFileDataObject.SetData(new VirtualFileDataObject.FileDescriptor[]
{
new VirtualFileDataObject.FileDescriptor
{
Name = "Example.xml",
StreamContents = stream =>
{
using(var webClient = new WebClient())
{
var data = webClient.DownloadData("https://www.w3schools.com/xml/note.xml");
stream.Write(data, 0, data.Length);
}
}
},
new VirtualFileDataObject.FileDescriptor
{
Name = "Example2.xml",
StreamContents = stream =>
{
using(var webClient = new WebClient())
{
var data = webClient.DownloadData("https://www.w3schools.com/xml/cd_catalog.xml");
stream.Write(data, 0, data.Length);
}
}
},
new VirtualFileDataObject.FileDescriptor
{
Name = "Example3.xml",
StreamContents = stream =>
{
using(var webClient = new WebClient())
{
var data = webClient.DownloadData("https://www.w3schools.com/xml/plant_catalog.xml");
stream.Write(data, 0, data.Length);
}
}
},
});
DoDragDropOrClipboardSetDataObject(e.ChangedButton, MoreVirtualFiles, virtualFileDataObject, DragDropEffects.Move | DragDropEffects.Copy);
}
private static void DoDragDropOrClipboardSetDataObject(MouseButton button, DependencyObject dragSource, VirtualFileDataObject virtualFileDataObject, DragDropEffects allowedEffects)
{
try
{
if (button == MouseButton.Left)
{
// Left button is used to start a drag/drop operation
VirtualFileDataObject.DoDragDrop(dragSource, virtualFileDataObject, allowedEffects);
}
else if (button == MouseButton.Right)
{
// Right button is used to copy to the clipboard
// Communicate the preferred behavior to the destination
virtualFileDataObject.PreferredDropEffect = allowedEffects;
Clipboard.SetDataObject(virtualFileDataObject);
}
}
catch (COMException ce)
{
traceListener.WriteLine("COM Exception");
traceListener.WriteLine(ce);
traceListener.WriteLine(ce.Message);
traceListener.WriteLine(ce.InnerException);
// Failure; no way to recover
}
}
private void Label_Drop(object sender, DragEventArgs e)
{
try
{
dropLabel.Content = "";
string[] retrievedFormats = e.Data.GetFormats();
foreach (string retFormat in retrievedFormats)
{
object retrievedData = e.Data.GetData(retFormat);
dropLabel.Content = dropLabel.Content + Environment.NewLine + retrievedData.ToString() + " - " + retFormat;
}
}
catch (Exception ex)
{
traceListener.WriteLine("-------------");
traceListener.WriteLine(ex + Environment.NewLine);
traceListener.WriteLine(ex.Message + Environment.NewLine);
traceListener.WriteLine(ex.StackTrace + Environment.NewLine);
traceListener.WriteLine("-------------");
traceListener.Flush();
}
}
private void Label_DragEnter(object sender, DragEventArgs e)
{
e.Effects = DragDropEffects.Copy;
}
}
}
和 XAML:
<Window x:Class="VirtualFileDataObjectDemo.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="VirtualFileDataObjectDemo"
Height="800"
Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<UniformGrid
Rows="5"
Background="#ffdddddd"
TextElement.FontSize="22"
TextElement.FontWeight="Bold">
<UniformGrid.Resources>
<Style TargetType="{x:Type Label}">
<Setter Property="Background" Value="Orange"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderBrush" Value="Black"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Margin" Value="10"/>
</Style>
</UniformGrid.Resources>
<ContentControl
Content="Drag an item or right-click to copy it:"
FontSize="18"
VerticalAlignment="Center"
Margin="20"/>
<Label
x:Name="Text"
Content="Text only"/>
<Label
x:Name="TextUrl"
Content="Text and URL"/>
<Label
x:Name="VirtualFile">
<DockPanel>
<ContentControl
Content="Virtual file"
DockPanel.Dock="Left"
VerticalAlignment="Center"/>
<ContentControl
Content="[Drag moves; paste cuts]"
FontSize="14"
HorizontalAlignment="Right"
VerticalAlignment="Center"/>
</DockPanel>
</Label>
<Label
x:Name="MoreVirtualFiles"
Content="More virtual files"/>
</UniformGrid>
<Grid
x:Name="BusyScreen"
Background="LightGray"
Visibility="Collapsed">
<StackPanel
VerticalAlignment="Center"
Margin="50">
<Viewbox>
<TextBlock Text="Busy..."/>
</Viewbox>
<ProgressBar IsIndeterminate="True" Height="20"/>
</StackPanel>
</Grid>
<Label Name="dropLabel" Grid.Row="1" Content="Drop Area" MinHeight="50" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" FontSize="20" AllowDrop="True" Drop="Label_Drop" DragEnter="Label_DragEnter"/>
</Grid>
</Window>
VirtualFileDataObject 可以从这里下载:
http://dlaa.me/Samples/VirtualFileDataObjectDemo/VirtualFileDataObjectDemo.zip
感谢 James Barrass。
他在此主题中的解决方案:
Dropped zip file causes e.Data.GetData("FileContents") to throw an exception
解决了我的问题。
我正在使用这个项目的解决方案:http://dlaa.me/blog/post/9913083#comment-2930777923
我想让我的 WPF 程序能够处理放入其中的虚拟文件,但我无法从 MemoryStream 读取相关数据,我从 DragEventArgs e.Data.GetData() 接收到的数据。
我读了很多帖子。最接近的是:
- https://blogs.msdn.microsoft.com/delay/2009/11/04/creating-something-from-nothing-asynchronously-developer-friendly-virtual-file-implementation-for-net-improved/#10040454
- https://www.codeproject.com/Articles/23139/Transferring-Virtual-Files-to-Windows-Explorer-in
- 当然还有上面提到的
- 和许多其他拖放本地文件的人,这显然没有帮助
但它们都只处理从我的应用程序到文件系统的情况。
没有任何花哨的 mvvm 东西...
我没有进一步了解的地方是
private void Label_Drop(object sender, DragEventArgs e){...}
方法在我后面的代码中:
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using System.Windows.Input;
using Delay;
using System.Runtime.Serialization.Formatters.Binary;
namespace VirtualFileDataObjectDemo
{
public partial class Window1 : Window
{
private static TextWriterTraceListener traceListener;
private string logPath;
private string filePath;
// From Windows SDK header files
private const string CFSTR_INETURLA = "UniformResourceLocator";
public Window1()
{
//logPath = Path.Combine(@"d:\Temp", @"Log\TextWriterOutput.log");
logPath = Path.Combine(Directory.GetCurrentDirectory(), @"Log\TextWriterOutput.log");
filePath = Path.Combine(Directory.GetCurrentDirectory(), @"Log");
traceListener = new TextWriterTraceListener(logPath, "traceListener");
InitializeComponent();
// Attach to interesting events
Text.MouseLeftButtonDown += new MouseButtonEventHandler(Text_MouseButtonDown);
Text.MouseRightButtonDown += new MouseButtonEventHandler(Text_MouseButtonDown);
TextUrl.MouseLeftButtonDown += new MouseButtonEventHandler(TextUrl_MouseButtonDown);
TextUrl.MouseRightButtonDown += new MouseButtonEventHandler(TextUrl_MouseButtonDown);
VirtualFile.MouseLeftButtonDown += new MouseButtonEventHandler(VirtualFile_MouseButtonDown);
VirtualFile.MouseRightButtonDown += new MouseButtonEventHandler(VirtualFile_MouseButtonDown);
MoreVirtualFiles.MouseLeftButtonDown += new MouseButtonEventHandler(TextUrlVirtualFile_MouseButtonDown);
MoreVirtualFiles.MouseRightButtonDown += new MouseButtonEventHandler(TextUrlVirtualFile_MouseButtonDown);
}
private void Text_MouseButtonDown(object sender, MouseButtonEventArgs e) //TEXT
{
var virtualFileDataObject = new VirtualFileDataObject();
// Provide simple text (in the form of a NULL-terminated ANSI string)
virtualFileDataObject.SetData(
(short)(DataFormats.GetDataFormat(DataFormats.Text).Id),
Encoding.Default.GetBytes("This is some sample text[=11=]"));
DoDragDropOrClipboardSetDataObject(e.ChangedButton, Text, virtualFileDataObject, DragDropEffects.Copy);
}
private void TextUrl_MouseButtonDown(object sender, MouseButtonEventArgs e) //WEBADDRESS
{
var virtualFileDataObject = new VirtualFileDataObject();
// Provide simple text and an URL in priority order
// (both in the form of a NULL-terminated ANSI string)
virtualFileDataObject.SetData(
(short)(DataFormats.GetDataFormat(CFSTR_INETURLA).Id),
Encoding.Default.GetBytes("http://blogs.msdn.com/delay/[=11=]"));
virtualFileDataObject.SetData(
(short)(DataFormats.GetDataFormat(DataFormats.Text).Id),
Encoding.Default.GetBytes("http://blogs.msdn.com/delay/[=11=]"));
DoDragDropOrClipboardSetDataObject(e.ChangedButton, TextUrl, virtualFileDataObject, DragDropEffects.Copy);
}
private void VirtualFile_MouseButtonDown(object sender, MouseButtonEventArgs e) //VIRTUALFILE
{
var virtualFileDataObject = new VirtualFileDataObject(
null,
(vfdo) =>
{
if (DragDropEffects.Move == vfdo.PerformedDropEffect)
{
// Hide the element that was moved (or cut)
// BeginInvoke ensures UI operations happen on the right thread
Dispatcher.BeginInvoke((Action)(() => VirtualFile.Visibility = Visibility.Hidden));
}
});
// Provide a virtual file (generated on demand) containing the letters 'a'-'z'
virtualFileDataObject.SetData(new VirtualFileDataObject.FileDescriptor[]
{
new VirtualFileDataObject.FileDescriptor
{
Name = "Alphabet.txt",
Length = 26,
ChangeTimeUtc = DateTime.Now.AddDays(-1),
StreamContents = stream =>
{
var contents = Enumerable.Range('a', 26).Select(i => (byte)i).ToArray();
stream.Write(contents, 0, contents.Length);
}
},
});
DoDragDropOrClipboardSetDataObject(e.ChangedButton, TextUrl, virtualFileDataObject, DragDropEffects.Move | DragDropEffects.Copy);
}
private void TextUrlVirtualFile_MouseButtonDown(object sender, MouseButtonEventArgs e) //ALL THREE TOGETHER
{
var virtualFileDataObject = new VirtualFileDataObject(
// BeginInvoke ensures UI operations happen on the right thread
(vfdo) => Dispatcher.BeginInvoke((Action)(() => BusyScreen.Visibility = Visibility.Visible)),
(vfdo) => Dispatcher.BeginInvoke((Action)(() => BusyScreen.Visibility = Visibility.Collapsed)));
virtualFileDataObject.SetData(new VirtualFileDataObject.FileDescriptor[]
{
new VirtualFileDataObject.FileDescriptor
{
Name = "Example.xml",
StreamContents = stream =>
{
using(var webClient = new WebClient())
{
var data = webClient.DownloadData("https://www.w3schools.com/xml/note.xml");
stream.Write(data, 0, data.Length);
}
}
},
new VirtualFileDataObject.FileDescriptor
{
Name = "Example2.xml",
StreamContents = stream =>
{
using(var webClient = new WebClient())
{
var data = webClient.DownloadData("https://www.w3schools.com/xml/cd_catalog.xml");
stream.Write(data, 0, data.Length);
}
}
},
new VirtualFileDataObject.FileDescriptor
{
Name = "Example3.xml",
StreamContents = stream =>
{
using(var webClient = new WebClient())
{
var data = webClient.DownloadData("https://www.w3schools.com/xml/plant_catalog.xml");
stream.Write(data, 0, data.Length);
}
}
},
});
DoDragDropOrClipboardSetDataObject(e.ChangedButton, MoreVirtualFiles, virtualFileDataObject, DragDropEffects.Move | DragDropEffects.Copy);
}
private static void DoDragDropOrClipboardSetDataObject(MouseButton button, DependencyObject dragSource, VirtualFileDataObject virtualFileDataObject, DragDropEffects allowedEffects)
{
try
{
if (button == MouseButton.Left)
{
// Left button is used to start a drag/drop operation
VirtualFileDataObject.DoDragDrop(dragSource, virtualFileDataObject, allowedEffects);
}
else if (button == MouseButton.Right)
{
// Right button is used to copy to the clipboard
// Communicate the preferred behavior to the destination
virtualFileDataObject.PreferredDropEffect = allowedEffects;
Clipboard.SetDataObject(virtualFileDataObject);
}
}
catch (COMException ce)
{
traceListener.WriteLine("COM Exception");
traceListener.WriteLine(ce);
traceListener.WriteLine(ce.Message);
traceListener.WriteLine(ce.InnerException);
// Failure; no way to recover
}
}
private void Label_Drop(object sender, DragEventArgs e)
{
try
{
dropLabel.Content = "";
string[] retrievedFormats = e.Data.GetFormats();
foreach (string retFormat in retrievedFormats)
{
object retrievedData = e.Data.GetData(retFormat);
dropLabel.Content = dropLabel.Content + Environment.NewLine + retrievedData.ToString() + " - " + retFormat;
}
}
catch (Exception ex)
{
traceListener.WriteLine("-------------");
traceListener.WriteLine(ex + Environment.NewLine);
traceListener.WriteLine(ex.Message + Environment.NewLine);
traceListener.WriteLine(ex.StackTrace + Environment.NewLine);
traceListener.WriteLine("-------------");
traceListener.Flush();
}
}
private void Label_DragEnter(object sender, DragEventArgs e)
{
e.Effects = DragDropEffects.Copy;
}
}
}
和 XAML:
<Window x:Class="VirtualFileDataObjectDemo.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="VirtualFileDataObjectDemo"
Height="800"
Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<UniformGrid
Rows="5"
Background="#ffdddddd"
TextElement.FontSize="22"
TextElement.FontWeight="Bold">
<UniformGrid.Resources>
<Style TargetType="{x:Type Label}">
<Setter Property="Background" Value="Orange"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderBrush" Value="Black"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Margin" Value="10"/>
</Style>
</UniformGrid.Resources>
<ContentControl
Content="Drag an item or right-click to copy it:"
FontSize="18"
VerticalAlignment="Center"
Margin="20"/>
<Label
x:Name="Text"
Content="Text only"/>
<Label
x:Name="TextUrl"
Content="Text and URL"/>
<Label
x:Name="VirtualFile">
<DockPanel>
<ContentControl
Content="Virtual file"
DockPanel.Dock="Left"
VerticalAlignment="Center"/>
<ContentControl
Content="[Drag moves; paste cuts]"
FontSize="14"
HorizontalAlignment="Right"
VerticalAlignment="Center"/>
</DockPanel>
</Label>
<Label
x:Name="MoreVirtualFiles"
Content="More virtual files"/>
</UniformGrid>
<Grid
x:Name="BusyScreen"
Background="LightGray"
Visibility="Collapsed">
<StackPanel
VerticalAlignment="Center"
Margin="50">
<Viewbox>
<TextBlock Text="Busy..."/>
</Viewbox>
<ProgressBar IsIndeterminate="True" Height="20"/>
</StackPanel>
</Grid>
<Label Name="dropLabel" Grid.Row="1" Content="Drop Area" MinHeight="50" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" FontSize="20" AllowDrop="True" Drop="Label_Drop" DragEnter="Label_DragEnter"/>
</Grid>
</Window>
VirtualFileDataObject 可以从这里下载: http://dlaa.me/Samples/VirtualFileDataObjectDemo/VirtualFileDataObjectDemo.zip
感谢 James Barrass。 他在此主题中的解决方案:
Dropped zip file causes e.Data.GetData("FileContents") to throw an exception
解决了我的问题。