Using Microsoft (or other) ribbon with large project and MVVM - wpf

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

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.

Related

WPF multiple views [duplicate]

I am working on a an WPF MVVM application where I need to have a Main Window with just a logo and it has to show child views inside it. I don't have any controls in Main Window all the controls reside in child view for example Buttons like Next, Back, Cancel and some text blocks etc. Now If users select Next button on the child view I have to draw or load the next child view inside the Main Window. If Back button is clicked I have to go back to the previous child view. So basically I am changing the child views depending on which button is clicked. Also I am maintaining different view models for every child view. Now the problem is I am not able to figure how should I link the child views to there respective view models. This application is similar to some Installation applications where different dialogs are shown depending on the selection and the button clicked by the user.I am new to this wpf and don't want to use MVVM Light , Prism etc. Any detailed help will be greatly appreciated. Thanks in advance.
One of the easiest ways to associate any data type with XAML controls is to use a DataTemplate. Therefore, you can simply add something like this into your Application.Resources and as long as you do not set the x:Key properties on the DataTemplates, then they will be explicitly applied by the Framework whenever it comes across instances of your view models:
<DataTemplate DataType="{x:Type ViewModels:HomeViewModel}">
<Views:HomeView />
</DataTemplate>
...
<DataTemplate DataType="{x:Type ViewModels:MainViewModel}">
<Views:MainView />
</DataTemplate>
Then displaying the view is as simple as this:
<ContentControl Content="{Binding YourViewModelProperty"} />
In code behind, or your view model:
YourViewModelProperty = new MainViewModel();
It's often handy to create a base class for your view models and then the YourViewModelProperty can of that type and you will be able to interchange them using the same property and ContentControl.
UPDATE >>>
The general idea is that you have one MainViewModel class with one BaseViewModel property data bound to one ContentControl in MainWindow.xaml... the navigation controls should also be in MainWindow.xaml and not in the views themselves. In this way, the MainViewModel class is responsible for changing the property to the relevant view model instances when it receives navigation Commands from the MainWindow.xaml.

Reusing User Controls

I'm trying to alter an existing WPF application and my lack of WPF experience (I'm WINFORMS) is making this difficult.
I've come across a situation where I need to reuse a UserControl and I'm not sure how to do this in terms of modifying the xaml. I'll explain.
UserControlA has the following code:
<Grid Name="gdMain" Style="{StaticResource ContentRoot}">
<content:MonitorAlarmsPage />
</Grid>
Now, "MonitorAlarmsPage" is an AXML document that defines another UserControl - UserControlB. This UserControl, once created has to persist for the lifetime of the application.
So, I could have many UserControlAs, but only ONE UserControlB.
I've created a static class that has an appropriate UserControlB field which is updated when UserControlB is created, but how do I modify the content:MonitorAlarmsPage so that the content of the grid is replaced by the existing UserControlB as referenced in this static class and not by the XAML file that defines UserControlB? Can this actually be done? i.e. essentially, insert pre created user controls inside an XAML page.
To make things a bit clearer, UserControlB is essentially a page that can sit inside another page. The page is complex and there is a massive overhead incurred when it is created and so must only be created once.
WPF is very different to WinForms, so you certainly have your work cut out for you. The basic idea for the solution to your problem is this. You'll need to add a DependencyProperty to the MonitorAlarmsPage control. This will enable you to data bind to this property from outside the control.. This property should be of a type of custom class that you define, that contains all of the properties required in the inner control.
The next stage is to develop a DataTemplate that defines how WPF should display your custom class when it comes across an instance of it. In this DataTemplate, you declare your inner control, so that when WPF sees the custom class, it displays the inner control and sets the custom class as the DataContext for the inner control:
<DataTemplate DataType="{x:Type YourXmlNamespacePrefix:YourCustomClass}">
<YourControlsXmlNamespacePrefix:YourInnerControl DataContext="{Binding}" />
</DataTemplate>
Finally, you'll need to data bind your custom class (the static class) to the outer UserControl:
<content:MonitorAlarmsPage YourCustomClassProperty="{Binding YourStaticClass}" />
So just to be clear... this static class should be a data class, not a UI class. When using WPF, we manipulate data, not UI elements. We let the wonderful templating system generate the UI for us.

How one approaches designing a view with MVVM best practices in mind

