Pass parameter value from Application to Command

Pass parameter value from Application to Command

lrsmns
Enthusiast Enthusiast
756 Views
8 Replies
Message 1 of 9

Pass parameter value from Application to Command

lrsmns
Enthusiast
Enthusiast

Hi,

 

I was wondering if it is possible to set values within a button creation, and pass it onto an external command.
I have several buttons where each suppose to filter elements according to a specific parameter value.. however i dont want to create repetitive commands just to differentiate a single parameter value.

0 Likes
Accepted solutions (1)
757 Views
8 Replies
Replies (8)
Message 2 of 9

Moustafa_K
Advisor
Advisor

there could be several ways to resolve this: out of my head

  1. Create and Read a json file that has all the settings you need and you may update on each time you initiate a IExternalCommand
  2. store your settings i the Project settings
  3. have a static class, name it global or something, that you can access it and store the info you need
  4. instantiate a static object of IExternalApplication, and access the variables you need.

 

public class Application : IExternalApplication
{
    internal static Application Instance;
    protected Application()
    {
        Instance = this;
    }

... the rest of the class

 

possibly many more ways, but all depends how your data type is structured to select the best method it fits your needs

Moustafa Khalil
Cropped-Sharp-Bim-500x125-Autodesk-1
0 Likes
Message 3 of 9

lrsmns
Enthusiast
Enthusiast

Let's say i'm trying to create several buttons for different categories and it supposed to filter the values of the category:

var filterPullDown = PullDownButtonUtilities.AddPulldownButton(app, TabName, filterElementName, "Filter", "Filter by Category", "Filter.png");

List<string> Kategorien = new List<string>
{
    "heating", "lighting", "sanitary"
};

foreach (string kategorie in Kategorien)
{

   PullDownButtonUtilities.AddPushButton(filterPullDown, kategorie, "MyPlugIn.Revit.FilterByCategoryCmd");
}

 

Where this is my PullDownButton Utility:

 public static class PullDownButtonUtilities
 {
     public static PulldownButton AddPulldownButton(UIControlledApplication app, string tabName, string panelName, string pulldownButtonName, string pulldownButtonText, string iconPath = null)
     {
         RibbonPanel panel = app.CreateRibbonPanel(tabName, panelName);
         PulldownButtonData pulldownButtonData = new PulldownButtonData(pulldownButtonName, pulldownButtonText);
         PulldownButton pulldownButton = panel.AddItem(pulldownButtonData) as PulldownButton;

         if (!string.IsNullOrEmpty(iconPath))
         {
             pulldownButton.LargeImage = ImageUtility.LoadImage(Assembly.GetExecutingAssembly(), iconPath);
         }
         return pulldownButton;
     }

     public static void AddPushButton(PulldownButton pulldownButton, string buttonText, string className)
     {
         var assembly = Assembly.GetExecutingAssembly();
         var path = assembly.Location;
         var buttonData = new PushButtonData(buttonText, buttonText, path, className);
         pulldownButton.AddPushButton(buttonData);
     }
 }  

 

How would I pass the category values to my command class so that it can filter elements according to the values set by the buttons?

