Drag and Drop dropData is null

charles200221
Contributor
Contributor

Drag and Drop dropData is null

charles200221
Contributor
Contributor

Good day everyone.

I'm writing a revit API that can

1. list all .rfa files' name from another computer,

2. single click file name to download to a custom directory at my local computer and

3. drag file name to place the component into revit.

I've finished the first two but when I drag and drop the file name to revit, a prompt shows up and says

"Value cannot be null. Parameter name: dropData".

I'm using Visual Studio 2019, winForm, .NET Framework v4.5.2, revit 2017 dll and revit 2017.

I copy the code from here , using these codes.

 

 public class LoadedFamilyDropHandler : IDropHandler
        {
            public void Execute(UIDocument document, object data)
            {
                ElementId familySymbolId = (ElementId)data;
                FamilySymbol symbol = document.Document.GetElement(familySymbolId) as FamilySymbol;
                if(symbol != null)
                {
                    document.PromptForFamilyInstancePlacement(symbol);
                }
            }
        }

        private void listView1_MouseMove(object sender, MouseEventArgs e)
        {
            if(System.Windows.Forms.Control.MouseButtons == MouseButtons.Left)
            {
                if(System.Windows.Forms.Control.MouseButtons == MouseButtons.Left)
                {
                    ListViewItem selectedItem = this.listView1.SelectedItems.Cast<ListViewItem>().FirstOrDefault<ListViewItem>();
                    if(selectedItem != null)
                    {
                        LoadedFamilyDropHandler myhandler = new LoadedFamilyDropHandler();
                        UIApplication.DoDragDrop(selectedItem.Tag, myhandler);
                    }
                }
            }
        }

 

My custom directory path is D:\DownloadedFromVault\ which contains all .rfa files downloaded from another computer.

This is the code when I click the file name and download it from the list view.

client.DownloadFile(server + path + "\\" + listView1.SelectedItems[0].Text, localFilePath + path + "\\" + listView1.SelectedItems[0].Text);

I guess that API cannot find the file because I store them in another directory ,or maybe the file name doesn't connect to the file downloaded.

I don't know how exactly drag and drop functions. Hope someone can help me with this and give me some insights.

0 Likes
Reply
1,132 Views
11 Replies
Replies (11)

jeremytammik
Autodesk
Autodesk

I would suggest debugging carefully step by step and determining the exact line of code throwing that exception. 

 

That might help understand what 'dropData' argument has a null value causing the problem.

 



Jeremy Tammik
Developer Technical Services
Autodesk Developer Network, ADN Open
The Building Coder

charles200221
Contributor
Contributor

Hello, Jeremy. Thanks for the input.

I've found out that selectedItem has the file name but selectedItem.Tag is null. I've checked that selectedItem.Tag is Object and that's what dropData in doDrapDrop(dropData,IHandler) need. How can I give value to selectedItem.Tag?

0 Likes

charles200221
Contributor
Contributor

I referenced Revit 2019 SDK and wrote selectedItem.Tag = selectedItem.Text; since it wrote item.Tag = familySymbol.Id;. I thought the tag only needed the file name. When I dragged file name into revit, the error prompt didn't appear anymore. Instead, my cursor turned into prohibit sign. In debug mode, it seems like  LoadedFamilyDropHandler did not get executed. I couldn't place components into revit. Any ideas on what went wrong?

0 Likes

RPTHOMAS108
Mentor
Mentor

Tag is a generic container you can put anything into. It is generally more advantageous to bind the collection to the control than manually adding items such as ListViewItem (do some research on DataBinding). If you create your own non API object containing  members such as ElementId, String (things that can tolerate an existance outside of API context). You can put that into a collection (populated from your collector) bind it to the ListView, override .ToString on your object (as a starting point to display text of item in ListView). Then you can use SelectedItem.YourProperty as you like (you are not constrained by members of ListViewItem).  Additionally you can then pass the entire item through as dropData and use it as you like during the Execute method of IDropHandler interface. You avoided a null reference exception but you'll get null later if you try to cast a string (filename) to element id.

 

When you show your window did you use .Show (should not be using .ShowDialog).

 

