WPF Navigate through views using MVVM pattern

前端 未结 2 1620
生来不讨喜
生来不讨喜 2020-12-12 16:38

I\'m building my first WPF using MVVM pattern. With the help of this community, I manage to create my Model, my first ViewModel and view. Now I want to add some complexity t

相关标签:
2条回答
  • 2020-12-12 16:59

    IMHO the best choose for you is to use MVVM framework (PRISM, MMVM Light, Chinch, etc) because navigation is already implemented. If you want to create your own navigation - try DataTemplate.

    0 讨论(0)
  • 2020-12-12 17:10

    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.

    0 讨论(0)
提交回复
热议问题