  public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
  {
      UIApplication uiApp = commandData.Application;
      UIDocument uiDoc = uiApp.ActiveUIDocument;
      Document doc = uiDoc.Document;

      try
      {
          // Get the active view
          View activeView = doc.ActiveView;

          // Collect all elements in the active view with the specified parameter value
          FilteredElementCollector collector = new FilteredElementCollector(doc, activeView.Id);
          IList<Element> elementsInView = collector.WhereElementIsNotElementType()
                                                    .Where(e => e.LookupParameter("Produktkategorie") != null
                                                             && e.LookupParameter("Produktkategorie").HasValue
                                                             && e.LookupParameter("Produktkategorie").AsString() == kategorie)
                                                    .ToList();
          if (elementsInView == null || !elementsInView.Any())
          {
              message = $"No elements found in the active view with the category: {kategorie}";
              TaskDialog.Show("No Elements Found", message);
              return Result.Failed;
          }

          ICollection<ElementId> elementIds = elementsInView.Select(e => e.Id).ToList();


          // Select elements
          uiDoc.Selection.SetElementIds(elementIds);

          return Result.Succeeded;
      }
      catch (Exception ex)
      {
          message = ex.Message;
          TaskDialog.Show("Error", message);
          return Result.Failed;
      }

  }

 

0 Likes
Message 4 of 9

Moustafa_K
Advisor
Advisor

oh ok now it is clearer. so you want to catch what is the button name that has executed. I recall i wrote an article about that over here

 

for reference you can first register ItemExecuted from the static ComponentManager class, Then from that class you can catch what command text see this example

 

Register this at the startup

 

 

public override Result OnStartup(UIControlledApplication application)
{
    Autodesk.Windows.ComponentManager.ItemExecuted += ComponentManager_ItemExecuted;
}

private void ComponentManager_ItemExecuted(
    object sender,
    Autodesk.Internal.Windows.RibbonItemExecutedEventArgs e
)
{
    // command is from an addin " not a built in Revit command"
    Globals.kategorie = e.Item.Text   // this is the name you used to register the button with
}

 

 

then in your command

 

 

IList<Element> elementsInView = collector.WhereElementIsNotElementType()
                                                    .Where(e => e.LookupParameter("Produktkategorie") != null
                                                             && e.LookupParameter("Produktkategorie").HasValue
                                                             && e.LookupParameter("Produktkategorie").AsString() == Globals.kategorie)
                                                    .ToList();

 

 

 

this is one of the possible solutions. of course you can also structure how to get or store this Globals static class.

Apologies this will trigger after the command is executed and not before.

Moustafa Khalil
Cropped-Sharp-Bim-500x125-Autodesk-1
0 Likes
Message 5 of 9

Moustafa_K
Advisor
Advisor
Accepted solution

I was mistaken above thought it differently. you can catch the name before the command is executed by registering to the ComponentManager.PreviewExecute

 

here is the implementation

 

 

ComponentManager.PreviewExecute += ComponentManager_PreviewExecute;

private void ComponentManager_PreviewExecute(object sender, EventArgs e)
{
    var ButtonCaller = sender as Autodesk.Windows.RibbonButton;
    //the exucted Category name is the button name
   Globals.kategorie =  command ButtonCaller.Text;
}

then form your code you can access this Global class[ a static class where you store static variable] through your command execution

IList<Element> elementsInView = collector.WhereElementIsNotElementType()
                                                    .Where(e => e.LookupParameter("Produktkategorie") != null
                                                             && e.LookupParameter("Produktkategorie").HasValue
                                                             && e.LookupParameter("Produktkategorie").AsString() == Globals.kategorie)
                                                    .ToList();

 

Moustafa Khalil
Cropped-Sharp-Bim-500x125-Autodesk-1
0 Likes
Message 6 of 9

lrsmns
Enthusiast
Enthusiast

I dont quiet follow with the first code in line 7.. what is Globals and why is it equal command Buttoncaller.Text; ?

0 Likes
Message 7 of 9

Moustafa_K
Advisor
Advisor

Globals can be a static class, where you can place and assign a values.

 

Pulic static class Globals{

public static string kategorie;

}

i couldn't formate the code style,seems not possible from phone.

 

What i did above is registering to a execution preview event, that to catch what button has executed the command. From that catch and store the value of the button.text {the value here is the pushbuttontext you registered in PulldownButtonData(pulldownButtonName, pulldownButtonText) method} in a global static class.

 

Eventually since this is triggered before the IExternalCommand execution, {this is not documented, so this needs to be tested thouroly on different revit versions}, you now hold the value of what is triggered and you can use it.

 

I hope i was able to clarify enough.

Moustafa Khalil
Cropped-Sharp-Bim-500x125-Autodesk-1
0 Likes
Message 8 of 9

lrsmns
Enthusiast
Enthusiast

Got it! i've got it working thank you very much!

Message 9 of 9

Moustafa_K
Advisor
Advisor

So glad you made it.... thanks for confirmation 👍 

I have added that part to my blog

Moustafa Khalil
Cropped-Sharp-Bim-500x125-Autodesk-1
0 Likes