C# 如何在WPF中使用多个ViewModel并通过一个MainViewModel绑定它们?

C# 如何在WPF中使用多个ViewModel并通过一个MainViewModel绑定它们?,c#,wpf,xaml,data-binding,C#,Wpf,Xaml,Data Binding,我在通过MainViewModel将2个ViewModels绑定到一个视图时遇到了麻烦 My MainWindow.xaml如下所示: <Window x:Class="Dojo4.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ViewM

我在通过MainViewModel将2个ViewModels绑定到一个视图时遇到了麻烦

My MainWindow.xaml如下所示:

<Window x:Class="Dojo4.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ViewModels="clr-namespace:Dojo4.ViewModels"
    Title="Dojo4" Height="346" Width="706">
<Window.DataContext>
    <ViewModels:MainViewModel/>
</Window.DataContext>
<Grid>
    <Button Content="Register" DataContext="{Binding RegisterViewModel}" Command="{Binding Register}" HorizontalAlignment="Left" Margin="19,63,0,0" VerticalAlignment="Top" Width="75"/>
    <Label Content="Registration Name&#xD;&#xA;" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Height="25"/>
    <Label Content="Field Size" HorizontalAlignment="Left" Margin="161,10,0,0" VerticalAlignment="Top" Height="25"/>
    <Label Content="X" HorizontalAlignment="Left" Margin="161,35,0,0" VerticalAlignment="Top" Height="25"/>
    <Label Content="Y" HorizontalAlignment="Left" Margin="203,35,0,0" VerticalAlignment="Top" Height="25"/>
    <TextBox DataContext="{Binding RegisterViewModel}" Text="{Binding Name}" MaxLength="8" HorizontalAlignment="Left" Height="23" Margin="19,35,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
    <TextBox DataContext="{Binding RegisterViewModel}" HorizontalAlignment="Left" Height="23" Margin="161,62,0,0" TextWrapping="Wrap" Text="{Binding X}" VerticalAlignment="Top" Width="23"/>
    <TextBox DataContext="{Binding RegisterViewModel}" HorizontalAlignment="Left" Height="23" Margin="203,62,0,0" TextWrapping="Wrap" Text="{Binding Y}" VerticalAlignment="Top" Width="23"/>
    <Button Content="Up" DataContext="{Binding PlayerControlViewModel}" Command="{Binding MovePlayer}" CommandParameter="up" HorizontalAlignment="Left" Margin="79,118,0,0" VerticalAlignment="Top" Width="75" IsEnabled="False"/>
    <Button Content="Down" DataContext="{Binding PlayerControlViewModel}" Command="{Binding MovePlayer}" CommandParameter="down" HorizontalAlignment="Left" Margin="79,226,0,0" VerticalAlignment="Top" Width="75" IsEnabled="False"/>
    <Button Content="Left" DataContext="{Binding PlayerControlViewModel}" Command="{Binding MovePlayer}" CommandParameter="left" HorizontalAlignment="Left" Margin="10,173,0,0" VerticalAlignment="Top" Width="75" RenderTransformOrigin="0.707,0.409" IsEnabled="False"/>
    <Button Content="Right" DataContext="{Binding PlayerControlViewModel}" Command="{Binding MovePlayer}" CommandParameter="right" HorizontalAlignment="Left" Margin="145,173,0,0" VerticalAlignment="Top" Width="75" IsEnabled="False"/>
</Grid>
namespace Dojo4.ViewModels
{
    class MainViewModel : BaseViewModel
    {
        private RegistrationViewModel _RegistrationViewModel;
        public RegistrationViewModel RegistrationViewModel
        {
            get { return _RegistrationViewModel; }
        }
        private PlayerControlViewModel _PlayerControlViewModel;

        public PlayerControlViewModel PlayerControlViewModel
        {
            get { return _PlayerControlViewModel; }
        }

        private GameModel _game;

        public MainViewModel()
        {
            _game = new GameModel();
            _PlayerControlViewModel = new PlayerControlViewModel(_game);
            _RegistrationViewModel = new RegistrationViewModel(_game);
        }
    }
}
运行程序后,绑定将失败,并出现以下错误:

System.Windows.Data错误:40:BindingExpression路径错误:“在对象”“MainViewModel'(HashCode=51295333)”上找不到“Register”属性。BindingExpression:Path=Register;DataItem='MainViewModel'(HashCode=51295333);目标元素是“按钮”(名称=“”);目标属性为“Command”(类型为“ICommand”) System.Windows.Data错误:40:BindingExpression路径错误:在“对象”“MainViewModel”(HashCode=51295333)“上找不到RegisterViewModel”属性。BindingExpression:Path=RegisterViewModel;DataItem='MainViewModel'(HashCode=51295333);目标元素是“按钮”(名称=“”);目标属性为“DataContext”(类型为“Object”) System.Windows.Data错误:40:BindingExpression路径错误:在“对象”“MainViewModel”(HashCode=51295333)“上找不到RegisterViewModel”属性。BindingExpression:Path=RegisterViewModel;DataItem='MainViewModel'(HashCode=51295333);目标元素是“TextBox”(名称=“”);目标属性为“DataContext”(类型为“Object”) System.Windows.Data错误:40:BindingExpression路径错误:在“对象”“MainViewModel”(HashCode=51295333)“上找不到RegisterViewModel”属性。BindingExpression:Path=RegisterViewModel;DataItem='MainViewModel'(HashCode=51295333);目标元素是“TextBox”(名称=“”);目标属性为“DataContext”(类型为“Object”) System.Windows.Data错误:40:BindingExpression路径错误:在“对象”“MainViewModel”(HashCode=51295333)“上找不到RegisterViewModel”属性。BindingExpression:Path=RegisterViewModel;DataItem='MainViewModel'(HashCode=51295333);目标元素是“TextBox”(名称=“”);目标属性为“DataContext”(类型为“Object”) System.Windows.Data错误:40:BindingExpression路径错误:“在”“对象”“PlayerControlViewModel'(HashCode=65331996)”上找不到MovePlayer属性。BindingExpression:Path=MovePlayer;DataItem='PlayerControlViewModel'(HashCode=65331996);目标元素是“按钮”(名称=“”);目标属性为“Command”(类型为“ICommand”) System.Windows.Data错误:40:BindingExpression路径错误:“在”“对象”“PlayerControlViewModel'(HashCode=65331996)”上找不到MovePlayer属性。BindingExpression:Path=MovePlayer;DataItem='PlayerControlViewModel'(HashCode=65331996);目标元素是“按钮”(名称=“”);目标属性为“Command”(类型为“ICommand”) System.Windows.Data错误:40:BindingExpression路径错误:“在”“对象”“PlayerControlViewModel'(HashCode=65331996)”上找不到MovePlayer属性。BindingExpression:Path=MovePlayer;DataItem='PlayerControlViewModel'(HashCode=65331996);目标元素是“按钮”(名称=“”);目标属性为“Command”(类型为“ICommand”) System.Windows.Data错误:40:BindingExpression路径错误:“在”“对象”“PlayerControlViewModel'(HashCode=65331996)”上找不到MovePlayer属性。BindingExpression:Path=MovePlayer;DataItem='PlayerControlViewModel'(HashCode=65331996);目标元素是“按钮”(名称=“”);目标属性为“Command”(类型为“ICommand”)

看起来,
DataContext
无法通过
MainViewModel
绑定到
ViewModels
,我会尝试以下几件事: 首先我会尝试将一些xaml提取到它自己的视图(usercontrols/pages)中。这里的问题是,您有单独的视图模型,但没有单独的视图。大多数时候,我尝试将我的主窗口用作容纳其他视图的容器,我尝试使用单一责任原则,这基本上意味着类/方法只做一件事,只做一件事。我可以为每个视图模型创建一个视图。我想我会尝试将您的观点设置为:

因此,创建两个用户控件:(RegisterControl和PlayerControl)

您可能需要实现INotifyPropertyChange,以便在更改这些值时通知视图进行更新。为什么要更改这些值?假设您想在不同的时间显示不同的视图,那么您可以创建一个名为IView的界面,并将属性更改为:

public IView RegisterView { get; set; }
public IView PlayerControlView { get; set; }

如果这些都不起作用或对您来说没有意义,我唯一能想到的是您现在的设置方式可能是因为您没有通知viewmodels上的属性更改(INotifyPropertyChanged)。同样,尽管我不喜欢按您的方式设置数据上下文。希望这至少能帮上一点忙。

你可以试试下面的方法

不要将DataContext设置为每个控件,而是在绑定过程中按如下方式自绑定实例中的属性

<Button Content="Register" Command="{Binding RegisterViewModel.Register}" HorizontalAlignment="Left" Margin="19,63,0,0" VerticalAlignment="Top" Width="75"/>


因为UI中有很多控件,所以首先从一个控件开始,注释掉所有其他控件,如果一个控件有效,那么您将清楚如何为其他控件执行此操作。其他明智的编译器将显示许多难以通过错误检查的错误

您的代码中有输入错误:RegisterViewModel vs RegistrationViewModel。但还有其他问题:

这个代码有问题吗
DataContext=“{Binding RegisterViewModel}”Command=“{Binding Register}”
是指,可以在DataContext绑定之前计算Command属性上的绑定

只需将IsAsync=True添加到命令绑定:

<Button DataContext="{Binding RegisterViewModel}"
        Command="{Binding Register, IsAsync=True}" />

当您绑定datacontext并同时绑定同一元素的另一个属性(例如Command属性)时,应将IsAsync=True设置为除datacontext之外的所有绑定,以便更早地对datacontext进行计算


但是,您应该避免在元素上设置datacontext,因为其他属性也绑定在元素上:

<Button Command="{Binding RegisterViewModel.Register}" />

由于将多个元素数据绑定到同一个viewmodel,因此应该按viewmodel将它们分组到根元素,这样代码更具可读性和maintanab
<Button DataContext="{Binding RegisterViewModel}"
        Command="{Binding Register, IsAsync=True}" />
<Button Command="{Binding RegisterViewModel.Register}" />
    <Grid DataContext="{Binding RegisterViewModel}" >
        <Button Command="{Binding Register}" />
        <Label Content="Registration Name&#xD;&#xA;"/>
        <Label Content="Field Size" />
        <Label Content="X" />
        <Label Content="Y" />
        <TextBox Text="{Binding Name}" />
        <TextBox Text="{Binding X}" />
        <TextBox Text="{Binding Y}" />
    </Grid>
    <Grid DataContext="{Binding PlayerControlViewModel}">
        <Button Command="{Binding MovePlayer}" CommandParameter="up" />
        <Button Command="{Binding MovePlayer}" CommandParameter="down"/>
        <Button Command="{Binding MovePlayer}" CommandParameter="left" />
        <Button Command="{Binding MovePlayer}" CommandParameter="right" />
    </Grid>