Community
Navisworks API
Welcome to Autodesk’s Navisworks API Forums. Share your knowledge, ask questions, and explore popular Navisworks API topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

List all of the Categories in the Model

11 REPLIES 11
Reply
Message 1 of 12
hnseir
4851 Views, 11 Replies

List all of the Categories in the Model

I am wondering if there is any way in API that allow to list all the Categories available in the model. Something very similar to what is available in Find Items interface.

 

Find Items Interface.jpg

11 REPLIES 11
Message 2 of 12
hnseir
in reply to: hnseir

I am trying to replicate what happens at the Find Items Interface where by clicking on the Category List, all the unique categories in the entire model get listed; and by clicking on the Property List, all the unique properties in the entire model under the selected category get listed. I have looked in .Net API and .COM API with no success.

 

In .Net, I accessed the PropertyCategoryCollection class for every ModelItem in the entire model and ran If Statement to find a unique PropertyCategory and PropertyData. However, this takes forever in a large model.

 

In COM, I used GetGUIPropertyNode and GUIAttributes methods to accessed InwGUIAttributesColl class for every InwOaPath in the entire model and ran If Statement to find a unique InwOaPropertyAttribute and InwOaProperty. Same thing, the process would take a long time.

 

Is there any way to make this process faster similar to the Find Items interface

Message 3 of 12
xiaodong_liang
in reply to: hnseir

Hi,

 

Sorry, as I know, there is not a direct way to dump all Categories. What I can suggest is to dump the Categories when you load the document the first time, stores them with a map < ModelItem , string list>. Thus when a model item is selected, you can achieve the string list directly, without dumping again. 

Message 4 of 12
sergeyzh
in reply to: xiaodong_liang

Hello Xiadong,

 

I'm very curious on this question too. And I'm still trying to get that list as fast as Navis does. I tried several scenarios and now I’m stopped at those:

 

 

1. Use Search() class to collect all items in model. Why Search() and not ActiveDocument.Model[0].RootItem.DescendantAndSelf ? Because second collection is accessed very slowly. So I do:

 

Search s1 = new Search();

s1.PruneBelowMatch = false;

s1.SearchConditions.Clear();

ModelItemCollection micTmp = new ModelItemCollection();

micTmp.Add(activeDoc.Models[0].RootItem);

s1.Selection.CopyFrom(micTmp);

searchResults = new ModelItemCollection();

SearchCondition oGroup1_sc = new SearchCondition(new NamedConstant("Item"), new NamedConstant(""), SearchConditionOptions.IgnoreNames, SearchConditionComparison.HasCategory, VariantData.FromNone());

s1.SearchConditions.Add(oGroup1_sc);

searchResults = s1.FindAll(Autodesk.Navisworks.Api.Application.ActiveDocument, true);

var cats = (from mi in searchResults from cats in mi.PropertyCategories select cats.DisplayName).Distinct();

 

FindAll() runs fast even on model with 500k ModelItems. And next LINQ runs faster too - we get list with unique PropertyCategories for whole model! But then we try to access that list just by

MessageBox.Show(String.Join("\n", cats.ToArray()));

Navis gets stuck on that for a very long time...

Does we can optimize that piece of code to run as faster as Navis?

 

 

