首页 文章

MVVM创建ViewModel

提问于
浏览
2

有人可以向我解释如何为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之间的绑定有一些疑问:使用这种类型的绑定有哪些优缺点?如果我要使用数据库,最好的绑定方式是什么?

  • 这是ViewModel和Model的样子

  • 如何创建RelayCommand以将学生添加到ObservableCollection

  • 为什么我们先私下设置,然后再公开[回答]

  • 如何将View绑定到ViewModel和Vice Versa

1 回答

  • 3

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

    至于你的问题:

    • 是的,看起来很好 .

    • 有几种方法可以设置继电器命令 . 我更喜欢

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

    没有传递学生对象的另一种方式

    private RelayCommand _addStudentCommand;
        public ICommand AddStudentCommand
        {
            get
            {
                return _addStudentCommand
                    ?? (_addStudentCommand = new RelayCommand(() =>
                        {
                            Student student = new Student(); 
                            studentList.Add(student);
                        }));
            }
        }
    
    • 这就是属性在.net中的工作方式,您可以使用自动属性,但由于您需要在setter中触发更改通知,因此您必须声明该属性将起作用的字段 .

    此外,因为看起来你正在使用mvvm灯,你应该尝试代码片段 . 它们使属性非常容易创建 . 键入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>
    

相关问题