8

我目前正在学习 WPF 和 MVVM(或者至少我正在尝试......)。

我创建了一个小示例应用程序,它显示了一个带有 2 个按钮的窗口,每个按钮都应在单击时显示一个新视图。所以我创建了 3 个用户控件(带有 2 个按钮的决策者,每个“点击目标”一个用户控件)。

因此,我将 MainWindow 的 CotentControl 绑定到 MainWindowViewModel 中名为“CurrentView”的属性

MainWindow.xaml 的代码:

<Window x:Class="WpfTestApplication.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfTestApplication"
    Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
    <local:MainWindowViewModel />
</Window.DataContext>
<Grid>
    <ContentControl Content="{Binding CurrentView, Mode=OneWay}" />
</Grid>
</Window>

MainWindowViewModel 代码:

class MainWindowViewModel
{      
    private UserControl _currentView = new DecisionMaker();
    public UserControl CurrentView
    {
        get { return _currentView; }
        set { _currentView = value; }
    }

    public ICommand MausCommand
    {
        get { return new RelayCommand(LoadMouseView); }
    }

    public ICommand TouchCommand
    {
        get { return new RelayCommand(LoadTouchView); }
    }

    private void LoadMouseView()
    {
        CurrentView = new UserControlMouse();
    }

    private void LoadTouchView()
    {
        CurrentView = new UserControlTouch();
    }
}

最初的 UserControl (DecisionMaker) 会按预期显示。该方法LoadMouseView也被调用。但是视图没有改变。我错过了什么?

更新:非常感谢!我错过了 INotifyPropertyChanged 接口。你所有的答案都很棒,非常准确和有帮助!我不知道该接受哪一个-我认为这是接受“第一个”答案的最公平方式?

我接受了盲目的回答,因为它解决了问题并帮助我更好地理解了 MVVM。但是每个答案都非常感谢你们所有人!

4

5 回答 5

7

如果你想做 mvvm - 那么你应该在你的视图模型中没有对你的视图/用户控件的引用。你必须实施INotifyPropertyChanged!ps:如果您的 Viewmodel 中需要 System.Windows 命名空间 - 那就有问题了。

在您的情况下,您需要什么:

  • 1个主视图模型
  • UserControlMouse 的 1 个视图模型
  • UserControlTouch 的 1 个视图模型
  • UserControlMouse 的 1 个视图/用户控件
  • UserControlTouch 的 1 个视图/用户控件

你的 mainviewmodel 应该至少有 2 个命令来切换你的视图和 CurrentView 的 1 个属性。在您的命令中,您只需将 CurrentView 设置为正确的视图模型实例。至少每个定义正确视图的视图模型都需要两个数据模板。

public object CurrentView
{
    get { return _currentView; }
    set {
        _currentView = value; this.RaiseNotifyPropertyChanged("CurrentView");}
}

xml

<Window x:Class="WpfTestApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfTestApplication"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
 <DataTemplate DataType="{x:Type local:MyMouseViewModel}">
   <local:MyMouseUserControlView/>
  </DataTemplate>
 <DataTemplate DataType="{x:Type local:MyTouchViewModel}">
   <local:MyTouchUserControlView/>
  </DataTemplate>
</Window.Resources>
<Window.DataContext>
 <local:MainWindowViewModel />
</Window.DataContext>
<Grid>

 <!-- here your buttons with command binding, i'm too lazy to write this. -->

 <!-- you content control -->
 <ContentControl Content="{Binding CurrentView, Mode=OneWay}" />
</Grid>
</Window>
于 2012-06-12T09:16:42.023 回答
3

我会做这样的事情来选择你想要的输入样式,到 MainWindow 我添加了一个属性,让我选择输入模式。

public enum UserInterfaceModes
{
    Mouse,
    Touch,
}

public UserInterfaceModes UserInterfaceMode
{
   get { return (UserInterfaceModes)GetValue(UserInterfaceModeProperty); }
   set { SetValue(UserInterfaceModeProperty, value); }
}

public static readonly DependencyProperty UserInterfaceModeProperty = DependencyProperty.Register("UserInterfaceMode", typeof(UserInterfaceModes), typeof(MainWindow), new UIPropertyMetadata(UserInterfaceModes.Mouse));

然后对于 xaml 视图部分,您可以使用触发器选择正确的模板。

<Style TargetType="{x:Type local:MainWindow}">
   <Style.Triggers>
        <DataTrigger Binding="{Binding UserInterfaceMode}" Value="Mouse">
             <Setter Property="Template">
                  <Setter.Value>
                       <ControlTemplate TargetType="{x:Type local:MainWindow}">
                            <Grid Background="Red"/>
                       </ControlTemplate>
                  </Setter.Value>
             </Setter>
        </DataTrigger>
        <DataTrigger Binding="{Binding UserInterfaceMode}" Value="Touch">
             <Setter Property="Template">
                  <Setter.Value>
                       <ControlTemplate TargetType="{x:Type local:MainWindow}">
                           <Grid Background="Blue"/>
                       </ControlTemplate>
                   </Setter.Value>
              </Setter>
         </DataTrigger>
    </Style.Triggers>
</Style>
于 2012-06-12T09:09:18.623 回答
2

视图模型需要实现INotifyPropertyChanged。否则,当视图模型中的属性发生更改时,视图将不会收到通知。

class MainWindowViewModel : INotifyPropertyChanged
{
    private UserControl _currentView = new DecisionMaker();

    public UserControl CurrentView
    {
        get { return _currentView; }
        set
        {
            _currentView = value;
            OnPropertyChanged("CurrentView");
        }
    } 

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
于 2012-06-12T09:08:06.877 回答
1

您需要实现INotifyPropertyChangedon ,以便在更改属性MainWindowViewModel时通知视图。CurrentView

于 2012-06-12T09:08:25.740 回答
1

听起来你想要的行为几乎就是你得到的[TabControl][1]- 为什么不使用这个内置控件并将DataContext两个选项卡绑定到同一个视图模型。

这还具有您的视图模型不知道视图类的优点(我假设UserControlMouse等是用户控件)。

注意:如果您需要视图模型知道它是处于触摸模式还是鼠标模式,这将不适用。

于 2012-06-12T09:09:57.487 回答