2. Second approach that somewhat successful and dirt workaround at once:

 

        Document activeDoc = NWApi.Application.ActiveDocument;

        int iCnt = 0;

        Search s1 = null;

        ModelItemCollection searchResults = null;

        List<string> lstCats = new List<string>();

        public override CommandState CanExecuteCommand(String commandId)

        {

            if (s1 == null && activeDoc.Models.Count > 0)

            {

                s1 = new Search();

                s1.PruneBelowMatch = false;

                s1.SearchConditions.Clear();

                ModelItemCollection micTmp = new ModelItemCollection();

                micTmp.Add(activeDoc.Models[0].RootItem);

                s1.Selection.CopyFrom(micTmp);

                searchResults = new ModelItemCollection();

                SearchCondition oGroup1_sc = new SearchCondition(new NamedConstant("Item"), new NamedConstant(""), SearchConditionOptions.IgnoreNames, SearchConditionComparison.HasCategory, VariantData.FromNone());

                s1.SearchConditions.Add(oGroup1_sc);

                searchResults = s1.FindAll(Autodesk.Navisworks.Api.Application.ActiveDocument, true);

            }

            if (s1 != null && iCnt < searchResults.Count)

            {

                for (int i = 0; i < 100; i++)

                {

                    ModelItem mi;

                    try

                    {

                        mi = searchResults[iCnt];

                    }

                    catch { MessageBox.Show("Searching categories in background complited"); break;  }

                    foreach (var cat in mi.PropertyCategories)

                    {

                        if (!lstCats.Contains(cat.DisplayName)) lstCats.Add(cat.DisplayName);

                    }

                    iCnt++;

                }

            }

                //process over things

            }

 

It is an attempt to simulate threading and it works. On models with 500k MI it finishes during 3 minutes on Core i5 2.5mhz. In that period of time Navis UI behaves a bit slowly but enough for user interaction. And after it finished we will get unique list of PropertyCategories in lstCats.

 

It is very interesting that LINQ is used by Navis. Loading even large model takes several seconds and after that if we open "Find Items" dialog and click on "Properties" tab we will see the whole categories tree list with its properties and values. I can suppose that Navis stores that list somewhere in Document(). For example in ActiveDocument.DocumentInfo.PropertyCategories. But that list is always is empty...

 

Xiadong, may be you can take us some clues?

 

With regards. Sergey.

 

Message 5 of 12
xiaodong_liang
in reply to: sergeyzh

Hi Sergey,

 

the 1st way is a nice solution. I have not found a better way. About the performance when you messagebox the array, I doubted if this is a specific issue with ToArray. I did saw some discussions om internet. 

 

I also tried with your code on a demo file. There are not much categories. The code works well. I did not see a problem with messagebox. How many categories with your file?

 

About performance of Navisworks, it would be one possible way it stores with the file, but I still think it just dumps it when the user wants to find the first time after the document is opened. e.g. when the file is opening, or the Find dialog is being invoked. I must confess I do not know how the core code is. In addition, since it is a core code, that means Navisworks itself can communicate with the data directly, without calling back-forth through the managed codes. So it is faster.

 

If you want to store something with the document, Document Database is one good choice. 

http://adndevblog.typepad.com/aec/2013/07/document-database-part-1.html

 

 

Anyway, I am also curious to know why messagebox gets Navisworks stuck. If you could provide more info, I'd like to take a look. If it is confidential, please send me private message.

Message 6 of 12
sergeyzh
in reply to: xiaodong_liang

Hello Xiadong!

 

As for the fact that Navis stucks on collection access - it is looks like "deffered" collection, that means that actual operation will start after first access to the collection's elements.

 

But I'm very interesting in threading such tasks. Is there any possibility to run very long and fair collection crunching in background?

 

With regards, Sergey.

Message 7 of 12
ratno123s
in reply to: sergeyzh

I am working on a similar task where I need to create schedules for Revit Elements in a spreadsheet.

 

For this I need to collect all the distinct values of the Property "Category" inside the PropertyCategory "Element" (if they exist for a ModelItem) and show the list in a litsbox (listBox_categories). Then on selecting a value in this listbox the  I need to display the distinct DisplayNames of the Properties in the PropertyCategory "Element" available in the ModelItems in a listbox (listBox_Properties). Finally I need to get the values for the selected properties of all the relevent ModelItems and display them in a gridview.

 

To achieve this I have written the following code:

List<string> properties = new List<string>();
Document doc;
DataTable dt=new DataTable();
long timeTaken;
public UI_CreateSchedule()
{
InitializeComponent();
this.doc = Application.ActiveDocument;
}

