【问题标题】:How do you programmatically get the file name when dragging and dropping from .Net Solution explorer?从 .Net 解决方案资源管理器拖放时,如何以编程方式获取文件名?
【发布时间】:2019-04-03 06:32:23
【问题描述】:

我想编写一个应用程序,通过直接将文件从 Visual Studio 解决方案资源管理器拖放到我的应用程序中来生成 zip 文件。

我使用以下代码 sn-p 来捕获传入的 DataObject:

private void lblIncludedFiles_DragEnter(object sender, DragEventArgs e)
{
    if (e.Data.GetDataPresent(DataFormats.FileDrop))
    {
        e.Effect = DragDropEffects.Copy;
    }
}

我已经尝试了DataFormats 的所有可能值,它们都返回 false。

【问题讨论】:

    标签: c# .net visual-studio winforms drag-and-drop


    【解决方案1】:

    由于此任务可能不像纸上看起来那么简单,因此这里有一个示例过程,它应该允许获取从 Visual Studio 解决方案资源管理器面板拖动的文件列表。

    Visual Studio 生成的DataFormats 是部分通用的(UnicodeTextText),但实际的文件列表是在不是通用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");
            }
        }
    }
    

    【讨论】:

    • 非常感谢。这个解决方案很完美!
    【解决方案2】:

    我尝试使用 TreeView 控件来解决问题:

            Tree.AllowDrop = true;
            Tree.DragEnter += (s, e) =>
            {
                e.Effect = DragDropEffects.Move;
            };
    
            Tree.DragDrop += (s, e) =>
            {
                var data = e.Data;
                var value = data.GetData(typeof(string));
            };
    

    从树内的解决方案资源管理器中删除 cs-File 后,您可以读出 cs-File 的路径。可用于将文件转换为 zip 的路径。

    >>> value = "C:\\Users\\Name\\Desktop\\Projekte\\Projekte-Visual Studio\\Project\\Project\\Classes\\Method.cs"
    

    希望对你有帮助。

    【讨论】:

    • 感谢您的宝贵时间。尽管您的解决方案适用于单项选择,但当您选择多个项目时,它仅显示第一项的文件。这就是我选择其他解决方案的原因。
    猜你喜欢
    • 2010-09-21
    • 1970-01-01
    • 2012-06-26
    • 2013-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-04
    相关资源
    最近更新 更多