8
votes

Our application is a large project with many modules and view. The main window has a ribbon in it, and we are looking for the best way to integrate the ribbon in the application.

I've created a service which modules a views can register to add ribbon items relevant for them, and, in addition, any main view instance can provide its own ribbon items relevant to the that instance. a RibbonItem is a small class which abstract the options of a ribbon item, and mainly have Title, Description, Command, UIType and ChildItems. The service is in charge to rebuild the ribbon when the main view changes.

A colleague of mine thinks this is bad MVVM, as users need to design their ribbon view in C# code and not in XAML, and he also say it would be hard in this way to make a group of items disabled or enabled at once, as each command of these items will need to update its CanExecute separately. Instead, he suggested to have a main Ribbon View and ViewModel files, where each developer that want to add a ribbon button for her module or view would need to add them in the View XAML and add a relevant command in the ViewModel. In addition, VisualStates will be used to determine what items will be displayed or enabled based on changes in the ViewModel (such as view change, or selection change). I really don't like this solution, mainly because all developers will have to put their modules knowledge in once big file.

Note that some items in the ribbon (e.g. Options, Exit) are common to the entire application, while some are relevant to a specific application domain and some are only relevant for a specific view.

Edit: I guess my main question is what is the recommended way to allow multiple development teams integrate on a single ribbon? Should we have a single RibbonView and single RibbonViewModel which will contain all of the possible items in a ribbon, and each team will add its items to these V/VM and also define the logic on when to show them (probably by using visual state)? Or do we allow every view, view-model or module register ribbon items (within their own C# code) against a service, and have the service then render the ribbon as needed when the active view changes with all items registered to that type? Or is there any better way to achieve this integration?

What do you think? Do you have a better idea or an opinion about how to manage the single ribbon resource which is common to multiple developers?

Thanks, splintor

1
Well, ViewModels shouldn't "know" anything about the ribbon. They shouldn't be controlling what is and is not displayed in the ribbon. The View should be responding to state changes of the ViewModel to determine what and what not to show.user1228
What does it mean "to rebuild the ribbon"? If you have different ribbons for different views, it isn't good practice, because the ribbon must be only one. Your colleague is right. And I would permit only one developer to change the ribbon.vortexwolf
This is a tabbed application, with many tab types - each type has its own ribbon commands. Instead of having all items in the ribbon, I thought rebuilding the ribbon content with the relevant items when tab changes will be easier. Unlike what you say, my colleague says that everyone edit their part in the main ribbon files, as every developer knows how she wants her ribbon items to look. I really don't like the idea of having two big files that centrelize the knowledge about the entire application and its screens - it's a large application and we need to decompose it as much as possible.splintor
@splintor Actually there are two types of applications which the ribbon can be applied in: 1) single-view apps with the tabbed ribbon (MS Office, Autocad, Paint, and Explorer in the future); 2) multi-view apps with the simple ribbon which doesn't have tabs at all (MS Dynamics). Application of the first time must have the single ribbon for an entire application and it is almost impossible to divide them on modules. All that is known about your application is that it is "tabbed with many types which have their own commands".vortexwolf
@splintor So I want to say that your question is too abstract and I don't know what to advise. Which application is the most similar to your application? MS Access, MS Dynamics? The only one correct answer in this situation is "it depends". Useless, but above reproach. Also, just interesting question: you work in women's team?vortexwolf

1 Answers

1
votes

I agree with Will's comment, your viewmodel should not care or know how it is being rendered or if the designers ever decide to change how it's rendered.

A ViewModel should only contain all required information for the presentation layer to render it.

So the ViewModel should have all the properties that the Ribbon bar needs bind to in order to function. Then you can use a Resources.xaml or some other strategy to present it.

Taking a shot in the dark I would try something like this for the ViewModels:

public interface IMenuViewModel : INotifyPropertyChanged
{
  ICommand Command {get;}
  string Title {get;}
  string Description {get;}
  UIType Type {get;}
  IList<IMenuViewModel> ChildItems {get;}
}

I would then probably create an abstract class that provides implements INotifyPropertyChanged with a collection class the implements INotifyCollectionChanged to take care of the plumbing code.

I would then probably do something like this in the Resources.xaml

<DataTemplate DataType="{x:Type vm:IMenuViewModel}">
  <StackPanel>
    <Button Command="{Binding Command}" Content="{Binding Type}"/>
    <ItemsControl ItemsSource="{Binding ChildItems}"/>
  </StackPanel>
</DataTemplate>

to provide a default view for your viewmodels

and then all someone has to do to create an entry into your ribbon bar is

1) Implement IMenuViewModel

2) Optionally add another DataTemplate entry into your resources.xaml if they want their widget rendered differently like so:

<DataTemplate DataType="{x:Type vm:FooViewModel}">
    <v:FooView />
</DataTemplate>

I hope I didn't dig to deep on how I would implement.

The main point is that a ViewModel should only expose properties required for the view to do it's job(which is rendering the ViewModel), not for the ViewModel to do the job or care how it's done.