private async void UI_CreateSchedule_Load(object sender, EventArgs e)
{

await Task.Run(new Action(() =>
{
Search search = new Search();
SearchCondition sc1 = SearchCondition.HasPropertyByDisplayName("Element", "Category");
search.SearchConditions.Add(sc1);
search.Selection.SelectAll();
search.Locations = SearchLocations.DescendantsAndSelf;
List<ModelItem> modelItems = search.FindAll(Autodesk.Navisworks.Api.Application.ActiveDocument,false).ToList();
if (modelItems.Count > 0)
{
List<string> categories = modelItems.Select(x => x.PropertyCategories.Where(y => y.DisplayName == "Element").FirstOrDefault().Properties.Where(y => y.DisplayName == "Category").FirstOrDefault().Value.ToDisplayString()).ToList();
//foreach (ModelItem mi in modelItemCollection)
//{
// categories.Add(mi.PropertyCategories.Where(x => x.DisplayName == "Element").FirstOrDefault().Properties.Where(x => x.DisplayName == "Category").FirstOrDefault().Value.ToDisplayString());
//}
categories = categories.Distinct().OrderBy(x=> x).ToList();
this.Invoke(new System.Action(() => { listBox_categories.DataSource = categories; }));
}
}));
}
public List<ModelItem> FillSCModelItemsList()
{
List<ModelItem> SCModelItems = new List<ModelItem>();
try
{
List<string> selectedCategories = listBox_categories.SelectedItems.Cast<string>().ToList();
Search search = new Search();
search.Selection.SelectAll();
search.Locations = SearchLocations.DescendantsAndSelf;
foreach (string category in selectedCategories)
{
SearchCondition sc = SearchCondition.HasPropertyByDisplayName("Element", "Category").EqualValue(VariantData.FromDisplayString(category));
search.SearchConditions.AddGroup(new List<SearchCondition>() { sc });
}

SCModelItems = search.FindAll(Autodesk.Navisworks.Api.Application.ActiveDocument, false).ToList();

}
catch (Exception ex)
{
MessageBox.Show("unable to get the elements. Running low on memory or the file is corrupted. Please try later..");
}
return SCModelItems;
}
public ModelItemCollection getSCModelItemCollection()
{
ModelItemCollection coll = new ModelItemCollection();
try
{
List<string> selectedCategories = listBox_categories.SelectedItems.Cast<string>().ToList();
Search search = new Search();
search.Selection.SelectAll();
search.Locations = SearchLocations.DescendantsAndSelf;
foreach (string category in selectedCategories)
{
SearchCondition sc = SearchCondition.HasPropertyByDisplayName("Element", "Category").EqualValue(VariantData.FromDisplayString(category));
search.SearchConditions.AddGroup(new List<SearchCondition>() { sc });
}
Thread.Sleep(2);
coll = search.FindAll(Autodesk.Navisworks.Api.Application.ActiveDocument, false);
}
catch (Exception ex)
{
MessageBox.Show("unable to get the elements. Running low on memory or the file is corrupted. Please try later..");
}
return coll;
}
private void listBox_categories_SelectedIndexChanged(object sender, EventArgs e)
{
this.Invoke(new System.Action(() => { listBox_Properties.DataSource = null; }));

//await Task.Run(new Action(() =>
//{


List<ModelItem> SCModelItems = FillSCModelItemsList();
List<string> selectedCategories = listBox_categories.SelectedItems.Cast<string>().ToList();

//foreach (ModelItem mi in SCModelItems)
//{
// properties.AddRange(mi.PropertyCategories.Where(x => x.DisplayName == "Element").FirstOrDefault().Properties.Select(x => x.DisplayName).ToList());
//}
//properties = properties.Distinct().OrderBy(x => x).ToList();
//listBox_Properties.DataSource = properties;
Thread.Sleep(100);
if (SCModelItems.Count > 0)
{
properties = SCModelItems.Where(x => selectedCategories.Contains(x.PropertyCategories.Where(y => y.DisplayName == "Element").FirstOrDefault().Properties.Where(y => y.DisplayName == "Category").FirstOrDefault().Value.ToDisplayString()))
.SelectMany(x => x.PropertyCategories.Where(y => y.DisplayName == "Element").FirstOrDefault().Properties.Select(y => y.DisplayName).ToList()).ToList();
properties = properties.Distinct().OrderBy(x => x).ToList();
this.Invoke(new System.Action(() => { listBox_Properties.DataSource = properties; }));
}

SCModelItems = null;

//}));

}

