Creating something from nothing [Developer-friendly virtual file implementation for .NET!]
2013-05-03 11:06
471 查看
Have you ever used one of those programs that lets you drag a UI widget, drop it in a folder, and - poof - a file that didn't exist magically appears? Me, too - it's cool! But how does it work? Are they really deferring the work of creating
that file until it's needed and then creating the file during the drag-and-drop operation? Yes they are - and now you can, too!
If I have seen a little further it is by standing on the shoulders of Giants.- Sir Isaac Newton
Everything you ever needed to know about drag-and-drop in Windows can probably be found in the MSDN documentation for Transferring
Shell Objects with Drag-and-Drop and the Clipboard. That documentation is a great resource for specific questions, but because it covers so many topics, it's not necessarily the best way to get an overview. For that, we turn to Raymond
Chen's blog - specifically, a series he did called "What a drag" in March of last year. Raymond's example uses native code exclusively, but don't let that scare you away - his presentation and explanations are always engaging! Please take a moment to read
(or at least skim) the following articles, or else the rest of this post might not make much sense:
What a drag: Dragging text
What a drag: Dragging a Uniform Resource Locator (URL)
What a drag: Dragging a Uniform Resource Locator (URL) and text
What a drag: Dragging a virtual file (HGLOBAL edition)
What a drag: Dragging a virtual file (IStream edition)
What a drag: Dragging a virtual file (IStorage edition)
You can drag multiple virtual objects, you know
Okay, so we know what we want to do and now we know how it's supposed to work! The first thing to consider is whether the WPF platform supports the virtual file scenario. And unfortunately, it doesn't seem to. :( Specifically, the DataObject
class is where we'd expect to find such support, but the closest it has is SetFileDropList.
And while that sounds promising, it's really just a list of strings with paths to existing files. Recall that the DragDrop.DoDragDrop
method is synchronous (i.e., does not return until the drag-and-drop operation is complete), and the obvious consequence is that creating a virtual file on the fly isn't practical with this API. Specifically, the bits already need to exist on disk by the
time the user starts the drag operation - but you don't know what data they're going to drag until they start! It's a classic Catch-22...
The natural next step is to consider whether subclassing
Move on to consider whether the System.Windows.IDataObject interface used by
be useful. But it seems not; it's pretty much the same API as
So that leaves us looking at the System.Runtime.InteropServices.ComTypes.IDataObject
interface which is a simple managed representation of the actualIDataObject COM interface that the shell uses directly.
Clearly, anything is possible at this point, so if we can just channel our inner Raymond, we ought to be in business!
The good news is that I've already done this for you. I've even written a simple WPF application to show how everything fits together:
![](http://blogs.msdn.com/blogfiles/delay/VirtualFileDataObjectDemo-Demo.png)
[Click here to download the complete code for VirtualFileDataObject and the sample application.]
What I've done is write a custom
the relevant data, and your users will be dragging-and-dropping virtual files in no time. And what's really neat is that writing the code to support drag-and-drop automatically gives complete support for the clipboard because the Clipboard.SetDataObject
method uses the same
Let's look at the sample scenarios to understand how
Text only
This is the simplest possible scenario - just to show off that simple things stay simple with
used above:
Note that it operates in "
in the right format. Specifically, the right format for
Aside: The
It's not very exciting, so I won't be showing it (or the
Text and URL
Another simple example based on Raymond's discussion of drag-and-drop into Internet Explorer. For our purposes, this example demonstrates that you can set multiple data formats and that formats other than those exposed by WPF's
are easy to deal with. Per the guidelines, supported formats are provided in order by priority, with higher priority formats coming first.
Virtual file
At last, something juicy! This example creates a virtual file named
they're actually required by the drop target, so there's no wasted effort if the user doesn't start the drag, aborts it, or whatever. When the file's contents are eventually needed,
the user-provided
I've used one here for conciseness) and passes it a write-only Stream instance for writing the data. The user code
writes to this stream as much or as little as necessary, then returns control to
The file that gets created when you drop/paste this item into a folder looks just like you'd expect. And because
it needs to help the user resolve possible conflicts:
![](http://blogs.msdn.com/blogfiles/delay/VirtualFileDataObjectDemo-Conflict.png)
Here's the relevant
as you want):
It makes use of another
And accepts data in the following form:
Text, URL, and a virtual file!
Finally, here's a sample that pulls everything together in a nice, fancy package with a bow on top. The text is an informative snippet, the URL is a link to an RSS feed, and the virtual file is the dynamically downloaded content of that RSS
feed! Way cool - it's like there's this big file sitting around that the user can drop anywhere they want - except that it only really exists on the web and it's always up to date whenever you drop it somewhere!
As you can see, the
There's just one small problem...
If you tried the drag-and-drop version of the last sample above on a machine with a slow network connection, you probably noticed that the sample application became unresponsive as soon as you dropped the virtual file and didn't recover until the download completed.
This is a natural consequence of the
the file's data is practically instantaneous. But when there's a delay, unresponsiveness is a possibility. The good news is that there's an official technique for solving this problem. The bad news is that it doesn't work
for WPF apps. The good news is that I can show you how to make it work anyway.
But that's a topic for another blog post - one that I'll write in a week or so... :)
Referenced from: http://blogs.msdn.com/b/delay/archive/2009/10/26/creating-something-from-nothing-developer-friendly-virtual-file-implementation-for-net.aspx
that file until it's needed and then creating the file during the drag-and-drop operation? Yes they are - and now you can, too!
If I have seen a little further it is by standing on the shoulders of Giants.- Sir Isaac Newton
Everything you ever needed to know about drag-and-drop in Windows can probably be found in the MSDN documentation for Transferring
Shell Objects with Drag-and-Drop and the Clipboard. That documentation is a great resource for specific questions, but because it covers so many topics, it's not necessarily the best way to get an overview. For that, we turn to Raymond
Chen's blog - specifically, a series he did called "What a drag" in March of last year. Raymond's example uses native code exclusively, but don't let that scare you away - his presentation and explanations are always engaging! Please take a moment to read
(or at least skim) the following articles, or else the rest of this post might not make much sense:
What a drag: Dragging text
What a drag: Dragging a Uniform Resource Locator (URL)
What a drag: Dragging a Uniform Resource Locator (URL) and text
What a drag: Dragging a virtual file (HGLOBAL edition)
What a drag: Dragging a virtual file (IStream edition)
What a drag: Dragging a virtual file (IStorage edition)
You can drag multiple virtual objects, you know
Okay, so we know what we want to do and now we know how it's supposed to work! The first thing to consider is whether the WPF platform supports the virtual file scenario. And unfortunately, it doesn't seem to. :( Specifically, the DataObject
class is where we'd expect to find such support, but the closest it has is SetFileDropList.
And while that sounds promising, it's really just a list of strings with paths to existing files. Recall that the DragDrop.DoDragDrop
method is synchronous (i.e., does not return until the drag-and-drop operation is complete), and the obvious consequence is that creating a virtual file on the fly isn't practical with this API. Specifically, the bits already need to exist on disk by the
time the user starts the drag operation - but you don't know what data they're going to drag until they start! It's a classic Catch-22...
The natural next step is to consider whether subclassing
DataObjectwould help - but it's
sealed, so that's a pretty quick dead end.
Move on to consider whether the System.Windows.IDataObject interface used by
DataObjectwould
be useful. But it seems not; it's pretty much the same API as
DataObjectwhich we've already dismissed.
So that leaves us looking at the System.Runtime.InteropServices.ComTypes.IDataObject
interface which is a simple managed representation of the actualIDataObject COM interface that the shell uses directly.
Clearly, anything is possible at this point, so if we can just channel our inner Raymond, we ought to be in business!
The good news is that I've already done this for you. I've even written a simple WPF application to show how everything fits together:
![](http://blogs.msdn.com/blogfiles/delay/VirtualFileDataObjectDemo-Demo.png)
[Click here to download the complete code for VirtualFileDataObject and the sample application.]
What I've done is write a custom
IDataObjectclass called
VirtualFileDataObjectthat does all the hard work for you. All you need to do is provide
the relevant data, and your users will be dragging-and-dropping virtual files in no time. And what's really neat is that writing the code to support drag-and-drop automatically gives complete support for the clipboard because the Clipboard.SetDataObject
method uses the same
IDataObjectinterface!
Let's look at the sample scenarios to understand how
VirtualFileDataObjectis used:
Text only
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\0")); DoDragDropOrClipboardSetDataObject(e.ChangedButton, Text, virtualFileDataObject);
This is the simplest possible scenario - just to show off that simple things stay simple with
VirtualFileDataObject. Here's the signature for the
SetDatamethod
used above:
/// <summary> /// Provides data for the specified data format (HGLOBAL). /// </summary> /// <param name="dataFormat">Data format.</param> /// <param name="data">Sequence of data.</param> public void SetData(short dataFormat, IEnumerable<byte> data)
Note that it operates in "
HGLOBALmode" where all the data is provided at the time of the call. Note also that it doesn't know anything about what the data is, so it's up to the caller to make sure it's
in the right format. Specifically, the right format for
DataFormats.Textis a NULL-terminated ANSI string, so that's what the sample passes in.
Aside: The
DoDragDropOrClipboardSetDataObjectmethod used above is a simple helper method for the test application - it calls
DragDrop.DoDragDropor
Clipboard.SetDataObjectdepending on what the user did.
It's not very exciting, so I won't be showing it (or the
VirtualFileDataObjectconstructor) in the following examples.
Text and URL
// Provide simple text and a 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/\0")); virtualFileDataObject.SetData( (short)(DataFormats.GetDataFormat(DataFormats.Text).Id), Encoding.Default.GetBytes("http://blogs.msdn.com/delay/\0"));
Another simple example based on Raymond's discussion of drag-and-drop into Internet Explorer. For our purposes, this example demonstrates that you can set multiple data formats and that formats other than those exposed by WPF's
DataFormatsenumeration
are easy to deal with. Per the guidelines, supported formats are provided in order by priority, with higher priority formats coming first.
Virtual file
// 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); } }, });
At last, something juicy! This example creates a virtual file named
Alphabet.txtthat's 26 bytes long and appears to have been written exactly one day ago. The contents of this file aren't generated until
they're actually required by the drop target, so there's no wasted effort if the user doesn't start the drag, aborts it, or whatever. When the file's contents are eventually needed,
VirtualFileDataObjectcalls
the user-provided
Action(not necessarily alambda expression, though
I've used one here for conciseness) and passes it a write-only Stream instance for writing the data. The user code
writes to this stream as much or as little as necessary, then returns control to
VirtualFileDataObjectin order to complete the operation.
The file that gets created when you drop/paste this item into a folder looks just like you'd expect. And because
VirtualFileDataObjectsupports the length and change time fields, Windows has all the information
it needs to help the user resolve possible conflicts:
![](http://blogs.msdn.com/blogfiles/delay/VirtualFileDataObjectDemo-Conflict.png)
Here's the relevant
SetDatamethod (note that you can provide an arbitrary number of
FileDescriptorinstances, so you can create as many virtual files
as you want):
/// <summary> /// Provides data for the specified data format (FILEGROUPDESCRIPTOR/FILEDESCRIPTOR) /// </summary> /// <param name="fileDescriptors">Collection of virtual files.</param> public void SetData(IEnumerable<FileDescriptor> fileDescriptors)
It makes use of another
SetDatamethod that's handy for dealing with "
ISTREAMmode":
/// <summary> /// Provides data for the specified data format and index (ISTREAM). /// </summary> /// <param name="dataFormat">Data format.</param> /// <param name="index">Index of data.</param> /// <param name="streamData">Action generating the data.</param> /// <remarks> /// Uses Stream instead of IEnumerable(T) because Stream is more likely /// to be natural for the expected scenarios. /// </remarks> public void SetData(short dataFormat, int index, Action<Stream> streamData)
And accepts data in the following form:
/// <summary> /// Class representing a virtual file for use by drag/drop or the clipboard. /// </summary> public class FileDescriptor { /// <summary> /// Gets or sets the name of the file. /// </summary> public string Name { get; set; } /// <summary> /// Gets or sets the (optional) length of the file. /// </summary> public UInt64? Length { get; set; } /// <summary> /// Gets or sets the (optional) change time of the file. /// </summary> public DateTime? ChangeTimeUtc { get; set; } /// <summary> /// Gets or sets an Action that returns the contents of the file. /// </summary> public Action<Stream> StreamContents { get; set; } }
Text, URL, and a virtual file!
// Provide a virtual file (downloaded on demand), its URL, and descriptive text virtualFileDataObject.SetData(new VirtualFileDataObject.FileDescriptor[] { new VirtualFileDataObject.FileDescriptor { Name = "DelaysBlog.xml", StreamContents = stream => { using(var webClient = new WebClient()) { var data = webClient.DownloadData("http://blogs.msdn.com/delay/rss.xml"); stream.Write(data, 0, data.Length); } } }, }); virtualFileDataObject.SetData( (short)(DataFormats.GetDataFormat(CFSTR_INETURLA).Id), Encoding.Default.GetBytes("http://blogs.msdn.com/delay/rss.xml\0")); virtualFileDataObject.SetData( (short)(DataFormats.GetDataFormat(DataFormats.Text).Id), Encoding.Default.GetBytes("[The RSS feed for Delay's Blog]\0"));
Finally, here's a sample that pulls everything together in a nice, fancy package with a bow on top. The text is an informative snippet, the URL is a link to an RSS feed, and the virtual file is the dynamically downloaded content of that RSS
feed! Way cool - it's like there's this big file sitting around that the user can drop anywhere they want - except that it only really exists on the web and it's always up to date whenever you drop it somewhere!
As you can see, the
VirtualFileDataObjectclass makes the whole scenario really easy and approachable - even if you're not an expert on shell interoperability. It's pretty snazzy, I'd say. :)
There's just one small problem...
If you tried the drag-and-drop version of the last sample above on a machine with a slow network connection, you probably noticed that the sample application became unresponsive as soon as you dropped the virtual file and didn't recover until the download completed.
This is a natural consequence of the
DoDragDropmethod being synchronous and getting called from the UI thread (like it should be). In most scenarios, you probably won't notice this problem because generating
the file's data is practically instantaneous. But when there's a delay, unresponsiveness is a possibility. The good news is that there's an official technique for solving this problem. The bad news is that it doesn't work
for WPF apps. The good news is that I can show you how to make it work anyway.
But that's a topic for another blog post - one that I'll write in a week or so... :)
Referenced from: http://blogs.msdn.com/b/delay/archive/2009/10/26/creating-something-from-nothing-developer-friendly-virtual-file-implementation-for-net.aspx
相关文章推荐
- Creating something from nothing, asynchronously [Developer-friendly virtual file implementation for
- Downloading files from a server to client, using ASP.Net, when file size is too big for MemoryStream using Generic Handlers (ashx)
- together designer/developer 2005, for vs.net出来了。
- Best Practices for Speeding Up Your Web Site (from Yahoo! Developer Network)
- This file is based on the Microsoft Data Access Application Block for .NET
- ODT .NET 详解之环境搭配 ----Oracle Developer Tools for Visual Studio .NET
- .net winform openFileDialog1 显示缩略图 .FromFile(path)
- (转)Creating a DotNetNuke® Module Using CodeSmith Tools(For DotNetNuke Version 4.4.0 or higher)
- PDFSharp-a .NET library for creating and modifying Adobe PDF documents
- 5 Tips for creating good code every day; or how to become a good software developer
- GTK+ -- from knowing nothing to knowing something (4)
- There is no such thing as something for nothing.
- GTK+ -- from knowing nothing to knowing something (5)
- Can't install '*' from pristine store, because no checksum is recorded for this file
- 立即注册Oracle虚拟开发者日:Oracle WebLogic Server & Java EE (Virtual Developer Day: A New Breed of Events for Java developers )
- NetAPP proposed File service for OpenStack
- consume an asp.net webservice(upload a file to server) from java via soap
- Creating Linux virtual filesystems 学习
- QT:make: Nothing to be done for `first'和error:QtSql:No such file or directory
- download excel file from datagrid for webpage