54

I have a WPF application with multiple views. I want to switch from view 1 to view 2 and from there I can switch to multiple views. So I want a button on view 1 that loads view2 in the same window.

I tried those things, but can't get it to work.

From the first link, the problem is that I don't understand the ViewModelLocator code. They call the CreateMain(); function but where is this defined, and how can I switch to another view from inside a view.

Kristian Barrett
  • 3,574
  • 2
  • 26
  • 40
user2499088
  • 625
  • 1
  • 6
  • 12
  • 1
    @AndrasSebö, on this occasion, I disagree with you. While I accept that this is not a great question, I have seen much worse and I believe that it is quite clear what the user is after. – Sheridan Oct 29 '13 at 09:37
  • 3
    Well the question is: How can i switch the view from inside a view. – user2499088 Oct 29 '13 at 09:47
  • 1
    Did you find a good way to address this issue? – User1551892 Jan 29 '14 at 08:52
  • 1
    I ended up with using the magellan framework. It's a great framework for apps with a lot of navigation. – user2499088 Jan 29 '14 at 15:04
  • 1
    @user2499088, please put that into an answer, perhaps with a few more lines and then accept it as the correct answer, so that this question can be marked as answered. – Sheridan May 06 '14 at 22:10
  • 3
    @user2499088, please add an answer and accept it. Duplicates of this question cannot be closed as a duplicate if this question does not have an accepted answer. Please follow the guidelines from the [What should I do when someone answers my question?](http://stackoverflow.com/help/someone-answers) and [What does it mean when an answer is "accepted"?](http://stackoverflow.com/help/accepted-answer) pages of the Help Center. – Sheridan Jul 30 '14 at 10:08

4 Answers4

129

Firstly, you don't need any of those toolkits/frameworks to implement MVVM. It can be as simple as this... let's assume that we have a MainViewModel, and PersonViewModel and a CompanyViewModel, each with their own related view and each extending an abstract base class BaseViewModel.

In BaseViewModel, we can add common properties and/or ICommand instances and implement the INotifyPropertyChanged interface. As they all extend the BaseViewModel class, we can have this property in the MainViewModel class that can be set to any of our view models:

public BaseViewModel ViewModel { get; set; }

Of course, you'd be implementing the INotifyPropertyChanged interface correctly on your properties unlike this quick example. Now in App.xaml, we declare some simple DataTemplates to connect the views with the view models:

<DataTemplate DataType="{x:Type ViewModels:MainViewModel}">
    <Views:MainView />
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModels:PersonViewModel}">
    <Views:PersonView />
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModels:CompanyViewModel}">
    <Views:CompanyView />
</DataTemplate>

Now, wherever we use one of our BaseViewModel instances in our application, these DataTemplates will tell the framework to display the related view instead. We can display them like this:

<ContentControl Content="{Binding ViewModel}" />

So all we need to do now to switch to a new view is to set the ViewModel property from the MainViewModel class:

ViewModel = new PersonViewModel();

Finally, how do we change the views from other views? Well there are several possible ways to do this, but the easiest way is to add a Binding from the child view directly to an ICommand in the MainViewModel. I use a custom version of the RelayComand, but you can use any type you like and I'm guessing that you'll get the picture:

public ICommand DisplayPersonView
{
    get { return new ActionCommand(action => ViewModel = new PersonViewModel(), 
        canExecute => !IsViewModelOfType<Person>()); }
}

In the child view XAML:

<Button Command="{Binding DataContext.DisplayPersonView, RelativeSource=
    {RelativeSource AncestorType={x:Type MainView}}, Mode=OneWay}" />

That's it! Enjoy.

Mike Fuchs
  • 12,081
  • 6
  • 58
  • 71
