WPF MVVM初体验

Posted 王超元

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WPF MVVM初体验相关的知识,希望对你有一定的参考价值。

首先MVVM设计模式的结构,

 

Views: 由Window/Page/UserControl等构成,通过DataBinding与ViewModels建立关联;

ViewModels:由一组命令,可以绑定的属性,操作逻辑构成;因为View与ViewModel进行了解耦,我们可以对ViewModel进行Unit Test;

Models:可以是实体对象或者Web服务;

下面通过一个简单的例子,来介绍一些WPF MVVM模式。示例将展示一个图片浏览器,打开图片,放大/缩小图片大小。首先项目结构:

UI:

复制代码
    <Grid>
        <DockPanel>
            <Menu DockPanel.Dock="Top">
                <Menu>
                    <MenuItem Header="_Open" Command="{Binding OpenFileCommand}"/>
                </Menu>
                <Menu>
                    <MenuItem Header="_ZoomIn" Command="{Binding ZoomCommand}" CommandParameter="ZoomIn"/>
                </Menu>
                <Menu>
                    <MenuItem Header="_ZoomOut" Command="{Binding ZoomCommand}" CommandParameter="ZoomOut"/>
                </Menu>
                <Menu>
                    <MenuItem Header="_Normal" Command="{Binding ZoomCommand}" CommandParameter="Normal"/>
                </Menu>
            </Menu>
            <ScrollViewer>
                <Image Source="{Binding ImagePath}" Stretch="None">
                    <Image.LayoutTransform>
                        <ScaleTransform ScaleX="{Binding Zoom}" ScaleY="{Binding Zoom}"/>
                    </Image.LayoutTransform>
                </Image>
            </ScrollViewer>
        </DockPanel>
    </Grid>
复制代码

ViewModelBase(用来实现修改通知):

复制代码
    public class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propName)
        {
            if(PropertyChanged!=null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
            }
        }
    }
复制代码

OpenFileCommand:

复制代码
public class OpenFileCommand : ICommand
    {
        private MainViewModel _data;
        public OpenFileCommand(MainViewModel data)
        {
            _data = data;
        }

        public event EventHandler CanExecuteChanged;

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public void Execute(object parameter)
        {
            OpenFileDialog dialog = new OpenFileDialog() { Filter = "Image Files|*.jpg;*.png;*.bmp;*.gif" };

            if(dialog.ShowDialog().GetValueOrDefault())
            {
                _data.ImagePath = dialog.FileName;
            }
        }
复制代码

ZoomCommand:

复制代码
    public enum ZoomType
    {
        ZoomIn = 0,
        ZoomOut = 1,
        Normal = 2
    }

    public class ZoomCommand : ICommand
    {
        private MainViewModel _data;

        public ZoomCommand(MainViewModel data)
        {
            _data = data;
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public bool CanExecute(object parameter)
        {
            return _data.ImagePath != null;
        }

        public void Execute(object parameter)
        {
            ZoomType type = (ZoomType)Enum.Parse(typeof(ZoomType), (string)parameter, true);

            switch(type)
            {
                case ZoomType.Normal:
                    _data.Zoom = 1;
                    break;
                case ZoomType.ZoomIn:
                    _data.Zoom *= 1.2;
                    break;
                case ZoomType.ZoomOut:
                    _data.Zoom /= 1.2;
                    break;
            }
        }
    }
复制代码

MainViewModel:

复制代码
public class MainViewModel : ViewModelBase
    {
        private string _imagePath;

        public string ImagePath
        {
            get
            {
                return _imagePath;
            }
            set
            {
                if (_imagePath != value)
                {
                    _imagePath = value;
                    OnPropertyChanged("ImagePath");
                }
            }
        }

        private double _zoom = 1.0;

        public double Zoom
        {
            get
            {
                return _zoom;
            }
            set
            {
                if(_zoom != value)
                {
                    _zoom = value;
                    OnPropertyChanged("Zoom");
                }
            }
        }

        private ICommand _openFileCommand;

        public ICommand OpenFileCommand
        {
            get { return _openFileCommand; }
        }

        private ZoomCommand _zoomCommand;

        public ZoomCommand ZoomCommand
        {
            get { return _zoomCommand; }
        }

        public MainViewModel()
        {
            _openFileCommand = new OpenFileCommand(this);
            _zoomCommand = new ZoomCommand(this);
        }
    }
复制代码


下一步我们要做的是将MainViewModel绑定到MainWindow上,我们可以通过下面两种方式绑定:
1. 直接在MainWindow的Code Behind中进行绑定:

        public MainWindow()
        {
            InitializeComponent();

            DataContext = new MainViewModel();
        }

2. 在App.xaml后台代码中绑定(将App.xaml中StartupUri="MainWindow.xaml"删除掉):

        public App()
        {
            MainWindow window = new MainWindow();
            window.DataContext = new MainViewModel();
            window.Show();
        }

程序运行效果如下:

到此为止,这个简单的示例就算完成了。点击这里下载代码。

感谢您的阅读。

以上是关于WPF MVVM初体验的主要内容,如果未能解决你的问题,请参考以下文章

vs code初体验

vue.js 初体验

MVVM Light:在 XAML 中添加 EventToCommand 而不使用 Blend、更简单的方法或片段?

MVVM 架构 WPF

基于 Webpack & Vue & Vue-Router 的 SPA 初体验

MVVM 一种新型架构框架