Is the parameter empty?

Is the parameter empty?

dem1st
Enthusiast Enthusiast
2,163 Views
8 Replies
Message 1 of 9

Is the parameter empty?

dem1st
Enthusiast
Enthusiast

Once sync is done an event triggers. The idea of it is to show a list of all instances, sheets, views in the project that *have a specific parameter AND *it is unfilled so that a user can see all of them and fill the parameter with the required text. Any recommendations/advice will be greatly appreciated.

PS: not a lot of API experience.

 

 

class ExternalDBapp : IExternalDBApplication
    {
        public ExternalDBApplicationResult OnShutdown(ControlledApplication application)
        {            
            application.DocumentSynchronizedWithCentral -= FilledParameter;
            return ExternalDBApplicationResult.Succeeded;
        }

        public ExternalDBApplicationResult OnStartup(ControlledApplication application)
        {           
            application.DocumentSynchronizedWithCentral += new EventHandler<DocumentSynchronizedWithCentralEventArgs>(FilledParameter);                        
            return ExternalDBApplicationResult.Succeeded;
        }

        public void FilledParameter(object sender, DocumentSynchronizedWithCentralEventArgs args)
        {         
            //??

            TaskDialog.Show();
        }
    }
}

 

 

0 Likes
2,164 Views
8 Replies
Replies (8)
Message 2 of 9

guillain.jolivet
Contributor
Contributor

First you have to collect all you views and sheets (with a filtered element collector), and then you have several ways. The simplest is to iterate over the collector, lookup the parameter name and check if it has value (with the HasValue property)

List<BuiltInCategory> categories = new List<BuiltInCategory>()
{
    BuiltInCategory.OST_Views,
    BuiltInCategory.OST_Sheets
};

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

ElementMulticategoryFilter filter = new ElementMulticategoryFilter(categories);

FilteredElementCollector collector = new FilteredElementCollector(doc).WherePasses(filter).WhereElementIsNotElementType();

foreach (Element el in collector)
{
    Parameter param = el.LookupParameter("param_to_read");
    if (!param.HasValue)
    {
         views_sheets_names.Add(el.Name);
    }
}

TaskDialog td = new TaskDialog("Warning");
td.MainContent = string.Join("\n", views_sheets_names);
td.Show();
Message 3 of 9

dem1st
Enthusiast
Enthusiast

We need one class (implementing IExternalDBApplication) if we want an event to work and we need another class-file (implementing IExternalCommand) to get the Document. Like in the picture above program can't find the document and I can't write "Document doc = application.ActiveUIDocument.Document". It will not work, because it says "... does not exist in the current context".

On the other hand, if we don't implement IExternalDBApplication interface we get an error.

 

er.png

 

a.png

0 Likes
Message 4 of 9

guillain.jolivet
Contributor
Contributor

 You can get the document with your DocumentSynchronizedWithCentralEventArgs

 

public void FilledParameter(object sender, DocumentSynchronizedWithCentralEventArgs args)
{
    Document doc = args.Document;
}
Message 5 of 9

RPTHOMAS108
Mentor
Mentor

You should use an ElementParameterFilter in conjunction with StringEquals rule evaluator i.e. string = "". This is a slow filter so should combine it with quick filter category/class etc..

 

Parameter.HasValue will return true if the text has been set to "" i.e. if it had a value and someone deleted it.  This is clearer on double storage types i.e. they are empty to start with but can then only be set to 0 later in UI. Refer to Parameter.ClearValue.

Message 6 of 9

guillain.jolivet
Contributor
Contributor

@RPTHOMAS108 Ok, i didn't know about Parameter.HasValue if you set a value then delete it. I usually test for specific values. Thanks for the information.

 

 

@dem1styou have to combine the previous solution (the quickfilter  ElementMultiCategoryFilter) with a slow filter ElementParameterFilter using a FilterStringRule.

Message 7 of 9

dem1st
Enthusiast
Enthusiast

