将ContentControl绑定到ApplicationViewModel,它将确定要查看的用户控件?

时间:2018-02-19 23:47:04

标签: c# wpf xaml mvvm

我是WPF的新手,也是C#编程的新手(一般编程),我正在尝试开发WPF应用程序。

我试图通过几个与此类似的帖子,但我似乎找不到为什么这不起作用的答案。

所以,我很难理解MVVM架构,在多个用户控件绑定到单个<ContentControl />之间切换的方式和内容。 从我目前所理解和阅读的内容来看,我必须像这样绑定视图模型:

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

所以这就是我想要实现的目标:

左侧带有侧边栏菜单的ApplicationWindow.xaml将在应用程序运行时始终显示,剩余空间为<ContentControl/>。侧栏菜单上显示的按钮为:

  • 主要(将显示MainView.xaml用户控件,应为默认用户控件)
  • 设置(将显示SettingsView.xaml用户控件)
  • 退出(将关闭应用程序)

我知道我需要将按钮绑定到ICommand命令,我理解RelayCommand.cs类的概念。 因此,让我们跳进我的想法的简化代码,找出我需要理解的内容以及我在此过程中可能误解的内容。

MainView.xamlSettingsView.xaml包含的内容现在并不重要,因为我只想弄清楚如何在我的应用程序中显示它们。

这是ApplicationWindow.xaml

<Window x:Class="WpfApp1.ApplicationWindow"
        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:WpfApp1"
        xmlns:v="clr-namespace:WpfApp1.View"
        xmlns:vm="clr-namespace:WpfApp1.ViewModel"
        mc:Ignorable="d"
        Title="ApplicationWindow" Height="1080" Width="1920"
        WindowStyle="None" WindowState="Maximized">

    <Window.Resources>
        <DataTemplate DataType="{x:Type vm:MainViewModel}">
            <v:MainView/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type vm:SettingsViewModel}">
            <v:SettingsView/>
        </DataTemplate>
    </Window.Resources>

    <DockPanel>
        <!--Menu bar on the left-->
        <Border DockPanel.Dock="Left">
            <StackPanel Orientation="Vertical" Background="Gray" Width="120">
                <Button Content="Main" Command="{Binding ShowMainCommand}"/>
                <Button Content="Settings" Command="{Binding ShowSettingsCommand}"/>
                <Button Content="Exit" Command="{Binding ExitApplicationCommand}"/>
            </StackPanel>
        </Border>
        <!--The content control that view the current view-->
        <ContentControl Content="{Binding ApplicationViewModel}"/>
    </DockPanel>
</Window>

注意:通过覆盖OnStartup()方法,在App.xaml.cs中将DataContext设置为ApplicationViewModel.cs

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        ApplicationWindow app = new ApplicationWindow
        {
            DataContext = new ApplicationViewModel()
        };
        app.Show();
    }
}

这是ApplicationViewModel.cs

public class ApplicationViewModel : ViewModelBase
{
    #region Fields

    private List<ViewModelBase> _viewModels;
    private ViewModelBase _currentViewModel;

    private ICommand _showMainCommand;
    private ICommand _showSettingsCommand;
    private ICommand _exitApplicationCommmand;

    #endregion

    #region Constructor

    public ApplicationViewModel()
    {
        ViewModels = new List<ViewModelBase>
        {
            new MainViewModel(),
            new SettingsViewModel()
        };

        CurrentViewModel = ViewModels[0];
    }

    #endregion

    #region Public Properties

    public List<ViewModelBase> ViewModels
    {
        get
        {
            return _viewModels;
        }
        set
        {
            if (_viewModels != value)
            {
                _viewModels = value;
                OnPropertyChanged(nameof(ViewModels));
            }
        }
    }

    public ViewModelBase CurrentViewModel
    {
        get
        {
            return _currentViewModel;
        }
        set
        {
            if(_currentViewModel != value)
            {
                _currentViewModel = value;
                OnPropertyChanged(nameof(CurrentViewModel));
            }
        }
    }

    #endregion

    #region Commands

    public ICommand ShowMainCommand
    {
        get
        {
            if(_showMainCommand == null)
            {
                _showMainCommand = new RelayCommand(action => ShowMain());
            }
            return _showMainCommand;
        }
    }

    public ICommand ShowSettingsCommand
    {
        get
        {
            if (_showSettingsCommand == null)
            {
                _showSettingsCommand = new RelayCommand(action => ShowSettings());
            }
            return _showSettingsCommand;
        }
    }

    public ICommand ExitApplicationCommand
    {
        get
        {
            if (_exitApplicationCommmand == null)
            {
                _exitApplicationCommmand = new RelayCommand(action => ExitApplication());
            }
            return _exitApplicationCommmand;
        }
    }

    #endregion

    #region Private Methods

    private void ShowMain()
    {
        CurrentViewModel = ViewModels[0];
    }

    private void ShowSettings()
    {
        CurrentViewModel = ViewModels[1];
    }

    private void ExitApplication()
    {
        MessageBoxResult result = MessageBox.Show("Are you sure you want to exit?", "Exit", MessageBoxButton.YesNo);
        if (result == MessageBoxResult.Yes)
        {
            System.Windows.Application.Current.Shutdown();
        }
    }

    #endregion
}

因此,根据我的理解,ApplicationWindow.xaml应该能够确定从CurrentViewModel设置的内容中显示哪个视图。

为了获取信息(或错过信息),这里有ViewModelBase.cs

public class ViewModelBase : INotifyPropertyChanged
{
    #region INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    #endregion
}

RelayCommand.cs

public class RelayCommand : ICommand
{
    #region Fields

    private readonly Action<object> _execute;
    private readonly Predicate<object> _canExecute;

    #endregion

    #region Constructors

    public RelayCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");

        _execute = execute;
        _canExecute = canExecute;
    }

    public RelayCommand(Action<object> execute) : this(execute, null)
    {
    }

    #endregion

    #region ICommand

    public bool CanExecute(object parameters)
    {
        return _canExecute == null ? true : _canExecute(parameters);
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public void Execute(object parameters)
    {
        _execute(parameters);
    }

    #endregion
}

我希望我对你的思考过程很清楚,你们中的一位聪明的程序员可以帮助解决这个问题,并帮助我理解为什么这不是我想要的结果。

如果我想要做的事情比Elon Musk关于制作多行星的项目更难,请随意解释原因并建议我更好的方式

1 个答案:

答案 0 :(得分:0)

您的内容控件绑定应指向切换ViewModels时更改的实际属性

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