Dynamically creating Ribbon control with PRISM

Topics: Prism v4 - WPF 4
Aug 10, 2012 at 8:27 PM
Edited Aug 10, 2012 at 9:18 PM

My requirement is that each module can define its own RibbonTab, or can also define just a RibbonGroup to be added to an existing tab or just RibbonButtons to be added in existing tab>group.

What I did so far is each module will define its own RibbonTab in xaml file, so the tab is added if a tab with same header doesn't exist but if tab already exists its group would be detached and added to the existing tab. Similarly if group exists it child controls are detached and added to the group that already exists.

But the problem I am getting with this approach is all the binding of buttons to their ICommands also breaks down.

My question is how can I best achieve my requirement. Otherwise if the approach I am using is fine, how can I keep binding intact.

I am using WPF, PRISM 4.1, MEF without MVVM pattern currently since I am porting some existing code. But willing to adopt MVVM eventually.

Thanks.

Aug 13, 2012 at 3:06 PM

Im still looking for solution. Will appreciate any help. Thanks.

Aug 13, 2012 at 4:41 PM
Edited Aug 13, 2012 at 4:42 PM

You're not alone (updated: ...looking for .../) waiting for an elegant solution.

Developer
Aug 13, 2012 at 5:52 PM

Hi,

In my opinion, I believe the approach you described seems to be valid to achieve the requirements of your scenario.

As far as I know, there could be several causes behind why your bindings are breaking. However, with the information you provided so far, I don't understand against "what" your buttons are binding to and why those binding are breaking.

Therefore, it would be helpful if you could provide us with more information about how the binding between the buttons and commands are done and where those commands are defined.

Regards,

Damian Cherubini
http://blogs.southworks.net/dcherubini

Aug 14, 2012 at 1:39 PM
Edited Aug 14, 2012 at 2:33 PM

I was binding buttons to ICommands which are defined in code behind of xaml. (I was setting datacontext in xaml like this: DataContext="{Binding RelativeSource={RelativeSource Self}}")

This binding breaks for the buttons that are moved to another tab in Ribbon bar. (As I mentioned I am not using MVVM pattern right now but would do that as I progress). Since each Module will have its own calss for defining its RibbonTab (in case if it already exists with same header, its contents would be detached from it and added to the tab already existing), to solve this I was thinking to have ModelView class for each module's RibbonTab and when Ribbon elements of modules are merged into Ribbon bar (which would have its own ModelView class) all modelView classes of modules should also merge into main Ribbon bar's modelview class. This is just an idea without having much knowledge of bringing it to life.

Will appreciate any comments and help.

Thanks,

Imad.

Developer
Aug 14, 2012 at 5:53 PM

Hi,

If I understood correctly, each module defines its own RibbonTab with an architecture similar to this:

  • RibbonTab1 - DataContext="{Binding RelativeSource={RelativeSource Self}}". The RibbonTab defines the ICommands in code-behind.
    • RibbonButton1 - Command="{Binding Command1}". Binds to the Command1 defined in RibbonTab1's code-behind.
    • RibbonButton2 - Command="{Binding Command1}". Binds to the Command2 defined in RibbonTab1's code-behind.

Based on my understanding, the problem with this architecture is that the buttons don't know the DataContext, the binding just escalates the visual tree searching for a DataContext (in this case, the binding would find the DataContext of the RibbonTab1). Therefore, when the buttons are detached and attached to a different tab, the binding escalate and use the DataContext of that tab. As this tab doesn't have commands defined for those buttons, the bindings don't work.

A possible approach to solve this could be to define the DataContext of each RibbonButton, so that when they are attached or detached they will take their DataContext with them. There are several approaches to do this; for example, in the constructor of the RibbonTab in code-behind you could manually set the DataContext of each button to a "kind of" view model, where the commands are defined.

Also, you can directly set the command corresponding to each button as the DataContext in code-behind. An example of this scenario would be something like this:

  • RibbonTab1 . The ICommands are defined in code-behind or in a view model.
    • RibbonButton1 - Command="{Binding}". Binds to the Command1 defined as its DataContext in the RibbonTab1's code-behind.
    • RibbonButton2 - Command="{Binding}". Binds to the Command2 defined as its DataContext in the RibbonTab1's code-behind.
public RibbonTab1 () {
    this.InitializeComponents();
    this.RibbonButton1.DataContext = this.Command1;
    this.RibbonButton2.DataContext = this.Command2;
}

Then, when the buttons are moved to another place, they should be able to execute the commands correctly.

I hope you find this useful,

Damian Cherubini
http://blogs.southworks.net/dcherubini

Aug 14, 2012 at 7:53 PM

Hi Damian, 

Thanks for your reply. It worked and its an excellent idea. 

Regards,

Imad.