Sheridan
  • 68,826
  • 24
  • 143
  • 183
  • I think this will work. But when i do ViewModel = new ... i get the error "ViewModel is a property, but is used as a type" – user2499088 Oct 29 '13 at 10:14
  • 1
    Did you set the type of the `ViewModel` property to `BaseViewModel` and do your view models all extend that class? – Sheridan Oct 29 '13 at 10:22
  • 1
    Well i use MVVM Light so its all set to ViewModelBase, but all the viewmodels extend that class, and the property has the type ViewModelBase – user2499088 Oct 29 '13 at 10:26
  • 6
    Well if you're using MVVM Light, then you should probably stick to whatever they use to do these things... my point was that you don't *need* to use a framework to implement this functionality. – Sheridan Oct 29 '13 at 10:28
  • 1
    @Sheridan Such an inspiring approach! How did you implement the x:Type ViewModels and the Views that you used in the DataTemplate defined in the app.xaml? – LamaCoder May 15 '14 at 08:28
  • 4
    I'm not sure if I really understand your question @ETG87. The view model classes are just classes that extend the `BaseViewModel` class and the views are just `UserControl`s. – Sheridan May 15 '14 at 08:37
  • 1
    @Sheridan I put the following inside : I get following errors: The namespace prefix "BaseViewModel" is not defined. The namespace prefix "Views" is not defined. I hope that makes sense. Edit, I solved the 1st error, I forgot that I must reference my namespace. – LamaCoder May 15 '14 at 08:43
  • 3
    Dude, you need to read up about [XAML Namespaces and Namespace Mapping for WPF XAML](http://msdn.microsoft.com/en-us/library/ms747086(v=vs.110).aspx)... my views are declared in a `AppName.Views` namespace which has a mapped XAML Namespace Prefix of `Views`. The view models are declared in a `AppName.ViewModels` namespace and therefore mapped to a XAML Namespace Prefix of `ViewModels`. There should be no `BaseViewModel` namespace because that is a class, not a namespace. – Sheridan May 15 '14 at 09:12
  • 1
    My bad, @Sheridan, thanks for the link. I got it working now. Your the man. – LamaCoder May 15 '14 at 19:27
  • 2
    @Sheridan, I do this in my application but even though I create a new instance of the ViewModel in my code, the XAML code creates another instance. Is there any way to get around this? I'm trying to switch the view and immediately bind to an event but since the XAML creates a new instance, I can't make it happen. – Cody Apr 09 '15 at 15:40
  • 1
    Or is there a way to send parameters using this method? – Cody Apr 09 '15 at 15:42
  • 1
    @DoctorOreo, please ask a new question, linking to this one if you like. If you use my '@' name as you did in the above comment, I will be notified and can answer there, where there will be more space. – Sheridan Apr 10 '15 at 07:46
  • 1
    @Sheridan I've been trying to follow this, and I believe I'm missing something trivial. I've posted a new question here; http://stackoverflow.com/questions/30849784/wpf-navigation-using-mvvm. Perhaps you could take a look at point me in the right direction? – entropic Jun 15 '15 at 16:07
  • 1
    Anyone found a way to adapt this to Windows 8.1 "universal" app? DataTemplate no longer supports "DataType" ? – AUSTX_RJL Jul 22 '15 at 00:03
  • 2
    @AUSTX_RJL, if you haven't already, you should ask a new question for that. – Sheridan Jul 28 '15 at 18:20
  • 1
    Excellent approach @Sheridan . Is there a way to switch the views from inside the child view usercontrol ViewModel? How can I call my mainviewmodel from child viewmodel . – Gopichandar May 04 '16 at 10:14
  • 1
    @Gopichandar, you can use a `delegate` to signal the `MainViewModel` from a child view model. See my answer to the [How to call functions in a main view model from other view models?](http://stackoverflow.com/questions/19522202/how-to-call-functions-in-a-main-view-model-from-other-view-models/19522419#19522419) question for an example of how to do this. – Sheridan May 04 '16 at 10:17
  • It's not finding my ancestor from the `RelativeSource` part. I'm pretty positive everything is named correctly, too. Any help with this? – Jonathan Tuzman May 05 '20 at 21:55
  • Sorry @JonathanTuzman, but this is not the place to ask a new question. You can link to this question in your new question though, if that helps. – Sheridan May 27 '20 at 16:00
8

When i first started wiht MVVM I also struggled with the different MVVM-frameworks and especially the navigation part. Therefore I use this little tutorial i found, that Rachel Lim has created. It's very nice and well explained.

Have a look at it on the following link:

Hope it helped you :)

RooKie-
  • 1,373
  • 3
  • 13
  • 24
  • 3
    Thanks, but this is not what i mean. I used this example for another application, but for this application i dont have a sidemenu. So i have a button on view1 and when i click on that button it has to switch to view2 – user2499088 Oct 29 '13 at 09:52
1

Maybe this link will help you. Just set the NavigateTo property to the view which you need to display on the window.

As an example you can do something like

<Window x:Class="MainWindowView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                                 xmlns:meffed="http:\\www.codeplex.com\MEFedMVVM"
                                 meffed:ViewModelLocator.NonSharedViewModel="YourViewModel"
                                 WindowStartupLocation="CenterScreen">

    <Button meffed:NavigationExtensions.NavigateTo="firstview"
                    meffed:NavigationExtensions.NavigationHost="{Binding ElementName=_viewContainer}"
                    meffed:NavigationExtensions.NavigateOnceLoaded="False"
                    Visibility="Visible" />

    <ContentControl x:Name="_viewContainer" Margin="0,0,0,10" />
<Window>

Then the class file would be

public partial class MainWindowView : Window
{
    public MainWindowView()
    {           
              InitializeComponent();
    }

        public ContentControl ViewContainer { get { return _viewContainer; } }

    }

Then you can define each view as UserControl and then using the link I gave above bind the button's meffed:NavigationExtensions.NavigateTo="secondView". To target the ContentControl of the Window just use a RelativeSource binding. For e.g

meffed:NavigationExtensions.NavigationHost="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}},Path=ViewContainer}"

In each of the view just see that you annotate the code behind class definition with the [NavigationView("firstview")] and so on.

It is complicated for first time but it will be very easy once you understand the idea.

Sandesh
  • 2,966
  • 1
  • 20
  • 34
1
<ContentControl x:Name="K.I.S.S" Content="{Binding ViewModel, Converter={StaticResource ViewLocator}}"/>
atomaras
  • 2,468
  • 2
  • 19
  • 28