Hi Henrique,
I am using another dll that creates buttons by reading information from my object called Node. Unfortunately, I don't have access to the buttons that are being created with the Revit pushbutton structure. I can only supply information to that tool that creates Pushbuttons or pulldowns or stacked items reading information from my node. This structure/template exists to make things easier while reading xaml recursively.
I am able to select parameters for the last step which is assigning commands, button names, or icons to my buttons. Therefore, I couldn't find any other method except AdWindows.dll to obtain the button name from the button that I clicked in my plugin on runtime.
Here is my code again. Sorry for the previous code-sharing attempt. In this partial code, I have buttoninfo class with click event and subscribe method to subscribe event and unsubscribe method to unsubscribe. I am using the Unsubscribe method inside of subscribe method, and I am using subscribe method inside of the command class' execute method. (which I believe explains why I am not able to see the command name when I clicked for the first time.)I cannot subscribe to my event to onstartup or shutdown because it is a plugin inside of another plugin as a part of the app store. The user downloads the plugin that he wants and the main plugin makes it visible for you if you would like to see it on the Revit ribbon. My project is dealing with the part uploading other plugins to our main plugin. So, this is why I am trying to subscribe to another class instead of onstartup. There is no startup for it. Here are my command and buttoninfo classes:
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Windows;
using ButtonCreation.Utils;
using System;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Xml.Linq;
namespace ButtonCreation
{
public class Command : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
ButtonInfo.SubscribeEvent(elements);
return Result.Succeeded;
}
}
}
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Windows;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Xml.Linq;
namespace ButtonCreation.Utils
{
public class ButtonInfo
{
public static void ClickedButtonName(object sender, UIElementActivatedEventArgs e, ElementSet elements)
{
string buttonText = e.Item.Text;
var filteredList = Station.ButtonData.Where(x => x.Item2 == buttonText).ToList();
if (filteredList.Count > 0)
{
Station.commandName = filteredList[0].Item1;
MessageBox.Show("Command name: " + Station.commandName);
string commandNamespace = @"bbTools.UI.Ribbon.Commands";
string fullCommandName = commandNamespace + "." + Station.commandName;
string assemblyLocation = Assembly.GetExecutingAssembly().Location;
string directoryName = System.IO.Path.GetDirectoryName(assemblyLocation);
string commandAssemblyLocation = directoryName + "\\Resources\\bbTools.dll";
Assembly externalAssembly = Assembly.LoadFrom(commandAssemblyLocation);
MessageBox.Show("Full Command Name from the Event: "+fullCommandName);
Type commandType = externalAssembly.GetType(fullCommandName);
if (commandType == null)
{
MessageBox.Show("Command name is not valid.");
return;
}
object command = Activator.CreateInstance(commandType);
var result = commandType.InvokeMember("ExecuteCommand",
BindingFlags.InvokeMethod,
null,
command,
new object[] { elements },
null);
}
else
{
MessageBox.Show(buttonText);
}
UnsubscribeEvent(elements);
}
public static void UnsubscribeEvent(ElementSet elements)
{
Autodesk.Windows.ComponentManager.UIElementActivated -= (s, e) => ButtonInfo.ClickedButtonName(s, e, elements);
Station.boolData = false;
}
public static void SubscribeEvent(ElementSet elements)
{
Autodesk.Windows.ComponentManager.UIElementActivated += (s, e) => ButtonInfo.ClickedButtonName(s, e, elements);
Station.boolData = true;
}
}
}
Here is how I create a pushbutton in another class and pass the command name data stored in the node object.
string displayName = node.Children.FirstOrDefault(item => item.Type == "DisplayName").InnerText;
string formattedDisplayName = displayName.Replace("\\n", "\r");
string commandName = "";
if(node.Children.FirstOrDefault(item => item.Type == "CommandName")!= null)
{
commandName = node.Children.FirstOrDefault(item =>
item.Type=="CommandName").InnerText;
Station.ButtonData.Add((commandName, formattedDisplayName));
}
// ...some gibberish and the last step
node.ndObject = (object)new ndPushButton(displayName, formattedDisplayName, Assembly.GetExecutingAssembly().Location, "ButtonCreation.Command", node.Icon, "ToolTip", "LongDescription");
Here is another station class that I use as data storage:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace ButtonCreation
{
public static class Station
{
public static List<(string, string)> ButtonData = new List<(string, string)>();
public static List<IconType> Images = new List<IconType>();
public static string commandName = "";
public static bool boolData = false;
}
}
With this current structure, I am able to pass the correct commandname to the clicked event method after clicking the button second time. I can print the correct command that button needs to execute but I believe it is creating a problem when I want to invoke a method from another dll. I am pretty sure of its parameters (only "elements") are correct as well. As extra information, the external command is using an interface called Cmd which is an abstract class with an IExternalCommand interface.
Hope this answer your questions.
@yildiz.emrullah wrote:
Hello,
I am working on an app that recreates an existing plugin to run it inside our plugin which works like a plugin store. In this app, I use an xaml file to read button data and create buttons with corresponding parameters such as DisplayName. I use a node object to store button information and create buttons as I read these objects.
In the end, I am trying to make one command that needs to find the clicked button name and filter the matching command name from the list where I archived the button name and command name. Then, it needs to find the command name from the external .dll and invoke its method. But I am getting a hard time setting the architecture since Revit is crushing saying " Unrecoverable error has occurred.." I am suspecting from that the way I am unsubscribing from the click event is not healthy. My code also is not showing anything when I click for the first time.
Do you have any suggestions or alternative ways to obtain the button name as soon as I clicked?
Here are some of my classes(ButtonInfo is my event class, Commands is the class which I assign for my buttons that I created by reading xaml.
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using ButtonCreation.Utils;
using System;
using System.Windows;
namespace ButtonCreation
{
[Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
public class Commands : IExternalCommand
{
private bool _subscribedToEvent = true;
private ExternalCommandData _commandData;
public ElementSet _elements;
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
_commandData = commandData;
_elements = elemenzts;
if (_subscribedToEvent)
{
Autodesk.Windows.ComponentManager.UIElementActivated += (s, e) => ButtonInfo.ClickedButtonName(s, e, _elements);
_subscribedToEvent = false;
}
else
{
MessageBox.Show("not subscribed");
}
return Result.Succeeded;
Autodesk.Windows.ComponentManager.UIElementActivated -= (s, e) => ButtonInfo.ClickedButtonName(s, e, _elements);
_subscribedToEvent = true;
}
}
}
using ArcadisRevitConnector.ArcadisRibbonItems.ArcadisButtons;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using System;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Xml.Linq;
namespace ButtonCreation.Utils
{
public static class ButtonInfo
{
public static void ClickedButtonName(object sender, Autodesk.Windows.UIElementActivatedEventArgs e, ElementSet elements)
{
string buttonText = e.Item.Text;
MessageBox.Show("Button Name: " + buttonText);
var filteredList = Station.ButtonData.Where(x => x.Item2 == buttonText).ToList();
if (!filteredList.Any())
{
return;
}
string externalCommandName = filteredList[0].Item1;
MessageBox.Show("Command Name: " + externalCommandName);
string commandNamespace = @"bbTools.UI.Ribbon.Commands";
string fullCommandName = commandNamespace + "." + externalCommandName;
string assemblyLocation = Assembly.GetExecutingAssembly().Location;
string directoryName = System.IO.Path.GetDirectoryName(assemblyLocation);
string commandAssemblyLocation = directoryName + "\\Resources\\bbTools.dll";
Assembly externalAssembly = Assembly.LoadFrom(commandAssemblyLocation);
Type commandType = externalAssembly.GetType(fullCommandName);
if (commandType == null)
{
MessageBox.Show("Command Name is not valid");
return;
}
object command = Activator.CreateInstance(commandType);
try
{
object result = commandType.InvokeMember("ExecuteCommand",
BindingFlags.InvokeMethod,
null,
command,
new object[] { elements },
null);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
string displayName = node.Children.FirstOrDefault(item => item.Type == "DisplayName").InnerText;
string formattedDisplayName = displayName.Replace("\\n", "\r");
string commandName = "";
if(node.Children.FirstOrDefault(item => item.Type == "CommandName")!= null)
{
commandName = node.Children.FirstOrDefault(item => item.Type == "CommandName").InnerText;
Station.ButtonData.Add((commandName, formattedDisplayName));
}
// Determine the size of button
bool isLargeButton = type.Value.EndsWith("LargeButton");
// Set the item type and get the corresponding icon name
node.ItemType = isLargeButton ? "CustomLargeButton" : "CustomSmallButton";
var iconName = node.Children.FirstOrDefault(item => item.Type == (isLargeButton ? "LargeIcon" : "SmallIcon")).InnerText;
//Find the corresponding icon
foreach (IconType i in Station.Images)
{
if (i.Name == iconName)
{
node.Icon = TransformImage.BitmapImageToBitmap(isLargeButton ? i.LargeBitmapImage : i.SmallBitmapImage);
break;
}
}
node.ArcadisRO = (object)new ArcadisPushButton(displayName, formattedDisplayName, Assembly.GetExecutingAssembly().Location, "ButtonCreation.Command", node.Icon, "ToolTip", "LongDescription");
@yildiz.emrullah wrote:
Hello,
I am working on an app that recreates an existing plugin to run it inside our plugin which works like a plugin store. In this app, I use an xaml file to read button data and create buttons with corresponding parameters such as DisplayName. I use a node object to store button information and create buttons as I read these objects.
In the end, I am trying to make one command that needs to find the clicked button name and filter the matching command name from the list where I archived the button name and command name. Then, it needs to find the command name from the external .dll and invoke its method. But I am getting a hard time setting the architecture since Revit is crushing saying " Unrecoverable error has occurred.." I am suspecting from that the way I am unsubscribing from the click event is not healthy. My code also is not showing anything when I click for the first time.
Do you have any suggestions or alternative ways to obtain the button name as soon as I clicked?
Here are some of my classes(ButtonInfo is my event class, Commands is the class which I assign for my buttons that I created by reading xaml.
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using ButtonCreation.Utils;
using System;
using System.Windows;
namespace ButtonCreation
{
[Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
public class Commands : IExternalCommand
{
private bool _subscribedToEvent = true;
private ExternalCommandData _commandData;
public ElementSet _elements;
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
_commandData = commandData;
_elements = elements;
if (_subscribedToEvent)
{
Autodesk.Windows.ComponentManager.UIElementActivated += (s, e) => ButtonInfo.ClickedButtonName(s, e, _elements);
_subscribedToEvent = false;
}
else
{
MessageBox.Show("not subscribed");
}
return Result.Succeeded;
Autodesk.Windows.ComponentManager.UIElementActivated -= (s, e) => ButtonInfo.ClickedButtonName(s, e, _elements);
_subscribedToEvent = true;
}
}
}
using ArcadisRevitConnector.ArcadisRibbonItems.ArcadisButtons;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using System;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Xml.Linq;
namespace ButtonCreation.Utils
{
public static class ButtonInfo
{
public static void ClickedButtonName(object sender, Autodesk.Windows.UIElementActivatedEventArgs e, ElementSet elements)
{
string buttonText = e.Item.Text;
MessageBox.Show("Button Name: " + buttonText);
var filteredList = Station.ButtonData.Where(x => x.Item2 == buttonText).ToList();
if (!filteredList.Any())
{
return;
}
string externalCommandName = filteredList[0].Item1;
MessageBox.Show("Command Name: " + externalCommandName);
string commandNamespace = @"bbTools.UI.Ribbon.Commands";
string fullCommandName = commandNamespace + "." + externalCommandName;
string assemblyLocation = Assembly.GetExecutingAssembly().Location;
string directoryName = System.IO.Path.GetDirectoryName(assemblyLocation);
string commandAssemblyLocation = directoryName + "\\Resources\\bbTools.dll";
Assembly externalAssembly = Assembly.LoadFrom(commandAssemblyLocation);
Type commandType = externalAssembly.GetType(fullCommandName);
if (commandType == null)
{
MessageBox.Show("Command Name is not valid");
return;
}
object command = Activator.CreateInstance(commandType);
try
{
object result = commandType.InvokeMember("ExecuteCommand",
BindingFlags.InvokeMethod,
null,
command,
new object[] { elements },
null);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
@yildiz.emrullah wrote:
Hello,
I am working on an app that recreates an existing plugin to run it inside our plugin which works like a plugin store. In this app, I use an xaml file to read button data and create buttons with corresponding parameters such as DisplayName. I use a node object to store button information and create buttons as I read these objects.
In the end, I am trying to make one command that needs to find the clicked button name and filter the matching command name from the list where I archived the button name and command name. Then, it needs to find the command name from the external .dll and invoke its method. But I am getting a hard time setting the architecture since Revit is crushing saying " Unrecoverable error has occurred.." I am suspecting from that the way I am unsubscribing from the click event is not healthy. My code also is not showing anything when I click for the first time.
Do you have any suggestions or alternative ways to obtain the button name as soon as I clicked?
Here are some of my classes(ButtonInfo is my event class, Commands is the class which I assign for my buttons that I created by reading xaml.
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using ButtonCreation.Utils;
using System;
using System.Windows;
namespace ButtonCreation
{
[Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
public class Commands : IExternalCommand
{
private bool _subscribedToEvent = true;
private ExternalCommandData _commandData;
public ElementSet _elements;
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
_commandData = commandData;
_elements = elements;
if (_subscribedToEvent)
{
Autodesk.Windows.ComponentManager.UIElementActivated += (s, e) => ButtonInfo.ClickedButtonName(s, e, _elements);
_subscribedToEvent = false;
}
else
{
MessageBox.Show("not subscribed");
}
return Result.Succeeded;
Autodesk.Windows.ComponentManager.UIElementActivated -= (s, e) => ButtonInfo.ClickedButtonName(s, e, _elements);
_subscribedToEvent = true;
}
}
}
using ArcadisRevitConnector.ArcadisRibbonItems.ArcadisButtons;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using System;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Xml.Linq;
namespace ButtonCreation.Utils
{
public static class ButtonInfo
{
public static void ClickedButtonName(object sender, Autodesk.Windows.UIElementActivatedEventArgs e, ElementSet elements)
{
string buttonText = e.Item.Text;
MessageBox.Show("Button Name: " + buttonText);
var filteredList = Station.ButtonData.Where(x => x.Item2 == buttonText).ToList();
if (!filteredList.Any())
{
return;
}
string externalCommandName = filteredList[0].Item1;
MessageBox.Show("Command Name: " + externalCommandName);
string commandNamespace = @"bbTools.UI.Ribbon.Commands";
string fullCommandName = commandNamespace + "." + externalCommandName;
string assemblyLocation = Assembly.GetExecutingAssembly().Location;
string directoryName = System.IO.Path.GetDirectoryName(assemblyLocation);
string commandAssemblyLocation = directoryName + "\\Resources\\bbTools.dll";
Assembly externalAssembly = Assembly.LoadFrom(commandAssemblyLocation);
Type commandType = externalAssembly.GetType(fullCommandName);
if (commandType == null)
{
MessageBox.Show("Command Name is not valid");
return;
}
object command = Activator.CreateInstance(commandType);
try
{
object result = commandType.InvokeMember("ExecuteCommand",
BindingFlags.InvokeMethod,
null,
command,
new object[] { elements },
null);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}