hannodb
hannodb

Reputation: 253

How do you programmatically get the file name when dragging and dropping from .Net Solution explorer?

I want to write an application that produces zip files by directly dragging and dropping files from Visual Studio Solution Explorer into my app.

I've used the following code snippet to catch the incoming DataObject:

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

I've tried every possible value of DataFormats, all of them return false.

Upvotes: 2

Views: 1056

Answers (2)

Jimi
Jimi

Reputation: 32248

Since this task might be somewhat less simple than it looks like on paper, here's a sample procedure that should allow to get a list of the files dragged from the Visual Studio Solution Explorer panel.

The DataFormats that Visual Studio generates are partially common (UnicodeText and Text), but the actual file list is passed on in a (classic) MemoryStream object that is not a common DataFormat: CF_VSSTGPROJECTITEMS.

The MemoryStream contains Unicode text - the actual list of the Project+File Name tuples that are being dropped - separated by a pipe (|) symbol - and some other binary sections that I don't think is useful to describe here.

The other non common/predefined format, VX Clipboard Descriptor Format, is also a MemoryStream object, but it's just an Unicode string that contains the Project name.


In this sample, the combined elements that are part of the Drop are organized using a custom class object that contains informations about:

  • the name and UUID of the Project(s) where the files are coming from,
  • the Project path and file path (.[xxx]prj) ,
  • the name of the object that started the Drag&Drop operation,
  • a list of all files that are dropped, the Project they're part of and their raw type (.cs, .vb, .h, .png etc.)

Pick a control that will receive the Drop and add the handlers:

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);
}

The class object, VisualStudioDataObject, contains the methods needed to extract the informations from the DataObject referenced by the DragDrop event DragEventArgs:

(Tested with 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");
        }
    }
}

Upvotes: 4

Mar Tin
Mar Tin

Reputation: 2392

I try you approch out with a TreeView Control:

        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));
        };

After drop a cs-File from the solution explorer inside the tree, you can read out the path of the cs-File. The path you can use to convert the file to zip.

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

Hope it helps.

Upvotes: 0

Related Questions