由于此任务可能不像纸上看起来那么简单,因此这里有一个示例过程,它应该允许获取从 Visual Studio 解决方案资源管理器面板拖动的文件列表。
Visual Studio 生成的DataFormats 是部分通用的(UnicodeText 和Text),但实际的文件列表是在不是通用DataFormat 的(经典)MemoryStream 对象中传递的:CF_VSSTGPROJECTITEMS.
MemoryStream 包含 Unicode 文本 - 正在删除的项目 + 文件名元组的实际列表 - 由管道 (|) 符号分隔 - 以及我认为描述无用的其他一些二进制部分在这里。
另一种非通用/预定义格式,VX Clipboard Descriptor Format,也是一个 MemoryStream 对象,但它只是一个包含项目名称的 Unicode 字符串。
在此示例中,属于 Drop 的组合元素使用自定义类对象进行组织,该对象包含以下信息:
- 文件来自的项目的名称和 UUID,
- 项目路径和文件路径(
.[xxx]prj),
- 启动拖放操作的对象的名称,
- 所有已删除文件的列表、它们所属的项目及其原始类型(
.cs、.vb、.h、.png 等)
选择一个将接收 Drop 的控件并添加处理程序:
private void ctlVSDrop_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetFormats().Contains("CF_VSSTGPROJECTITEMS"))
{
e.Effect = DragDropEffects.Copy;
}
}
private void ctlVSDrop_DragDrop(object sender, DragEventArgs e)
{
var vsObject = new VisualStudioDataObject(e.Data);
}
类对象VisualStudioDataObject包含从DragDrop事件DragEventArgs引用的DataObject中提取信息所需的方法:
(使用 Visual Studio 2017 测试)
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
class VisualStudioDataObject
{
public VisualStudioDataObject(IDataObject data)
{
if (data is null) {
throw new ArgumentNullException("IDataObject data", "Invalid DataObject");
}
FileList = new List<FileObject>();
GetData(data);
}
public List<FileObject> FileList { get; private set; }
public string ProjectUUID { get; private set; }
public string ProjectPath { get; private set; }
public string ProjectFilePath { get; private set; }
public string StartingObject { get; private set; }
public class FileObject
{
public FileObject(string project, string path, string type) {
SourceProject = project;
FilePath = path;
FileType = type;
}
public string SourceProject { get; }
public string FilePath { get; }
public string FileType { get; }
}
private void GetData(IDataObject data)
{
List<string> formats = data.GetFormats(false).ToList();
if (formats.Count == 0) return;
foreach (string format in formats) {
switch (format) {
case "UnicodeText":
StartingObject = data.GetData(DataFormats.UnicodeText, true).ToString();
break;
case "VX Clipboard Descriptor Format":
var projectMS = (MemoryStream)data.GetData(format, false);
projectMS.Position = 0;
string prjFile = Encoding.Unicode.GetString(projectMS.ToArray()).TrimEnd("\0".ToCharArray());
ProjectFilePath = prjFile;
ProjectPath = Path.GetDirectoryName(prjFile);
break;
case "CF_VSSTGPROJECTITEMS":
GetFileData((MemoryStream)data.GetData(format, false));
break;
}
}
}
private void GetFileData(MemoryStream ms)
{
string uuidPattern = @"\{(.*?)\}";
string content = Encoding.Unicode.GetString(ms.ToArray());
//Get the Project UUID and remove it from the data object
var match = Regex.Match(content, uuidPattern, RegexOptions.Singleline);
if (match.Success) {
ProjectUUID = match.Value;
content = content.Replace(ProjectUUID, "").Substring(match.Index);
//Split the file list: Part1 => Project Name, Part2 => File name
string[] projectFiles = content.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < projectFiles.Length; i += 2) {
string sourceFile = projectFiles[i + 1].Substring(0, projectFiles[i + 1].IndexOf("\0"));
FileList.Add(new FileObject(projectFiles[i], sourceFile, Path.GetExtension(sourceFile)));
}
}
else {
FileList = null;
throw new InvalidDataException("Invalid Data content");
}
}
}