Based on your pieces of advice here's what I was able to come up with, am I on the right path?

 

    class ExternalDBapp : IExternalDBApplication
    {
        public ExternalDBApplicationResult OnShutdown(ControlledApplication application)
        {
            application.DocumentSynchronizingWithCentral -= SyncReminder;
            return ExternalDBApplicationResult.Succeeded;
        }

        public ExternalDBApplicationResult OnStartup(ControlledApplication application)
        {
            application.DocumentSynchronizingWithCentral += new EventHandler<DocumentSynchronizingWithCentralEventArgs>(SyncReminder);

            return ExternalDBApplicationResult.Succeeded;
        }

        public void SyncReminder(object sender, DocumentSynchronizingWithCentralEventArgs args)
        {
            List<BuiltInCategory> categories = new List<BuiltInCategory>()
            {
              BuiltInCategory.OST_Views,
              BuiltInCategory.OST_Sheets,
              BuiltInCategory.OST_PipeFitting
            };

            ElementId nameParamterId = new ElementId(BuiltInParameter.DATUM_TEXT);
            ParameterValueProvider pvp = new ParameterValueProvider(nameParamterId);
            FilterStringRuleEvaluator evaluator = new FilterStringEquals();
            FilterRule rule = new FilterStringRule(pvp, evaluator, "", false);
            ElementMulticategoryFilter filter1 = new ElementMulticategoryFilter(categories);
            
            ElementParameterFilter filter2 = new ElementParameterFilter(rule);

            Document doc = args.Document;
            FilteredElementCollector collector = new FilteredElementCollector(doc).WherePasses(filter1).WherePasses(filter2).WhereElementIsNotElementType();
            IList<Element> collectedElements = collector.ToElements();
            List<string> views_sheets_elements = new List<string>();
            foreach (Element el in collectedElements)
            {
                views_sheets_elements.Add(el.Name);
            }

            TaskDialog td = new TaskDialog("Warning");
            td.MainContent = string.Join("\n", views_sheets_elements);
            td.Show();

        }
    }
}

 

After sync in the dialog box we must see a list of names of all instances, sheets, views in the project that *have a specific parameter AND *it is unfilled so that a user can see all of them and fill the parameter. In this code "filter2" is responsible for checking parameters with unfilled parameter. ALTHOUGH I can't put my finger on how to specify the name of the parameter we need (let's say its name is "param_to_read")? "FilterRule" class constructor requires "ParameterValueProvider" and the last one can only accept some "BuiltInParameter". Does it mean there is no way to have my own project or shared parameter "param_to_read"?

 

datumText.png

 

@RPTHOMAS108is this code above something you had in mind?

 

Thank you for your efforts and patience!

0 Likes
Message 8 of 9

RPTHOMAS108
Mentor
Mentor

I believe you are supposed to do as follows:

 

For shared parameters (project bound or family): filter for class of SharedParameterElement with GUID matching your shared parameter, then use the id of this.

For non-shared project bound parameters: filter for the base class of ParameterElement you can only compare the name and storage type so ensure it is unique, then use the id of this.

If the Parameter is non-shared and comes from the family then I believe you can't use it.

 

The underlying system is from what is used in visibility filters so has the same limitations i.e. no non-shared family parameters.

  

Message 9 of 9

RPTHOMAS108
Mentor
Mentor

Example of filtering for SharedParameterElement and ParameterElement:

 

public Result Obj_211005a(ExternalCommandData commandData, ref string message, ElementSet elements)
		{

			dynamic uidoc = commandData.Application.ActiveUIDocument;
			dynamic IntDoc = uidoc.Document;

			FilteredElementCollector FEC = new FilteredElementCollector(IntDoc);

			const string TargetName = "Text Param 1";
			ParameterElement El = FEC.OfClass(typeof(ParameterElement)).Cast<ParameterElement>().FirstOrDefault(x => x.Name == TargetName && x.GetDefinition().GetDataType() == SpecTypeId.String.Text);

			//Alternative for shared parameters
			Guid TargetGUID = new Guid("356E9AB7-8F4C-456A-924D-11A20A28D84F");
			SharedParameterElement Elx = FEC.OfClass(typeof(SharedParameterElement)).Cast<SharedParameterElement>().FirstOrDefault(x => x.GuidValue == TargetGUID);


			if (El == null)
				return Result.Cancelled;

			ParameterValueProvider PVP = new ParameterValueProvider(El.Id);
			FilterStringEquals FSEq = new FilterStringEquals();
			FilterStringRule FR = new FilterStringRule(PVP, FSEq, "");

			ElementParameterFilter EPF = new ElementParameterFilter(FR);
			FilteredElementCollector FEC0 = new FilteredElementCollector(IntDoc);
			ElementMulticategoryFilter MCF = new ElementMulticategoryFilter(new List<BuiltInCategory>(){
			BuiltInCategory.OST_Views,
			BuiltInCategory.OST_Sheets,
			BuiltInCategory.OST_PipeFitting});


			IList<Element> Els = FEC0.WherePasses(MCF).WherePasses(EPF).WhereElementIsNotElementType().ToElements();

			for (int i = 0; i <= Els.Count - 1; i++)
			{
				Debug.WriteLine(Els[i].Name);
			}

			return Result.Succeeded;
		}