MVVM创建ViewModel

时间:2013-11-14 17:32:55

标签: c# wpf mvvm viewmodel

有人可以向我解释如何为MVVM模式创建一个ViewModel。 我试着理解这里的教程:http://msdn.microsoft.com/en-us/magazine/dd419663.aspx,但我无法理解代码中到底发生了什么。

假设我们要创建一个基本应用程序,用于从本地数据库中获取和添加人员,并在视图中显示它们。 ViewModel应该如何以及如何为它创建RelayCommands。首先,我们为什么要将变量设置两次:一次私有,然后再次公开。

编辑:感谢您的帮助到目前为止。我还有一件我不知道的事情 - 如何将View绑定到ViewModel和Vice Versa

以下是模型:

public class Student : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    private string name;
    private string surname;
    private string age;

    public string Name
    {
        get
        {
            return name;
        }
        set
        {
            name = value;
            OnPropertyChanged("Name");
        }
    }

    public string Surname
    {
        get
        {
            return surname;
        }
        set
        {
            surname = value;
            OnPropertyChanged("Surname");
        }
    }

    public string Age
    {
        get
        {
            return age;
        }
        set
        {
            age = value;
            OnPropertyChanged("Age");
        }
    }
}

这是ViewModel:

public class MainViewModel : ViewModelBase
{
    ObservableCollection<Student> studentList;
    Student selectedPerson;

    public MainViewModel()
    {
        //populate some sample data
        studentList = new ObservableCollection<Student>()
    {
        new Student(){Name="John", Surname="Smith", Age="28"},
        new Student(){Name="Barbara", Surname="Anderson", Age="23"}
    };
    }

    public ObservableCollection<Student> StudentList
    {
        get { return studentList; }
    }

    public Student SelectedPerson
    {
        get { return selectedPerson; }
        set
        {
            selectedPerson = value;
            RaisePropertyChanged("SelectedPerson");
        }
    }

    private RelayCommand _addStudentCommand;
    public ICommand AddStudentCommand
    {
        get
        {
            return _addStudentCommand
                ?? (_addStudentCommand = new RelayCommand(() =>
                {
                    Student student = new Student();
                    studentList.Add(student);
                }));
        }
    }
}

我找到了一种方法,使用Csharp中的视图将ViewModel绑定到View,但是我仍然想到如何将View绑定到ViewModel的问题。更具体地说,如何使用用户在视图中输入的值创建新学生。

这是View的XAML代码

<Window x:Class="MVVMLight.View.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" 
    SizeToContent="WidthAndHeight">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="2*"/>
        <RowDefinition Height="2*"/>
        <RowDefinition Height="2*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>

    <TextBlock x:Name="NameTextBlock"
               Text="Name"
               Style="{StaticResource TextBlockTextStyle}"/>
    <TextBlock x:Name="SurnameTextBlock"
               Grid.Row="1"
               Text="Surname"
               Style="{StaticResource TextBlockTextStyle}"/>
    <TextBlock x:Name="AgeTextBlock"
               Grid.Row="2"
               Text="Age"
               Style="{StaticResource TextBlockTextStyle}"/>
    <TextBox x:Name="NameTextBox"
             Grid.Column="1"
             Style="{StaticResource TextBoxTextStyle}"/>
    <TextBox x:Name="SurnameTextBox"
             Grid.Row="1"
             Grid.Column="1"
             Style="{StaticResource TextBoxTextStyle}"/>
    <TextBox x:Name="AgeTextBox"
             Grid.Row="2"
             Grid.Column="1"
             Style="{StaticResource TextBoxTextStyle}"/>
    <ListBox x:Name="StudentListBox"
             Grid.ColumnSpan="2"
             Grid.Row="4"
             Style="{StaticResource ListBoxStyle}"
             ItemsSource="{Binding StudentList}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <TextBlock Text="{Binding Name}"
                               Style="{StaticResource TextBlockTextStyle}"/>
                    <TextBlock Text="{Binding Surname}"
                               Grid.Column="1"
                               Style="{StaticResource TextBlockTextStyle}"/>
                    <TextBlock Text="{Binding Age}"
                               Grid.Column="2"
                               Style="{StaticResource TextBlockTextStyle}"/>
                </Grid>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
    <Button x:Name="AddButton"
            Grid.Row="7"
            Grid.ColumnSpan="2"
            HorizontalAlignment="Center"
            Content="Add"
            Margin="7,7,7,7"
            Command="{Binding AddStudentCommand}"/>        
</Grid>

这是View的Csharp代码

 public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new MainViewModel();
    }
}

我对View和ViewModel之间的绑定有一些疑问: 使用这种绑定有哪些优缺点? 如果我打算使用数据库,最好的绑定方式是什么?

  1. 这是ViewModel和Model应该是什么样子
  2. 如何创建用于将学生添加到ObservableCollection的RelayCommand
  3. 为什么我们先私下设置,然后再公开[已回答]
  4. 如何将View绑定到ViewModel和Vice Versa

1 个答案:

答案 0 :(得分:3)

在您的属性设置器中,您应该检查新值是否等于旧值,如果是,则应该返回并且不触发PropertyChanged事件。

关于你的问题:

  1. 是的,这看起来不错。
  2. 设置中继命令有两种方法。我更喜欢

    private RelayCommand<Student> _addStudentCommand;
    public ICommand AddStudentCommand
    {
        get
        {
            return _addStudentCommand
                ?? (_addStudentCommand = new RelayCommand<Student>((student) =>
                    {
                         studentList.Add(student);
                    }));
        }
    }
    
  3. 另一种不传递学生对象的方法

    private RelayCommand _addStudentCommand;
        public ICommand AddStudentCommand
        {
            get
            {
                return _addStudentCommand
                    ?? (_addStudentCommand = new RelayCommand(() =>
                        {
                            Student student = new Student(); 
                            studentList.Add(student);
                        }));
            }
        }
    
    1. 这就是属性在.net中的工作方式,您可以使用自动属性,但由于您需要在setter中触发更改通知,因此您必须声明该属性将起作用的字段。
    2. 此外,由于您看起来使用的是mvvm light,因此您应该尝试使用代码段。它们使属性非常容易创建。键入mvvvminpc然后按Tab键两次。然后填写突出显示的部分并点击标签直到完成。

      您可以通过几种方式将View绑定到Viewmodel。我知道它是一个Antipattern,但你可以使用一个定位器。基本思想是将viewmodel设置为views datacontext。

      public class Locator
      {
         public Viewmodel1 Viewmodel1
          {
             return new Viewmodel1();
          }   
      }  
      

      然后你在app.xaml中添加了这个类

      <Application.Resources>
         <Locator x:key="VMLocator" />
      </Application.Resources>
      

      然后在xaml的视图中

      <Page  DataContext="{Binding Source="{StaticResource VMLocator}" Path=ViewModel1}">
      
      </Page>
      
相关问题