Wpf Mvvm Navigate Views

MVVM WPF navigating views from inside the views

to make example work remove x:Key="..." from DataTemplates.


ContentControl binds to CurrentView. CurrentView is a view model instance and doesn't have visual representation (doesn't derive from framework Visual class).

There is no ContentTemplate, so ContentControl tries to find a default DataTemplate for View1Model (or View2Model) type. but both DataTemplates which are declared in Resources, are not default, they are named with x:Key. So ContentControl finds nothing and falls back to guaranted method: display string representation of Content, because every .NET object has ToString() method.

WPF Navigate through views using MVVM pattern

I'm not sure you need a separate "navigation" view model, you could easily put it into the main. Either way:

To separate your "child" views, I would use a simple ContentPresenter on your "main" view:

<ContentPresenter Content="{Binding CurrentView}"/>

The easiest way to implement the backing property is to make it a UserControl, though some would argue that doing so violates MVVM (since the ViewModel is now dependent on a "View" class). You could make it an object, but you lose some type safety. Each view would be a UserControl in this case.

To switch between them, you are going to need some sort of selection control. I've done this with radio buttons before, you bind them like so:

<RadioButton Content="View 1" IsChecked="{Binding Path=CurrentView, Converter={StaticResource InstanceEqualsConverter}, ConverterParameter={x:Type views:View1}"/>

The converter is pretty simple, in "Convert" it just checks if the current control is a type of the parameter, in "ConvertBack" it returns a new instance of the parameter.

public class InstanceEqualsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (parameter as Type).IsInstanceOfType(value);
}

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (bool)value ? Activator.CreateInstance(parameter as Type) : Binding.DoNothing;
}
}

Binding to a combobox or other selection control would follow a similar pattern.

Of course you could also use DataTemplates (with a selector, unfortunately not something I have done before) and load them into your resources using merged dictionaries (allowing separate XAML). I personally prefer the user control route, pick which is best for you!

This approach is "one view at a time". It would be relatively easy to convert to multiple views (your UserControl becomes a collection of user controls, use .Contains in the converter etc.).

To do this with buttons, I would use commands and take advantage of the CommandParameter.

The button XAML would look like:

<Button ... Command={Binding SwitchViewsCommand} CommandParameter={x:Type local:ClientsView}/>

Then you have a delegate command (tutorial here) that runs the activator code from the converter:

public ICommand SwitchViewsCommand {get; private set;}

public MainViewModel()
{
SwitchViewsCommand = new DelegateCommand((parameter) => CurrentView = Activator.CreateInstance(parameter as Type));
}

That is off the top of my head, but should be pretty close. Let me know how it goes!

Let me know if I provide any further information!

Update:

To answer your concerns:

  1. Yes, each time you push the button a new instance of the view is created. You could easily fix this by holding a Dictionary<Type, UserControl> that has pre-created views and index into it. For that matter, you could use a Dictonary<String, UserControl> and use simple strings as the converter parameters. The disadvantage is that your ViewModel becomes tightly coupled to the kinds of views it can present (since it has to populate said Dictionary).

  2. The class should get disposed, as long as no one else holds a reference to it (think event handlers that it registered for).

  3. As you point out, only one view is created at a time so you shouldn't need to worry about memory. You are, of course, calling a constructor but that isn't THAT expensive, particularly on modern computers where we tend to have plenty of CPU time to spare. As always, the answer to performance questions is "Benchmark it" because only you have access to the intended deployment targets and entire source to see what actually performs the best.

Navigation from one view to another in WPF MVVM

Your ViewModel property implementation doesn't raise a PropertyChanged event when the value changes. This is usually done via an INotifyPropertyChanged implementation. Because of that, your view doesn't get notified that something has changed.

In your case, this means that you need a backing field for your ViewModel property and implement your ViewModel property similar to this:

private BaseViewModel _viewModel;
public BaseViewModel ViewModel
{
get { return _viewModel; }
set
{
if(_viewModel != value)
{
_viewModel = value;
OnPropertyChanged("ViewModel");
}
}
}

Since you are already deriving from BaseViewModel I assume that the method OnPropertyChanged (or some method with a similar name) is implemented there. It is also quite common that you don't have to specify the property name ("ViewModel") as an argument, since lots of implementations use the [CallerMemberName] attribute for this purpose.

Page navigation in WPF using MVVM

I would suggest using a framework to achieve the IOC container solution. Mvvm light for example uses a ViewModelLocator to store its ViewModels.
An example for page navigation in MVVMLight can be found here.

WPF Navigation using MVVM

The AncestorType should be MainWindow not MainViewModel. MainViewModel is not a class that is part of the visual tree.



Related Topics



Leave a reply



Submit