I'm doing some research on WPF and MVVM to evaluate if this is something we wish to implement in a project.
There is one particular thing that I quite cannot understand (my book on WPF hasn't arrived yet).
I've read Josh Smith's article 'WPF Apps With The Model-View-ViewModel Design Pattern' and also his article 'Using RoutedCommands with a ViewModel in WPF'.
My demo application is somewhat different from Josh Smith's demo in that I have menu items that aren't directly bound to the main view model, but would rather be handled by other view-models.
My main window is bound to a MainViewModel object which exposes a object called View (derived from a ViewModelBase class and bound to the Main Window through a ContentControl). This View object is replaced with different ViewModel's such as CustomerViewModel, CustomersViewModel etc. (these are rendered using views defined in my resource dictionary: ).
All this is well and find, but since I wan't menu items in my main window (such as New, Save etc.) that should be enabled, disabled based on the view I found Josh Smith's article on the CommandSink pattern(?) but he states that it is obsolete, and one should rather use the RelayCommand.
Now I am confused, as I cannot figure out how to implement this functionality using this approach.
Thanks,
Vincent
A simple way to do this is to have each view expose a property of type MenuItem, which contains its local menu. (The menu itself can live in the view's resource dictionary; you just implement a property getter that retrieves it using FindResource.)
Then make your main menu look something like this:
<Menu>
<MenuItem Header="File">
<!-- content for your application's File menu goes here -->
</MenuItem>
<MenuItem Header="Edit">
<!-- content for your application's Edit menu goes here -->
</MenuItem>
<MenuItem Header="{Binding SelectedView.MenuItem.Header}"
ItemsSource={Binding SelectedView.MenuItem.Items}/>
<MenuItem Header="Help">
<!-- content for your application's Help menu goes here -->
</MenuItem>
</Menu>
Edit
I think I misunderstood your question, but in part that's because I think your question's much easier to answer than the one I thought you were asking.
Create a RoutedCommand property in your application view model called DisabledCommand that is always disabled. Then bind your application menus to commands like this:
public RoutedCommand SaveCustomerCommand
{
get
{
CustomerView cv = SelectedView as CustomerView;
return cv == null
? DisabledCommand;
: ((CustomerViewModel)cv.DataContext).SaveCommand;
}
}
The casting is a little awkward looking, but other than that this is straightforward, elegant even. The only implementation detail your application view needs to know about the customer view is that there's a SaveCommand on its view model.
Related
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.
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
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.
Does anybody have an idea how to change screens (views) in a MVVM View-First-Approach (The view instantiates the ViewModel:
DataContext="{Binding Source={StaticResource VMLocator},
Path=Find[EntranceViewModel]}"
)
For example:
In my MainWindow (Shell) I show a entrance view with a Button "GoToBeach".
<Window>
<DockPanel>
<TextBox DockPanel.Dock="Top" Text="{Binding Title}" />
<view.EntranceView DockPanel.Dock="Top" />
</DockPanel>
</Window>
When the button is clicked I want to get rid of the "EntranceView" and show the "BeachView".
I am really curious if somebody knows a way to keep the View-First Approach and change the screen (view) to the "BeachView".
I know there are several ways to implement it in a ViewModel-First Approach, but that is not the question.
Perhabs I missed something in my mvvm investigation and can't see the wood for the trees... otherwise i am
hoping for a inspiring discussion.
One possibility would be to have all views in the (MainWindow(Shell) and using Triggers for their visibility. But having a lot of different screens (views) all declared in the MainWindow doesnt feel right for me...
This question came up while reading this nice way of using MEF with MVVM I found on John Papas Blog: Simple ViewModel Locator for MVVM: The Patients Have Left the Asylum . But as nice as this marriage of view and viewmodel is, it seems like there is no way to change screens that satisfies me. :)
So in my opinion if you have a lot of screens(views) you better use a ViewModel-First-Approach...
This looks like it might help:
Creating a ViewModel : do it before or after model data is available?
Failing that, how about creating the ViewModel once only at startup, and assigning it to each View window as it's created (rather than creating a new ViewModel each time). Then just close the first View and open up a new View as required, reassigning the single ViewModel instance.
You may want to look at Prism (i.e. the composite application library). Prism facilitates navigation between views via the region manager. This might be overkill for your application and can take a while to get your head around. Prism also allows you to develop using the MVVM pattern as well.
You can find more information about prism and prism navigation in the Prism documentation.
I'm looking into using MVVM and while I understand it for the most part, there is one thing I can't get my head around.
Imagine I have a View and ViewModel combination that show a list of foobars. When the user selects a foobar in the list and clicks the edit button I want the foobar to be shown in a popup dialog window so it can be edited. This dialog window (the view) will have its own associated ViewModel.
I understand that the button can be bound to a command on the list ViewModel, but from here how do I instantiate the foobar editor?
1) Do I have to send a message back to the View, which will open the dialog window? If so, doesn't this defeat the purpose of having the command?
2) How does the foobar get passed into the ViewModel for the editor? If it is by its constructor, doesn't this make it hard to declare the ViewModel in XAML?
I feel that this is the last piece of the puzzle that is preventing me from using MVVM and I'd really like to get a nice decoupled solution to this.
Thanks
Matt
I would perhaps do it in the following way:
The command attached to the edit button starts edit dialog, creating a custom ViewModel (VM) for it. The command itself should be perhaps either in the list's VM or in the Model (not quite sure).
Foobar edit dialog's VM gets a reference to the Foobar at its constructor.
The foobar is cloned and the clone is edited.
As soon as the user presses OK in the foobar edit dialog, the clone's values are written back to the original foobar in the VM (and the dialog is closed).
The need for a clone comes from the fact that the user doesn't want to see the changes in the foobar list until he accepts the changes in the edit dialog. If however online editing is okay, the clone is not needed.
The changes are propagated automatically.
PS: although I am MVVM's proponent, I am not sure that my solution is an orthodox one from pure MVVM point of view.
This article from codeproject shows a WPF Dialog control that does exactly what you need. The reason this implementation is necessary is that you can't put a Window inside the visual tree of any other control. Which means out of the box WPF doesn't let you create a dialog inside a window. So the above article creates a subclass ContentControl that creates a window.
Anyways, you put this in your FooBarList View
<dialog:Dialog Content="{Binding Path=DialogViewModel}" />
You make sure you have something like this in a resource dictionary somewhere:
<Style TargetType="{x:Type dialog:Dialog}">
<Style.Triggers>
<Trigger Property="HasContent" Value="True">
<Setter Property="Showing" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
and just write something like this(For WPF to work you need to implement INotifyPropertyChanged):
public Class FooBarListViewModel
{
IList<FooBar> FooBarList {get;set;}
FooBar SelectedFooBar {get;set;}
ViewModelBase DialogViewModel {get;set;}
public EditFooBar(object param)
{
DialogViewModel = FooBar;
}
}
To link the View to edit the FooBar to the FooBar ViewModel just do something like this(preferably in the Application.Resources so it's global)
<DataTemplate DataType={x:Type vm:FooBarViewModel}>
<vw:FooBarView/>
</DataTemplate>
(Or optionally: Use an IValueConverter to Convert get your View from a ViewModel like this post shows)
And then you're set. May sound like a lot, but it really frees you up a lot.
What’s missing is a Controller which is responsible for the workflow of the ViewModels. The Controller creates the ViewModels and it passes the necessary data between the ViewModels.
The WPF Application Framework (WAF) project contains sample applications that show how this might work.