Hi
Here is the case. When I try to add to QAT item that is of MenuTool type, application crash (exception is caused by trying to set in cloned control ItemsSource property to the ItemsSource collection of the original MenuTool, but cloned MenuTool already contains some items). From what I've investigated, this occurs only when MenuTool is declared as a separate control with some ButtonTools inside, and put as an single control to the ApplicationMenu (see SeparatedMenu class in provided sample). Case doesn't happen when this kind of menu is declared as a whole in the ApplicationMenu.
Declaring menu items as separate controls is important case for me, because ApplicationMenu is dynamically composed in runtime.
So, what are my options for being able to add such items to the QAT?
I've attached simple application that reproduces this issue.
Unfortunately this really can't work the way you have it. I'll try to explain the process of cloning for the QAT and that might help to understand the limitations/implications. When you choose to add an element to the qat, we have to clone an instance of the tool. During the cloning of that tool we create a new instance of the source type and then copy over the properties of that type. In the case of some properties we actually bind the property on the clone to the property of the source so that the cloned tools stay in sync. Tools can get involved in the cloning process via the RibbonToolProxy. The menu tool needs to show the same items that are in the source tool since you could manipulate the source collection, make changes to the tools in that source tool, etc. The way it acheives this is by binding the ItemsSource of the clone to the Items of the source since the items for a menu will only ever be shown for 1 tool at a time.
Well in your case when the new instance of the source type (SeparatedMenu) is created, the ctor is invoked and the way you have your tool defined means that the InitializeComponent will be executed and that cloned tool will have a new set of items in it based on the associated xaml file you have. Those tools would have no association with the tools that were in the original menu so if you changed the state of one it would not affect the tools in the cloned menu's items collection and vice versa. Worst yet is that since these are different instances they would be registered with the ribbon as new tools and if there is no id set on the tools they could be cloned again and if there is an id set, it would result in an exception because you can only have 1 instance of a tool with a given id.
If you really want to go down this route then the only thing I could think is that you could provide a custom ribbontoolproxy that will clear the items of the cloned tool before allowing the binding of the itemssource to occur. I've attached a sample that provides 2 different options. One is to just override the Clone method and return a new MenuTool. The default binding support would bind the normal tool properties (i.e. large|smallimage, caption, isqatcommontool,visibility, isenabled, datacontext, contextmenu, tooltip, ischecked, buttontype, command, commandtarget, commandparameter). The other option is to override the Bind method and clear the items before calling the base so the itemssource can be bound to the source tool's items.
It should be noted though that the second approach has some dangers. If you set up your xaml such that you set an x:Name on one of those items then that element will continue to exist as the framework will store that as a member variable of the cloned instance. Also if you had tried to do element bindings based on element name then those may not work.
Option one seems to be more appropriate, and I think it will work in my case.
Thanks for this solution.
There is one detail with the proposed solution (option one). I can add to QAT my MenuTool multiple times. Something else should be implemented on the control or ToolProxy to prevent this?
It looks like option 1 won't work then. I forgot to copy the Id of the tool from the source tool to the new one (i.e. "return new MenuTool { Id = sourceTool.Id };"). However when you do so an exception will generate when it's added to the QAT because we verify that the type of the source and target tools are the same. So in order for option 1 to work, you would have to create the derived tool anyway.