private async void button_show_Click(object sender, EventArgs e)
{
ModelItemCollection SCModelItemsCollections = getSCModelItemCollection();
await Task.Run(new Action(() =>
{
DatabaseManager.SetTime();
List<string> selectedProperties = listBox_Properties.SelectedItems.Cast<string>().ToList();
if (selectedProperties.Count > 0 && SCModelItemsCollections.Count > 0)
{
dt = new DataTable();
foreach (string selectedProperty in selectedProperties)
{
dt.Columns.Add(selectedProperty);
}
dt.Columns.Add("Attachment");
//List<ModelItem> modelItems = SCModelItemsCollections.Cast<ModelItem>().ToList();
//List<ModelItem> SCmodelItems = modelItems.Where(x => selectedCategories.Contains(x.PropertyCategories.Where(y => y.DisplayName == "Element").FirstOrDefault().Properties.Where(y => y.DisplayName == "Category").FirstOrDefault().Value.ToDisplayString())).ToList();
this.Invoke(new System.Action(() => { progressBar1.Maximum = SCModelItemsCollections.Count; progressBar1.Step = 1; progressBar1.Value = 0; }));
foreach (ModelItem SCmodelItem in SCModelItemsCollections)
{
try
{
List<string> row = new List<string>();
foreach (DataColumn dc in dt.Columns)
{
try
{
if (dc.ColumnName == "Attachment")
{
row.Add(getSuperParentName(SCmodelItem));
}
else
{
DataProperty dp = SCmodelItem.PropertyCategories.Where(x => x.DisplayName == "Element").FirstOrDefault().Properties.Where(x => x.DisplayName == dc.ColumnName).FirstOrDefault();
row.Add(getValue(dp));
}
}
catch (Exception)
{

}

Thread.Sleep(5);
}
dt.Rows.Add(row.ToArray());
Thread.Sleep(5);
this.Invoke(new System.Action(() => { progressBar1.PerformStep(); }));
}
catch (Exception ex)
{

}
}
this.Invoke(new System.Action(() => { dataGridView_Schedule.DataSource = null; dataGridView_Schedule.DataSource = dt; dataGridView_Schedule.Refresh(); }));

}
timeTaken = 0;
timeTaken = Convert.ToInt64(DatabaseManager.GetDuration());
DatabaseManager.InsertUsages("CreateRevitSchedule", System.Environment.MachineName, System.Security.Principal.WindowsIdentity.GetCurrent().Name, dt.Rows.Count, timeTaken, AppVariables._ApplicationName, this.doc.CurrentFileName);
}));

}
private string getValue(DataProperty dp)
{
if(dp!= null && !dp.Value.IsNone)
{
if (dp.Value.IsDouble) return dp.Value.ToDouble() + "";
else if (dp.Value.IsBoolean) return dp.Value.ToBoolean() + "";
else if (dp.Value.IsDateTime) return dp.Value.ToDateTime() + "";
else if (dp.Value.IsDisplayString) return dp.Value.ToDisplayString();
else if (dp.Value.IsDoubleAngle) return dp.Value.ToDoubleAngle() + "";
else if (dp.Value.IsDoubleArea) return dp.Value.ToDoubleArea() + "";
else if (dp.Value.IsDoubleLength) return dp.Value.ToDoubleLength() + "";
else if (dp.Value.IsDoubleVolume) return dp.Value.ToDoubleVolume() + "";
else if (dp.Value.IsInt32) return dp.Value.ToInt32() + "";
else if (dp.Value.IsIdentifierString) return dp.Value.ToIdentifierString();
else if (dp.Value.IsNamedConstant) return dp.Value.ToNamedConstant().DisplayName;
else if (dp.Value.IsPoint2D) return dp.Value.ToPoint2D().X + ", " + dp.Value.ToPoint2D().Y;
else if (dp.Value.IsPoint3D) return dp.Value.ToPoint3D().X + ", " + dp.Value.ToPoint3D().Y + "," + dp.Value.ToPoint3D().Z;
else return "unknown";
}
else
{
return "";
}
}
public string getSuperParentName(ModelItem mi)
{
string name = "";
try
{
bool cont = true;
ModelItem parent;
do
{
parent = mi.Parent;
if (parent != null)
{
PropertyCategory pc = parent.PropertyCategories.Where(x => x.DisplayName == "XRef").FirstOrDefault();
if (pc != null)
{
pc = parent.PropertyCategories.Where(x => x.DisplayName == "Item").FirstOrDefault();
name = pc.Properties.Where(x => x.DisplayName == "Name").FirstOrDefault().Value.ToDisplayString();
cont = false;
}
}
else
{
cont = false;
}
mi = parent;
}
while (cont);
}
catch (Exception ex)
{

}
return name;
}