I say use ElementId outside of context (which you can) but you have to be careful it relates to the correct document (active document may change during a session). I would probably use UniqueIds for this out of context purpose actually since worksharing can change ElementIds (rationalisation during synchronisation).

 

charles200221
Contributor
Contributor

Thank you for your reply. I really appreciate everyone's help.

Sorry if this is a basic question. I don't know what to google to solve my problem.

I changed .showDialog() to .show() and the prohibit sign disappeared. I can now drag components to revit but nothing appears.

Right now, I'm adding these codes to my code and I deleted selectedItem.Tag = selectedItem.Text; 'cause I thought that item.tag stored all ListViewItem tag's value so that selectedItem.Tag can get its value.

FYI, I have a treeView which contains directory path and listView which contains files in the directory. UpdateLoadedFamilies() is prior to both of them. I don't know if there's a order problem.

 

private static Form1 s_form;
        public static Form1 GetTheForm(Document document)
        {
            if(s_form == null)
            {
                s_form = new Form1(document);
            }
            s_form.UpdateLoadedFamilies();
            return s_form;
        }

        private void UpdateLoadedFamilies()
        {
            ListView.ListViewItemCollection collection = listView1.Items;
            collection.Clear();

            FilteredElementCollector collector = new FilteredElementCollector(m_document);
            collector.OfClass(typeof(FamilySymbol));
            
            foreach(FamilySymbol familySymbol in collector.Cast<FamilySymbol>())
            {
                ListViewItem item = new ListViewItem();
                item.Tag = familySymbol.Id;
                item.Text = familySymbol.Family.Name + "::" + familySymbol.Name;
                item.ToolTipText = "Drag to place instances of " + item.Text + " in the active document.";

                collection.Add(item);
            }
        }

 

I have these questions.

1. selectedItem.Tag is null. It didn't get the item.tag's value which type is object {Autodesk.Revit.DB.ElementId}.

2. The items are from revit's own library, not from my custom rfa file directory. I wonder how can I change it to my custom directory path.

Maybe there is some tutorials I can learn from. I don't know where to find them.

0 Likes

RPTHOMAS108
Mentor
Mentor

At what stage is .Tag null (just before exiting UpdateLoadedFamilies or within the drop handler), step through and see because at one stage you are definitely taking items from a collector and populating the tag with their ElementId. It may be that SelectedItems is empty at the point you are dragging but that is something you can only see through stepping. Also I'm not familiar with sample but most drag drop operations have three phases MouseButtonDown, MouseDrag, MouseButtonUp.

 

Ordinarily (outside of API when writing desktop applications) there are DragDrop events on the source and distination controls to take advantage of, within the API you ony have source events plus DoDragDrop function. 

 

Also please confirm if you are using Forms or WPF.

0 Likes

charles200221
Contributor
Contributor

1. I'm using winForm.

2. I've checked that selectedItem.Tag is null at the start. 

 

 

ListViewItem selectedItem = this.listView1.SelectedItems.Cast<ListViewItem>().FirstOrDefault<ListViewItem>();

 

 

I think it's because UpdateLoadedFamilies doesn't get files at my custom directory(D:\DownloadedFromVault\).

I've checked that they get values from Revit's own library which I don't understand how. That's why selectedItem does not have .tag value. I don't think any item in my listView have .Tag value either. I don't know which variable to change to make UpdateLoadedFamilies update files' path at D:\DownloadedFromVault\ though.

3. In sample, it only has listView_MouseMove. It does not have MouseButtonDown, MouseDrag, MouseButtonUp.

I also have listView_MouseClick which is to click the file name and download it from another computer. I've removed it and it still worked like it was before. So, I don't think it's affecting this.

0 Likes

RPTHOMAS108
Mentor
Mentor

You should step through the original sample located below to see if you have a similar issue if not then by comparison you may see where it is going wrong:

SDK...\Samples\UIAPI\CS\DragAndDrop\DragAndDrop.cs

 

You'll see from below code that the library paths are being taken from what you have set in your Revit options:

 