This question is about how to factor the app design with MVVM in mind. I understand the general concepts of MVVM (ViewModel is not aware of the View, Models represent domain objects, etc...). I'm also aware of Commands and how some controls can invoke commands on the ViewModel.
What I can't figure out is how to factor a real-life app to fit this model (as opposed to simple textbox/button/query database examples). Here are some questions:
In a main view I have preview section that displays contextual data depending on what the user is doing (i.e. by interacting with certain controls, show some preview section). Should the View contain all the possible previews in XAML and then show/hide/update? Should ViewModel have properties like "public bool ShowPreviewA" and "public bool ShowPreviewB" set by ViewModel's internal state machine?
Where should the logic live when some controls have intricate interactions between them. For example, 3 checkboxes where at least one must be selected, by preventing unchecking. Seems to me that it would pollute the ViewModel on one hand, and also feel like there is certain "knowledge" about the View on the other hand.
This question is difficult to articulate properly (if I could, I would probably understand MVVM much better).
Any tips are welcomed.
EDIT:
The real question is how to break down the problem when writing the functionality of ViewModels. Is it a combination of top-down - i.e. encode every possible View state in ViewModel properties - and bottom-up - i.e. each logically related set of controls receive their properties from child ViewModels that report "up" some logical state (e.g. valid input)?
In a main view I have preview section that displays contextual data
depending on what the user is doing (i.e. by interacting with certain
controls, show some preview section). Should the View contain all the
possible previews in XAML and then show/hide/update? Should ViewModel
have properties like "public bool ShowPreviewA" and "public bool
ShowPreviewB" set by ViewModel's internal state machine?
No, of course not, if these "previews" are completely different UIs with completely different data, then use DataTemplates.
For example:
Given some classes:
public class Person: BusinessEntity //BusinessEntity is just a fictional base class for Model classes
{
public string LastName {get;set;}
}
public class Product: BusinessEntity
{
public string ProductName {get;set;
}
suppose your ViewModel is defined like this:
public class SomeViewModel: ViewModelBase //Same comment as above
{
public BusinessEntity SelectedEntity {get;set;} //NotifyPropertyChanged() etc
}
Your XAML can be defined like this:
<Window ...>
<Window.Resources>
<!-- DataTemplate for Person class -->
<DataTemplate DataType="Person">
<TextBox Text="{Binding LastName}"/>
</DataTemplate>
<!-- DataTemplate for Product class -->
<DataTemplate DataType="Product">
<TextBox Text="{Binding ProductName}"/>
</DataTemplate>
</Window.Resources>
<ContentPresenter Content="{Binding SelectedEntity}"/>
</Window>
WPF will take care of rendering the appropiate DataTemplate inside the ContentPresenter depending on which object type is put in the SelectedEntity property in the ViewModel.
Where should the logic live when some controls have intricate
interactions between them. For example, 3 checkboxes where at least
one must be selected, by preventing unchecking. Seems to me that it
would pollute the ViewModel on one hand, and also feel like there is
certain "knowledge" about the View on the other hand.
You can easily inherit ObservableCollection<T> to create this logic in a reusable manner. Then in your ViewModel just put some public SelectableCollection<T> MyItems {get;set;} where the selection / mutual exclusion, etc is handled by SelectableCollection and so on.
Bottom line: MVVM is all about reusability and encapsulation of functionality.

MVVM Light Multiple Data Contexts

I am new to MVMM Light toolkit (note, I'm using Light on .NET 3.5). So far I am really starting to like the pattern save for two challenges.
I'd like to have reusable XAML templates for certain types of pages in my application. Think of a movie ticket kiosk. Main content in the center but almost always a button somewhere on the screen to cancel or go back. I'd like to have reusable grid(s) where when I create a new page I basically import that grid layout and don't have to worry about the common bits and can just focus on my part. Doing a little research I feel like <ContentPresenter> might be the way to go here but it's still foreign to me.
Assumign I succeed in my first challenge, I would think the data context for those common items (say a Cancel button) would be somewhere else other than that page's ViewModel to avoid a bunch of duplication of code. Is that best approach to take the individual controls and reference a different view model from the XAML? Something like...
<Button ... DataContext={Binding CommonStuffVM, Source={StaticResource Locator}} />
You can use Templates or DataTemplates to create a reusable template defining how an object should look.
For example,
<DataTemplate DataType="{x:Type local:MovieTicket}">
<!-- Write the XAML for your Movie Ticket -->
</DataTemplate>
You can give your DataTemplate an x:Key to reference it specifically in an ItemTemplate or ContentTemplate, or leave it out so it will be used anytime WPF tries to draw a MovieTicket object
<ItemsControl ItemsSource="{Binding MovieTickets}" />
<ContentControl Content="{Binding SelectedMovieTicket}" />
For your second question, I think this would be a bad idea for individual controls, although its a common practice for complete sections of the program.
When using MVVM, your ViewModels are your application, not your UI.
If your window should display a list of Movies for the user to edit, and allow the user to Save or Cancel their changes, then your ViewModel should contain an ObservableCollection<Movie> and contain an ICommand SaveCommand and ICommand CancelCommand
You really want your View to only reflect your ViewModels, and not have them pulling the data they need from all over the ViewModel hierarchy.
Based on your question, you may be interested in a blog article I have about Navigation with MVVM which uses code very similar to what you're asking about. It shows how to change the UI using implicit DataTemplates, and contains a complete code sample that you can download and look at to get a better idea of the MVVM architecture.

Dynamically bind Views into a ContainerControl with MVVM

I've been learning the MVVM pattern with Josh Smith's article and I want to create a classic layout with some links to the right (managed with commands) so when I click one I can show my view to the right into a tab control (inside it there is a ContentControl).
This is simple when I use a DataTemplate with the specific View and ViewModel I want to show on screen like this.
<!-- this section into my MainWindow's resources file -->
<DataTemplate xmlns:vm='clr-namespace:WpfFramework.ViewModels'
xmlns:vw='clr-namespace:WpfFramework.Views'
DataType="{x:Type vm:MySpecificViewModel }" >
<vw:MySpecificView />
</DataTemplate>
But, I want something more generic. I mean that my mainWindow should not know a specific View nor a specific ViewModel. It should only know that it binds to some commands and has a tab control which shows "some view". Every sample including Josh Smith's article seems to have limited universe of views and viewmodels, that's great with a sample.
So, how can I tell my ContentControl that some view (with its corresponding viewModel) is gonna be there without being so specific (without "burning" into the mainView the concrete types)?
best regards
Rodrigo
PD. I have tryed with base a ViewModel and Base View but it doesn't seem to work.
In your main View, bind a ContentControl to a generic ViewModelBase property
<ContentControl Content="{Binding CurrentPage}" />
CurrentPage would be defined in the main ViewModel as a ViewModelBase object, and to switch pages you simply set CurrentPage to whatever you want.
So when you click on something like the HomePageCommand, the main ViewModel would execute CurrentPage = new HomePageViewModel(); providing that HomePageViewModel inherits from ViewModelBase.
I wrote something a little while ago that shows some samples here if you're interested

Resources