WPF-24 使用MVVM进行导航

当我们刚开始使用MVVM模式的时候和传统CodeBehind相比会遇到很多问题,但是肯定是有解决方法,我们应该尽量往这个方向思考,下面我们通过一个例子来演示如何在不同View之间进行切换,这节我们通过一个简单例子来实现导航,基本的项目结构如下:

WPF-24 使用MVVM进行导航

我们新建一个Common文件夹,创建DelegateCommand和ViewModelBase类两个基础类.

using System;using System.Windows.Input;namespace WPF_23_MVVM_SwitchPage.Common{    public class DelegateCommand : ICommand    {        private Action<object> _execute;        private Func<bool> _canExecute;        public DelegateCommand(Action<object> executeMethod)        {            _execute = executeMethod;        }        public DelegateCommand(Action<object> executeMethod, Func<bool> canExecute)            : this(executeMethod)        {            this._canExecute = canExecute;        }        public event EventHandler? CanExecuteChanged;        public bool CanExecute(object parameter)        {            return true;        }        public void Execute(object parameter)        {            _execute?.Invoke(parameter);        }    }}
using System;using System.ComponentModel;using System.Runtime.CompilerServices;namespace WPF_23_MVVM_SwitchPage.Common{    public class ViewModelBase : INotifyPropertyChanged    {        #region INotifyPropertyChanged Members        public event PropertyChangedEventHandler? PropertyChanged;        public virtual void NotifyPropertyChanged([CallerMemberName] String propertyName = "")        {            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));        }        #endregion    }}
创建View文件夹并在下面新建三个UserControl如下所示,每个页面放一个TextBlock代表当前是哪个页面,创建ViewModel文件夹及对应的ViewModel

WPF-24 使用MVVM进行导航

我们定义一个接口IViewModel,分别让三个ViewModel实现该接口

namespace WPF_23_MVVM_SwitchPage{    public interface IViewModel    {        string Name { get; }    }}
    public class DiscoverViewModel : ViewModelBase, IViewModel    {        public string Name => "Disconver";    }
    public class SearchViewModel : ViewModelBase, IViewModel    {        public string Name => "Search";    }
    public class ToolsViewModel : ViewModelBase, IViewModel    {        public string Name => "Tool";    }
我们将MainWindow.xaml命名为MainFrameView.xaml,并创建对应的MainFrameViewModel.cs
    public class MainFrameViewModel : ViewModelBase    {        private ICommand _changeViewCommand;        private IViewModel _currentViewModel;        private List<IViewModel> _viewModels;        public MainFrameViewModel()        {            _viewModels = new List<IViewModel>();            ViewModels.Add(new SearchViewModel());            ViewModels.Add(new DiscoverViewModel());            ViewModels.Add(new ToolsViewModel());
            _changeViewCommand = new DelegateCommand(ChangeView);            _currentViewModel = ViewModels[0];        }        public ICommand ChangeViewCommand        {            get { return _changeViewCommand; }            set { _changeViewCommand = value; }        }        public List<IViewModel> ViewModels        {            get            {                return _viewModels;            }            set            {                _viewModels = value;            }        }        public IViewModel CurrentViewModel        {            get            {                return _currentViewModel;            }            set            {                if (_currentViewModel != value)                {                    _currentViewModel = value;                    NotifyPropertyChanged("CurrentViewModel");                }            }        }        public void ChangeView(object parameter)        {            var viewModel = (IViewModel)parameter;            if (!ViewModels.Contains(viewModel))                ViewModels.Add(viewModel);
            CurrentViewModel = ViewModels.FirstOrDefault(vm => vm == viewModel);        }    }
<Window x:Class="WPF_23_MVVM_SwitchPage.MainFrameView"        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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"        xmlns:local="clr-namespace:WPF_23_MVVM_SwitchPage"        mc:Ignorable="d"        Title="MainFrameView" Height="450" Width="800">    <Window.Resources>        <DataTemplate DataType="{x:Type local:SearchViewModel}">            <local:SearchView />        </DataTemplate>        <DataTemplate DataType="{x:Type local:DiscoverViewModel}">            <local:DiscoverView />        </DataTemplate>        <DataTemplate DataType="{x:Type local:ToolsViewModel}">            <local:ToolsView/>        </DataTemplate>    </Window.Resources>    <DockPanel>        <Border DockPanel.Dock="Left" BorderBrush="Black" BorderThickness="0,0,1,0">            <ItemsControl ItemsSource="{Binding ViewModels}">                <ItemsControl.ItemTemplate>                    <DataTemplate>                        <Button Content="{Binding Name}"                                Command="{Binding DataContext.ChangeViewCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"                                CommandParameter="{Binding }"                                Margin="5,5" Width="60" Height="34"/>                    </DataTemplate>                </ItemsControl.ItemTemplate>            </ItemsControl>        </Border>        <ContentControl Content="{Binding CurrentViewModel}" />    </DockPanel></Window>
我们在App.xaml 设置一下启动项之前,我们需要在xaml中移除StartupUri="MainWindow.xaml",上面还有一个小细节,统一命名空间是WPF_23_MVVM_SwitchPage
    public partial class App : Application    {        protected override void OnStartup(StartupEventArgs e)        {            base.OnStartup(e);            MainFrameView app = new MainFrameView();            MainFrameViewModel context = new MainFrameViewModel();            app.DataContext = context;            app.Show();        }    }

到此代码基本完成,我们看一下效果:

WPF-24 使用MVVM进行导航