private void UpdateFamilyFileList()
      {
         // Visit each Revit library looking for Furniture families
         IDictionary<String, String> libraryPaths = m_document.Application.GetLibraryPaths();
         foreach (String libraryPath in libraryPaths.Values)
         {
             foreach (String directory in System.IO.Directory.EnumerateDirectories(libraryPath, "*Furniture", SearchOption.AllDirectories))
             {
                 foreach (String familyFile in System.IO.Directory.EnumerateFiles(directory, "*.rfa", SearchOption.AllDirectories))
                 {
                     // Add each Furniture family to the listbox
                     String fileName = Path.GetFileName(familyFile);
                     FamilyListBoxMember member = new FamilyListBoxMember(familyFile, fileName);
                     listBox1.Items.Add(member);
                 }
             }
         }
      }

 

It is extracting items from folders named 'Furniture' in these library paths.

0 Likes

charles200221
Contributor
Contributor

I'm really sorry. It's been too long now and I'm still in the bind here.

I really can't find out what went wrong with my code. 

No matter what I do, UpdateLoadedFamilies still gets the files from revit's own library instead of my file path.

The item.Tag's value won't pass to selectedItem.Tag since they reference different files.

I don't have listBox so I exclude UpdateFamilyFileList.

Here's my code. Hope someone know how to fix this.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;

namespace BIMconnectVault
{
    [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
    public class Class1 : IExternalCommand
    {
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            try
            {
                Form1 form = Form1.GetTheForm(commandData.View.Document);
                form.Show();
                form.BringToFront();
                return Result.Succeeded;
            }
            catch (Exception ex)
            {
                message = ex.Message;
                return Result.Failed;
            }
        }
    }
}
using System;
using System.IO;
using System.Net;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Configuration;
using System.Collections.Specialized;
using Autodesk.Revit.UI;
using System.Diagnostics;
using System.Data.SqlClient;
using Autodesk.Revit.DB;

namespace BIMconnectVault
{

    public partial class Form1 : System.Windows.Forms.Form
    {
        private ExternalCommandData commandData;
        private string server;
        private string path;
        private Document m_document;
       
        private static Form1 s_form;
        public static Form1 GetTheForm(Document document)
        {
            if (s_form == null)
            {
                s_form = new Form1(document);
            }
            s_form.UpdateLoadedFamilies();
            return s_form;
        }

        private void UpdateLoadedFamilies()
        {
            ListView.ListViewItemCollection collection = listView1.Items;
            collection.Clear();

            FilteredElementCollector collector = new FilteredElementCollector(m_document);
            
            collector.OfClass(typeof(FamilySymbol));
            
            foreach(FamilySymbol familySymbol in collector.Cast<FamilySymbol>())
            {
                ListViewItem item = new ListViewItem();
                item.Tag = familySymbol.Id;
                item.Text = familySymbol.Family.Name + "::" + familySymbol.Name;
                item.ToolTipText = "Drag to place instances of " + item.Text + " in the active document.";

                collection.Add(item);
            }
        }

        public Form1(Document document)
        {
            InitializeComponent();
            m_document = document;
            Open_Remote_Connection("IP", "vaultname", "password");
            DirectoryInfo directoryInfo = new DirectoryInfo("directorypath");
            if (directoryInfo.Exists)
            {
                BuildTree(directoryInfo, treeView1.Nodes);
            }
            listView1.Click += new System.EventHandler(ListView1_Click);
        }



        private void Open_Remote_Connection(string strComputer, string strUserName, string strPassword)
        {
            System.Diagnostics.ProcessStartInfo ProcessStartInfo = new System.Diagnostics.ProcessStartInfo();
            ProcessStartInfo.FileName = "net";
            ProcessStartInfo.Arguments = "use \\\\" + strComputer + "\\c$ /USER:" + strUserName + " " + strPassword;
            ProcessStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
            System.Diagnostics.Process.Start(ProcessStartInfo);
            System.Threading.Thread.Sleep(2000);
        }

        private void BuildTree(DirectoryInfo directoryInfo, TreeNodeCollection addInMe)
        {
            TreeNode curNode = addInMe.Add(directoryInfo.Name);
            foreach (DirectoryInfo subdir in directoryInfo.GetDirectories())
            {
                BuildTree(subdir, curNode.Nodes);
            }
        }

