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

时间:2013-11-23 20:53:51

标签: c# wpf xaml data-binding

我被困在将MainViewModel上的2个ViewModel绑定到一个View。

我的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>

和我的MainViewModel如下:

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路径错误:'object'''MainViewModel'(HashCode = 51295333)'上找不到'Register'属性。 BindingExpression:路径=注册; DataItem ='MainViewModel'(HashCode = 51295333); target元素是'Button'(Name =''); target属性是'Command'(类型'ICommand')   System.Windows.Data错误:40:BindingExpression路径错误:'object'''MainViewModel'(HashCode = 51295333)'上找不到'RegisterViewModel'属性。 BindingExpression:路径= RegisterViewModel; DataItem ='MainViewModel'(HashCode = 51295333); target元素是'Button'(Name =''); target属性是'DataContext'(类型'Object')   System.Windows.Data错误:40:BindingExpression路径错误:'object'''MainViewModel'(HashCode = 51295333)'上找不到'RegisterViewModel'属性。 BindingExpression:路径= RegisterViewModel; DataItem ='MainViewModel'(HashCode = 51295333); target元素是'TextBox'(Name =''); target属性是'DataContext'(类型'Object')   System.Windows.Data错误:40:BindingExpression路径错误:'object'''MainViewModel'(HashCode = 51295333)'上找不到'RegisterViewModel'属性。 BindingExpression:路径= RegisterViewModel; DataItem ='MainViewModel'(HashCode = 51295333); target元素是'TextBox'(Name =''); target属性是'DataContext'(类型'Object')   System.Windows.Data错误:40:BindingExpression路径错误:'object'''MainViewModel'(HashCode = 51295333)'上找不到'RegisterViewModel'属性。 BindingExpression:路径= RegisterViewModel; DataItem ='MainViewModel'(HashCode = 51295333); target元素是'TextBox'(Name =''); target属性是'DataContext'(类型'Object')   System.Windows.Data错误:40:BindingExpression路径错误:'object'''PlayerControlViewModel'(HashCode = 65331996)'上找不到'MovePlayer'属性。 BindingExpression:路径= MovePlayer; DataItem ='PlayerControlViewModel'(HashCode = 65331996); target元素是'Button'(Name =''); target属性是'Command'(类型'ICommand')   System.Windows.Data错误:40:BindingExpression路径错误:'object'''PlayerControlViewModel'(HashCode = 65331996)'上找不到'MovePlayer'属性。 BindingExpression:路径= MovePlayer; DataItem ='PlayerControlViewModel'(HashCode = 65331996); target元素是'Button'(Name =''); target属性是'Command'(类型'ICommand')   System.Windows.Data错误:40:BindingExpression路径错误:'object'''PlayerControlViewModel'(HashCode = 65331996)'上找不到'MovePlayer'属性。 BindingExpression:路径= MovePlayer; DataItem ='PlayerControlViewModel'(HashCode = 65331996); target元素是'Button'(Name =''); target属性是'Command'(类型'ICommand')   System.Windows.Data错误:40:BindingExpression路径错误:'object'''PlayerControlViewModel'(HashCode = 65331996)'上找不到'MovePlayer'属性。 BindingExpression:路径= MovePlayer; DataItem ='PlayerControlViewModel'(HashCode = 65331996); target元素是'Button'(Name =''); target属性是'Command'(类型'ICommand')

看起来,DataContext无法通过MainViewModel绑定到ViewModel。

也许你对我有所暗示? :) 任何帮助都会受到欢迎!

3 个答案:

答案 0 :(得分:8)

您的代码中存在拼写错误:RegisterViewModel与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将它们分组到根元素,这样您的代码就更具可读性和可维护性:

    <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>

请注意这有助于避免违反最简单的“不要重复自己”的形式。原理。我不会一直重复datacontext绑定

答案 1 :(得分:1)

我会尝试几件事: 首先我会尝试将一些xaml提取到自己的视图中(usercontrols / pages)。我在这里遇到的问题是你有单独的视图模型但不是单独的视图。大多数时候我尝试使用我的主窗口作为容纳其他视图的容器,我尝试使用单一责任主体,这基本上意味着类/方法只做一件事和一件事。我可以这样做并查看每个视图模型。我想我会尝试按如下方式设置您的视图:

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

        <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>
   <ContentPresenter Content="{Binding RegisterView}" />
   <ContentPresenter Content="{Binding PlayerControlView}" />
</Grid>

在MainViewModel中:

public RegisterControl RegisterView { get; set; }
public PlayerControl PlayerControlView { get; set; }

public MainWindow()
{
   RegisterView = new RegisterControl();
   PlayerControlView = new PlayerControl();
}

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

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

如果这一切都不起作用或对你有意义,我现在唯一可以想到你设置它的方式可能是因为你没有在你的视图模型上通知属性更改(INotifyPropertyChanged)。虽然我不喜欢按照你的方式设置数据上下文。希望这至少有点帮助。

答案 2 :(得分:1)

您可以尝试下面的内容

不要将DataContext设置为每个控件,而是在绑定期间自动绑定该实例的属性,如下所示

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

由于您在UI中有很多控件,首先从One控件开始,注释掉所有其他控件,如果一个控件工作,您将清楚如何为其他控件执行此操作。其他明智的编译器将显示很多错误,这很难通过错误