浅谈WPF中的MVVM

浅谈WPF中的MVVM

一些基础知识

  1. WPF中的一个重要特性就是数据绑定(data binding),简单的说就是你有一些数据要显示给用户,你可以把数据和xaml进行绑定。
  2. WPF由2部分组成:描述GUI布局和效果的xaml文件,和xaml关联的cs文件。
  3. 如果你想最大程度上的复用你的代码,最好的方法就是使用MVVM(Model、View、ViewModel)模式,这样可以保证你的View部分包含的代码最少

一些关键点

  1. 你需要一个集合来保存数据,这个集合是ObservableCollection,而不能用List,Dictionary等。ObservableCollection的特点是在添加项、移除项或刷新整个列表时,此集合将提供通知
  2. 包括Window在内的所有的WPF控件都有一个DataContext
  3. 接口INotifyPropertyChanged是数据变化和GUI之间的桥梁

实例

再多的描述也不如一个实例更能够说明问题

我创建了一个Song类,用于存储一首歌曲中的信息,包括了歌曲名和歌手名

public class Song
{
    private string _songTitle;
    private string _artistName;

    public string SongTitle
    {
        get { return _songTitle; }
        set { _songTitle = value; }
    }

    public string ArtistName
    {
        get { return _artistName;}
        set { _artistName = value; }
    }
}

设置DataContext方法有两种:
在xaml中:

<Window.DataContext>
    <local:SongViewModel />
</Window.DataContext>

或者在cs文件中设置DataContext

public partial class MainWindow : Window
{
    SongViewModel _viewModel = new SongViewModel();
    public MainWindow()
    {
        InitializeComponent();
        base.DataContext = _viewModel;
    }
}

在WPF中,这个Song类就是Model,而GUI则是View,ViewModel把这2层之间的数据进行关联。
然后我们继续创建ViewModel层
这个ViewModel层需要继承INotifyPropertyChanged接口,这样这个类中的某个属性值发生变化,我们会接到通知。

public class SongViewModel : INotifyPropertyChanged
{
    private Song _song;

    public Song Song
    {
        get { return _song; }
        set { _song = value; }
    }

    public string ArtistName
    {
        get { return Song.ArtistName; }
        set
        {
            if (Song.ArtistName != value)
            {
                Song.ArtistName = value;
                RaisePropertyChanged("ArtistName");
            }
        }
    }

    public string SongTitle
    {
        get { return Song.SongTitle; }
        set
        {
            if (Song.SongTitle != value)
            {
                Song.SongTitle = value;
                RaisePropertyChanged("SongTitle");
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

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

上面的代码中,首先检查属性的值是否真的发生变化if (Song.SongTitle != value),这样做可以在复杂的项目中,稍微提升一下性能,如果属性发生变化了,会引发PropertyChanged事件。

现在Model,ViewModel都已经完成,只差View了。MainWindow.xaml文件如下

<Window x:Class="WVVM.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WVVM"
        mc:Ignorable="d"
        Title="MainWindow" Height="201.288" Width="380.794">
    <Window.DataContext>
        <local:SongViewModel></local:SongViewModel>
    </Window.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"></ColumnDefinition>
            <ColumnDefinition Width="Auto"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Label Grid.Column="0" Grid.Row="0" Content="SongTitle"></Label>
        <Label Grid.Column="0" Grid.Row="1" Content="Artist:"></Label>
        <Label Grid.Column="1" Grid.Row="1" Content="{Binding ArtistName}"></Label>
        <Label Grid.Column="1" Grid.Row="0" Content="{Binding SongTitle}"></Label>
        <Button Grid.Column="0" Grid.Row="2" Name="BtUpdate" Content="Update" Click="BtUpdate_OnClick"></Button>
    </Grid>
</Window>

这里写图片描述
这里写图片描述
xaml文件中,Button按钮有一个click事件,在这个事件中更新了viewMode的属性。

CS文件代码如下

    private SongViewModel _viewModel;
    public MainWindow()
    {
        InitializeComponent();
        _viewModel = (SongViewModel) base.DataContext;
    }

    private void BtUpdate_OnClick(object sender, RoutedEventArgs e)
    {
        _viewModel.ArtistName = "Adele";
        _viewModel.SongTitle = "Hello";
    }

像上文那样直接在GUI文件中绑定事件(Button的click事件),存在很多问题,WPF提供了一个更好的方法那就是ICommand。许多控件都有Command属性,它的绑定和Content,ItemSource的绑定方式相同。
继承ICommand需要实现2个方法:bool CanExecute(用于判断是否能够执行)和void Execute(在这里写你需要的代码,如更改SongTitle等)。

我建立了一个RelayCommand类来实现ICommand接口,代码如下

 public class RelayCommand :ICommand
    {
        private readonly Func<Boolean> _canExecute;
        private readonly Action _execute;

        public RelayCommand(Action execute) : this(execute,null)
        {

        }

        public RelayCommand(Action execute, Func<Boolean> canExecute)
        {
            if(execute == null)
                throw new ArgumentNullException("execute");
            _execute = execute;
            _canExecute = canExecute;
        }

        public event EventHandler CanExecuteChanged
        {
            add
            {
                if (_canExecute != null)
                    CommandManager.RequerySuggested += value;
            }
            remove
            {
                if (_canExecute != null)
                    CommandManager.RequerySuggested -= value;
            }
        }

        [DebuggerStepThrough]
        public Boolean CanExecute(Object parameter)
        {
            return _canExecute == null ? true : _canExecute();
        }

        public void Execute(Object parameter)
        {
            _execute();
        }

    }

相应的,在SongViewModel类中,增加如下代码

        void UpdateSongExecute()
        {
            //在这里执行更新数据的操作
            ArtistName = "One Direction";
            SongTitle = "Taken";
        }

        bool CanUpdateSongExecute()
        {
            //在这里检查是否可以更新数据,我直接返回true了
            return true;
        }

        public ICommand UpdateSong
        {
            get { return new RelayCommand(UpdateSongExecute,CanUpdateSongExecute);}
        }

此时,MainWindow.xaml文件中的Button,就不需要写click事件了,只需绑定Command即可

 <Button Grid.Column="0" Grid.Row="2" Name="BtUpdate" Content="Update" Command="{Binding UpdateSong}"></Button>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值