        void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
        {
            server = "\\\\IP\\";
            path = treeView1.SelectedNode.FullPath;
            string serverPath = server + path;
            DirectoryInfo directoryInfo = new DirectoryInfo(serverPath);
            listView1.Clear();
            foreach (FileInfo file in directoryInfo.GetFiles())
            {
                listView1.Items.Add(file.Name);
            }
        }

        void ListView1_Click(Object sender, EventArgs e)
        {
            string localFilePath = "D:\\DownloadFromVault\\";
            UIApplication app = commandData.Application;
            Document document = app.ActiveUIDocument.Document;
            using (var client = new WebClient())
            {
                if (!File.Exists(localFilePath + path + "\\" + listView1.SelectedItems[0].Text))
                {
                    if (!Directory.Exists(localFilePath + path))
                    {
                        Directory.CreateDirectory(localFilePath + path);
                    }
                    client.DownloadFile(server + path + "\\" + listView1.SelectedItems[0].Text, localFilePath + path + "\\" + listView1.SelectedItems[0].Text);
                    string query = "query";
                    SQLtoCSV(query, listView1.SelectedItems[0].Text);
                    MessageBox.Show("Download successfully.");
                }
                else
                {
                    MessageBox.Show("File already Exists.");
                }
            }
        }

        private void SQLtoCSV(string query, string Filename)
        {
            string localFilePath = "localFilePath ";
            string csvFileName = Filename.Replace(".rfa", ".csv");
            string connectionString = "connectionString";
            SqlConnection conn = new SqlConnection(connectionString);
            conn.Open();
            SqlCommand cmd = new SqlCommand(query, conn);
            SqlDataReader dr = cmd.ExecuteReader();
            if (!Directory.Exists(localFilePath + path))
            {
                Directory.CreateDirectory(localFilePath + path); 
            }
            using (System.IO.StreamWriter fs = new System.IO.StreamWriter(localFilePath + path + "\\" + csvFileName, false, Encoding.UTF8))
            {
                for (int i = 0; i < dr.FieldCount; i++)
                {
                    string name = dr.GetName(i);
                    if (name.Contains(","))
                    {
                        name = "\"" + name + "\"";
                    }
                    fs.Write(name + ",");
                }
                fs.WriteLine();
                while (dr.Read())
                {
                    for (int i = 0; i < dr.FieldCount; i++)
                    {
                        string value = dr[i].ToString();
                        if (value.Contains(","))
                        {
                            value = "\"" + value + "\"";
                        }
                        fs.Write(value + ",");
                    }
                    fs.WriteLine();
                }
                fs.Close();
            }
        }

        public class LoadedFamilyDropHandler : IDropHandler
        {
           public void Execute(UIDocument document, object data)
           {
               ElementId familySymbolId = (ElementId)data;
               FamilySymbol symbol = document.Document.GetElement(familySymbolId) as FamilySymbol;
               if(symbol != null)
               {
                   document.PromptForFamilyInstancePlacement(symbol);
               }
           }
        }

        private void listView1_MouseMove(object sender, MouseEventArgs e)
        {
           if(System.Windows.Forms.Control.MouseButtons == MouseButtons.Left)
           {
              ListViewItem selectedItem = this.listView1.SelectedItems.Cast<ListViewItem>().FirstOrDefault<ListViewItem>();
              if(selectedItem != null)
               {
                   LoadedFamilyDropHandler myhandler = new LoadedFamilyDropHandler();
                   UIApplication.DoDragDrop(selectedItem.Tag, myhandler);
               }
           }
        }
    }
}

 

0 Likes

RPTHOMAS108
Mentor
Mentor

Seems odd, do you have directory or location called "directorypath" would have expected UNC path for IO.DirectoryInfo within your Form1 constructor, by consequence is BuildTree being run?

 

Why would Revit know where to look for something perhaps by a setting or by default in absence of one? Have you eliminated  Document.Application.GetLibraryPaths() everywhere?

 

I think you should still go back to original sample and change Document.Application.GetLibraryPaths within UpdateFamilyFileList to another local location to understand that before moving onto more complex things with remote server locations etc.

 

0 Likes

charles200221
Contributor
Contributor

Hello, everyone.

Because I stuck here for too long, I decided to change the method to place the component.

I used a button and external event to load and place rfa files right now.

I'll come back to this question in the future.

Thanks for everyone's help.

0 Likes