private void button_export_Click(object sender, EventArgs e)
{
try
{

if (dt != null && !dt.HasErrors)
{
using (FileStream stream = new FileStream(@"c:\report.xls", FileMode.Create, FileAccess.Write))
{
ExcelWriter writer = new ExcelWriter(stream);
writer.BeginWrite();
int row = 1, col = 0;
foreach (DataColumn dc in dt.Columns)
{
writer.WriteCell(0, col, dc.ColumnName);
col++;
}

foreach (DataRow dr in dt.Rows)
{
for (int i = 0; i < col; i++)
{
writer.WriteCell(row, i, dr[i] + "");
}
row++;
}
writer.EndWrite();
stream.Close();
}

Process.Start(@"c:\report.xls");
}
}
catch (Exception ex)
{
MessageBox.Show("unable to create report. Check whether the file is open or not");
}
}

 

But to my vain this crashes the navisworks application when running in large files. The problems that I face while in debug mode is "memory is corrupt", or sometimes the Navisworks just hangs and becomes not responding at the search.FindAll().

I am going crazy as to why this is happening. First I thought that may be the system is getting overloaded so I gave Thead.sleep() to make the system relax a bit. but that didnot help as because the search.findall is crashing over me everytime

I have tried using search.findIncremental() and also tried foreach ing through the model collection. but the search.findAll or search.findincremental is giving me pains.

Please help or suggest some ideas

 

 

Message 8 of 12
ratno123s
in reply to: ratno123s

I am Attaching a snapshot of the kind of errors that I am gettingimage.png

I just cannot understand this error.. why this is comming

 

Message 9 of 12
elmitodeherta2
in reply to: ratno123s

@ratno123s did you fix the issue? If yes, could you explain me how please?

Message 10 of 12
ratno123s
in reply to: elmitodeherta2

Apologise for the late reply...

I posted the work progress of my thread to navisworks default progress counter... It seems that when there is a long thread you want to run, you have to report it to the navisworks default progress counter... That way navisworks understands that your thread is still running and wont let it go.. You also get the advantage of displaying your progress through Navisworks' interface....

Message 11 of 12
alexisDVJML
in reply to: ratno123s

Wondering what you mean by "navisworks default progress counter".
I use Navisworks Progress class for long operations but not in async mode and the Progress class display a dialog that must be completed/cancelled before anything else can run.

So really interested to know more on your technique 🙂

Main Scientist, Full Stack Developer & When Time Permits Director of IDIGO ► On your marks, Set, Go
Message 12 of 12
skjeedig
in reply to: alexisDVJML

Did you get any way to read those properties from navisworks file which are added through data tools?

 

 

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Rail Community


Autodesk Design & Make Report