**The Final Product
Rendered (sans propert icons and logical hierarchy):

Input XML:
<Toolbar>
<ToolbarOption display='File'>
<Options>
<Option icon='icon_settings' display='New' args='string1,string2,string3' desc='[user.usertheme](syntaxresolution allowed),arg2 is this, arg3 is that' func='MyTestFunction'>
<Options>
<Option display='Document' func='CreateNewDocument' args='txt' desc='filetype extension' >
<Options>
<Option display='Text' func='CreateNewDocument' args='txt' desc='filetype extension' />
<Option display='PDF' func='CreateNewDocument' args='txt' desc='filetype extension' />
<Option display='RDL' func='CreateNewDocument' args='txt' desc='filetype extension' />
<Option display='Excel' func='CreateNewDocument' args='txt' desc='filetype extension' />
</Options>
<Option display='Text' func='CreateNewDocument' args='txt' desc='filetype extension' />
</Option>
</Options>
</Option>
<Option display='Exit' args='' desc='' func='Exit' />
</Options>
</ToolbarOption>
<ToolbarOption display='Edit'>
<Options>
<Option icon='icon_settings' display='Undo' args='string1,string2,string3' desc='[user.usertheme](syntaxresolution allowed),arg2 is this, arg3 is that' func='MyTestFunction' >
<Options>
<Option display='Bookmarks' args='' desc='' func='Exit' />
<Option display='Bookmarks' args='' desc='' func='Exit' />
</Options>
</Option>
<Option icon='icon_settings' display='Redo' args='string1,string2,string3' desc='[user.usertheme](syntaxresolution allowed),arg2 is this, arg3 is that' func='MyTestFunction' />
<Option icon='icon_settings' display='Find and Replace' args='string1,string2,string3' desc='[user.usertheme](syntaxresolution allowed),arg2 is this, arg3 is that' func='MyTestFunction'>
<Options>
<Option display='Quick Find' func='CreateNewDocument' args='txt' desc='filetype extension' />
<Option display='Settings' func='CreateNewDocument' args='txt' desc='filetype extension' >
<Options>
<Option display='Func1' func='CreateNewDocument' args='txt' desc='filetype extension' />
<Option display='Func2' func='CreateNewDocument' args='txt' desc='filetype extension' />
</Options>
</Option>
</Options>
</Option>
</Options>
</ToolbarOption>
</Toolbar>";
Summary:
Pretty Cool Toolbar control that lets you supply XML in contruction of option hierarchies or programmaticly build in the backend. Option clicks are interpreted in either of two ways, programmatic ToolbarOptions can subscribe to the individual events and run custom code or if ToolbarOption.Func attributes are non-null/empty, attempt to invoke a matching function on the ToolBar.Parent object. This method must contain a matching digital signature for any supplied Toolbar.Args. A String[] Array is generated from the supplied comma-delimited xml attribute value and currently only supports arguments of type String. ParentRef is set either by adding the Toolbar control to a visual tree as a child element, or by setting the property manually. The control is not fully componentized as it still highly relies on style resources and types inherit to another app I've been working on, but could be migrated to a stand alone control pretty easily. Either way, I think it's pretty cool ;)
/// <summary>
/// Uses System.Reflection to attempt to invoke method name supplied on parent ToolBar.ParentRef object.
/// Fired when option is clicked and ToolbarOption.Function is non empty/null.
/// Passes ToolbarOption.Args to Member Invoked - method arguments must match supplied arguments and must all be typeof(string)
/// </summary>
/// <param name="e"></param>
private void InvokeOptionMethod(OptionEventArgs e)
{
if (e != null
&& !string.IsNullOrEmpty(e.Function)
&& this.ParentRef != null)
{
try
{
ParentRef.GetType().InvokeMember(e.Function, BindingFlags.IgnoreCase | BindingFlags.InvokeMethod, null, ParentRef, e.Args);
}
catch (Exception ex)
{
OnToolbarException(ex);
}
}
}
** End of Update
Toolbar => Collection of ToolbarHeader
=> Collection of ContextPopup
XFunction/Actions => (XML) namespace reflection invocation (cpu?)
=> (object) XAction : IXAction (string[] args, void Invoke(string[] args))
or => XAction collection ? App.Current.Resources/Global...
=> switch(key) { case: logic; }
VisualDefinition - (XML) <id(guid)><display(string)>
MethodDefinition - Collection<KeyValuePair(guid, XAction)>
ContextPopup - UIElement Renders Menu/highlighting/calculates position of children menus
E.g.
<Toolbar>
<ToolbarOption>
<header>File</header>
<Options>
<Option>
<id>9365f54e-e9fd-4679-9579-91bc92d971c0</id>
<display>New</display>
<args>string1,string2,string3</args>
<desc>[user.usertheme](syntaxresolution allowed),arg2 is this, arg3 is that</desc>
<Options>
<Option>
<id>9365f54e-e9fd-4679-9579-91bc92d971c2</id>
<display>Text Document</display>
<args>.txt</args>
<desc>filetype</desc>
</Option>
</Options>
</Option>
<Option>
<id>9365f54e-e9fd-4679-9579-91bc92d971c3</id>
<display>Exit</display>
<args />
<desc />
</Option>
</Options>
</ToolbarOption>
<ToolbarOption>
<header>Edit</header>
<Options />
</ToolbarOption>
</Toolbar>
public class XActions
{
public XActions(string[] args, Guid key)
{
try
{
InvokeMethod(args, key);
}
catch(Exception ex)
{
new ErrorDialog("The Squirrels have taken over..").show();
}
}
public void InvokeMethod(Guid key, string[] args)
{
switch(key)
{
case ...
...logic
break;
case ...
//desc node tells us what each indexed argument represents
//should add some sort of validation, expected types/arg.length vs actual
new ObjectHelper().LoadMyObject(Convert.ToInt(args[0]), args[1]);
break;
}
}
}
1. Option Click
2. Get Option id
3. Lookup id in MethodDefinition in global/static/instanced definition class
4. If exists, construct string[] from <args> node and passvalues to MethodDefinition instance
5. Logic happens... the squirrels attack!