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();
        }

程序运行效果如下:

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

感谢您的阅读。

时间: 2024-12-21 19:26:04

WPF MVVM初体验的相关文章

&quot;xaml+cs&quot;桌面客户端跨平台初体验

"Xaml+C#"桌面客户端跨平台初体验 前言 ??随着 .Net 5的到来,微软在 .Net 跨平台路上又开始了一个更高的起点.回顾.Net Core近几年的成果,可谓是让.Net重生了一次. ??Asp .Net Core跨平台解决了Windows服务器昂贵的费用和不能长时间待机的问题,让Asp程序能够跑在Linux甚至Mac上.从博客园里.Net分类可以看到,每天都可以涌现大批Asp .Net Core的技术文章,越来越多的开发者或者公司开始尝试这个船新的跨平台框架. ??然鹅,

Xamarin.iOS开发初体验

Xamarin是一个跨平台开发框架,这一框架的特点是支持用C#开发IOS.Android.Windows Phone和Mac应用,这套框架底层是用Mono实现的. Mono是一款基于.NET框架的开源工程,包含C#语言编译器.CLR运行时和一组类库,能运行于Windows.Linux.Unix.Mac OS和Solaris.对于.NET程序员来说,Xamarin是走向安卓.iOS.Mac跨平台开发的神器,不仅能用熟悉的C#来开发,还能使用Visual Studio作为IDE.本文内容是Xamar

Knockout.js初体验

前不久在网上看到一个轻量级MVVM js类库叫Knockout.js,觉得很好奇,搜了一下Knockout.js相关资料,也初体验了一下,顿时感觉这个框架的设计很有意思.接下来就搞清楚什么是Knockout.js Knockout.js有4个重要的概念:(一定要牢记) 1.声明绑定:使用简明易读的语法很容易地将模型(model)数据关联到DOM元素上. 2.UI界面自动刷新:当您的模型状态(model state)改变时,您的UI界面将自动更新. 3.依赖跟踪:为转变和联合数据,在你的模型数据之

vue.js 初体验

Vue.js是什么? 一个构建数据驱动的web界面的库.他不是一个全能框架,技术上重点集中在MVVM中的ViewModel层. Vue.js特点? 轻巧.高性能.可组件化 官网地址:http://cn.vuejs.org/ Vue.js初体验 引入Vue.js独立版本, 至官网下载独立版本.根据提示,开发时选择开发版本. 直接引入Vue.js到静态页面中,从数据绑定开始编写DOM部分和js部分 <div id="app"> {{message}} </div>

【腾讯Bugly干货分享】基于 Webpack &amp; Vue &amp; Vue-Router 的 SPA 初体验

本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57d13a57132ff21c38110186 导语 最近这几年的前端圈子,由于戏台一般精彩纷呈,从 MVC 到 MVVM,你刚唱罢我登场. backbone,angularjs 已成昨日黄花,reactjs 如日中天,同时另一更轻量的 vue 发展势头更猛,尤其是即将 release 的2.0版本,号称兼具了 angularjs 和 reactjs 的两者优点.不过现在的官方

erlang 初体验

最近测试了一下 erlang的坑... 如不出意外.... 大家第一眼看到这语法... 心里第一句一定是"我擦.这TM都是啥!!!!!" 没有变量!!! 没有结构体!!! 没有循环!!! 好吧,至少我是这样想的. 找了半天..连个if也不知道怎么写.. 这记录一些基本常识.. -module(module_name)  %%定义模块 括号内的要和文件名相同. -export([fun1/1 fun2/2]) %%这里是导出2个函数对外使用  函数名/参数名. 一个简单的函数定义如下 f

linux初体验

第一次听到linux这个'词语'是在一次偶然的朋友聊天中朋友提到的,之前压根没听到过'这个东西',所以我可以说是个linux的新新手,菜鸟都不算. 截至到目前,我已经开始linux系统运维学习有差不多10天时间了.在没接触linux之前,我对它的认识仅仅是:它是个计算机系统.决定学习linux系统运维之前,自我以为运维应该是对系统的一些日常维护之类的,不会很难的东西,我更希望运维是个不难的东西,我个人很笨,对难的东西可能接受的很慢,所以我愿意认为运维是很简单的,这样我就可以轻轻松松的掌握运维相关

【Spark深入学习 -15】Spark Streaming前奏-Kafka初体验

----本节内容------- 1.Kafka基础概念 1.1 出世背景 1.2 基本原理 1.2.1.前置知识 1.2.2.架构和原理 1.2.3.基本概念 1.2.4.kafka特点 2.Kafka初体验 2.1 环境准备 2.2 Kafka小试牛刀 2.2.1单个broker初体验 2.2.2 多个broker初体验 2.3 Kafka分布式集群构建 2.3.1 Kafka分布式集群构建 2.3.2 Kafka主题创建 2.3.3 生产者生产数据 2.3.4消费者消费数据 2.3.5消息的

Java8初体验(二)Stream语法详解

原文链接:http://ifeve.com/stream/ 1. Stream初体验 我们先来看看Java里面是怎么定义Stream的: A sequence of elements supporting sequential and parallel aggregate operations. 我们来解读一下上面的那句话: Stream是元素的集合,这点让Stream看起来用些类似Iterator: 可以支持顺序和并行的对原Stream进行汇聚的操作: 大家可以把Stream当成一个高级版本的