MVVM的特点之一是实现数据同步,即,前台页面修改了数据,后台的数据会同步更新。

上一篇我们已经一起编写了框架的基础结构,并且实现了ViewModel反向控制Xaml窗体。

那么现在就要开始实现数据同步了。

DataContext—数据上下文

在实现数据同步前,我们要了解一个知识点——DataContext。

WPF中每个UI都有一个Content和一个DataContext,那么Content和DataContext是什么呢?

Content:Content是指页面内容,即我们编写的代码,或者认为它是展示的UI。

打个比方,Content就是HTML页面中的标签,如【<html></html】;那么,在WPF中Content是指的就是Xaml页面的标签了。

DataContext:DataContext是指页面中的数据内容,这部分内容只有运行了才存在,用过ASP.NET MVC的同学可以把它理解为MVC中的Model。(每个页面都有一个唯一的指定Model)

既然在WPF里DataContext就是MVC中的Model。那么,自然的,DataContext就要存储页面的ViewModel了,所以,我们为它赋值它自身对应的ViewModel。

现在,找到我们的BaseViewModel的构造函数,加入这行代码[UIElement.DataContext = this;],代码如下:

public BaseViewModel()
{
WindowMain = Application.Current.MainWindow;
SetUIElement();
UIElement.DataContext = this;
}

这样用ViewModel创建的页面的DataContext就被自动赋值了。

页面与ViewModel的基础关系就建立完成了。

Binding—绑定

在我们编写的框架中,绑定分两种,一种是属性绑定,一种是命令绑定。

属性绑定:属性绑定很好理解,就是将Xaml页面的控件属性和ViewModel中的自定义属性捆绑到一起,让他们的数据值同步。

命令绑定:命令绑定是Xaml页面触发命令,然后由ViewModel来处理命令。

这里的命令(Command)有点不太好理解,不过大家都做过面向事件的开发,我们可以把命令想象成事件,就是Xaml页面触发事件,ViewModel来执行事件内容。

接下来,我们一起做一些简单的绑定。

Property—属性绑定

首先,在程序框架中找到VM_WindowMain页面,然后在里面创建属性HeaderName,代码如下:

public string _HeaderName = "HeaderName_KibaFramework";
public string HeaderName { get { return _HeaderName; } set { _HeaderName = value; OnPropertyChanged(); } }

然后,我们再找到VM对应的Xam页面—WindowMain.xaml,修改Header代码如下:

<StackPanel  DockPanel.Dock="Top" Background="Gainsboro">
<TextBlock TextAlignment="Left" Text="{Binding HeaderName}" Margin="20,20,0,0" Height="70" FontSize="36"></TextBlock>
</StackPanel>

界面效果如下:

通过图片,我们可以看到,属性已经绑定成功了,并且成功输出了我们的HeaderName。

然后,我们重点看一下这段代码{Binding HeaderName}。

这句话的意思就是让TextBlock的Text属性绑定HeaderName属性,其中Binding就是绑定的意思。【注意,这里只能是属性绑定属性】

HeaderName是我们在VM中刚刚定义的属性,那么Text是怎么绑定到了HeaderName上的呢?

很简单,因为上面我们已经把ViewModel赋值到了DataContext中了,所以在Xaml中,我们就可以使用{Binding 属性名}这样的语句,来绑定VM中所有的属性。

在Xaml中,TextBlock默认的绑定是单向绑定,就是说,VM中的属性值改变会同步Xaml页面的属性值,让其改变;但,当Xaml页面的属性值改变了,VM中的属性值却不会改变。

那么如何让他们同步呢?

很简单,只需要在绑定的时候多加一个属性Mode=TwoWay即可,代码如下:

{Binding HeaderName,Mode=TwoWay}

Command—命令绑定

在MVVM中,事件被极大的程度的弱化了,因为Command在ViewModel中替代了事件来处理业务逻辑,所以,事件在框架中就只负责处理UI变化这么一件事了。

BaseCommand

在WPF中,系统为我们提供一些Command,但为了能处理更多细节,自定义Command的效果会更好,所以,我们需要编写属于我们框架自己的自定义BaseCommand。

代码如下:

public class BaseCommand : ICommand
{
public Action<object> ExecuteAction;
public BaseCommand(Action<object> action)
{
ExecuteAction = action;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
ExecuteAction(parameter);
}
}

如上代码所示,我们自定义了BaseCommand,并且继承了ICommand接口,实现了接口方法。

Command的应用

下面我们开始Command的基础应用,使用Command实现页面切换;页面切换我们采用最简单的模式Window—Frame—Page的控制模式。

首先我们找到VM_WindowMain,创建切换Page的Command和存储页面实例的属性FrameSource。

代码如下:

public Page _FrameSource;
public Page FrameSource { get { return _FrameSource; } set { _FrameSource = value; OnPropertyChanged(); } }
public BaseCommand ChangeFrameSourceCommand
{
get
{
return new BaseCommand(ChangeFrameSourceCommand_Executed);
}
}
public void ChangeFrameSourceCommand_Executed(object obj)
{
string pageName = obj.ToString();
switch(pageName)
{
case "PageMain":
FrameSource = new VM_PageMain().UIElement as Page;
break;
case "PageUser":
FrameSource = new VM_PageUser().UIElement as Page;
break;
}
}

接下来在页面实现按钮事件绑定和Frame显示页面绑定。

代码如下:

<TreeViewItem>
<TreeViewItem.Template>
<ControlTemplate>
<Button HorizontalAlignment="Left" Content="PageMain" Command="{Binding ChangeFrameSourceCommand}" CommandParameter="PageMain" Style="{StaticResource NullButton}"></Button>
</ControlTemplate>
</TreeViewItem.Template>
</TreeViewItem>
<TreeViewItem>
<TreeViewItem.Template>
<ControlTemplate>
<Button HorizontalAlignment="Left" Content="PageUser" Command="{Binding ChangeFrameSourceCommand}" CommandParameter="PageUser" Style="{StaticResource NullButton}"></Button>
</ControlTemplate>
</TreeViewItem.Template>
</TreeViewItem> /* 省略了框架其他元素代码 */ <Frame x:Name="frameMain" Content="{Binding FrameSource,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" NavigationUIVisibility="Hidden" ScrollViewer.CanContentScroll="True" ></Frame>

从代码中我们可以看到,VM中的属性FrameSource绑定到了页面Frame的Content属性上。

由于TreeViewItem没有Command的依赖属性,所以我们修改了他的模板,然后用模板内的Button的Command属性绑定了VM中的ChangeFrameSourceCommand属性。

因为ChangeFrameSourceCommand是BaseCommand类型,所以,当按钮被按下时,就会触发ChangeFrameSourceCommand定义的执行命令——ChangeFrameSourceCommand_Executed。

这样我们就实现了框架内的页面切换了。

----------------------------------------------------------------------------------------------------

到此,我们框架的基础功能就已经实现了。

但如果框架只写到这里,那ViewModel对页面的掌控力度就显的太弱了。

而且项目框架不能仅仅考虑结构分离和业务独立,我们还要降低使用难度和提高使用者的开发效率。

所以为了更好的掌控UI,降低开发者的门槛,我们还需要编写数据控件,让开发者在不能熟练掌握Xaml样式的情况下,依然可以顺利完成开发。

那么,本篇文章就先讲到这了,下一篇文章我们将一起为框架编写数据控件,敬请期待。

框架代码已经传到Github上了,并且会持续更新。

相关文章:

【我们一起写框架】MVVM的WPF框架(一)—序篇

To be continued

Github地址:https://github.com/kiba518/KibaFramework

----------------------------------------------------------------------------------------------------

注:此文章为原创,任何形式的转载都请联系作者获得授权并注明出处!
若您觉得这篇文章还不错,请点击下方的【推荐】,非常感谢!

【我们一起写框架】MVVM的WPF框架(二)—绑定的更多相关文章

  1. 【我们一起写框架】MVVM的WPF框架(五)—完结篇

    前言 这篇文章是WPF框架系列的最后一篇,在这里我想阐述一下我对框架设计的理解. 我对框架设计的理解是这样的: 框架设计不应该局限于任何一种设计模式,我们在设计框架时,应该将设计模式揉碎,再重组:这样 ...

  2. 【我们一起写框架】MVVM的WPF框架(一)—序篇

    前言 我想,有一部分程序员应该是在二三线城市的,虽然不知道占比,但想来应该不在少数. 我是这部分人群中的一份子. 我们这群人,面对的客户,大多是国内中小企业,或者政府的小部门.这类客户的特点是,资金有 ...

  3. 【我们一起写框架】MVVM的WPF框架(三)—数据控件

    这世上,没人能一次性写出完美无缺的框架:因为,任何一个框架都需要项目的淬炼,然后才能升华,趋近完美. 所以,框架是个反复修改的东西,最终形成的东西. 如果你学了一点技术,觉得自己可以写出框架了,觉得自 ...

  4. 【我们一起写框架】MVVM的WPF框架(四)—DataGrid

    前言 这个框架写到这里,应该有很多同学发现,框架很多地方的细节,其实是违背了MVVM的设计逻辑的. 没错,它的确是违背了. 但为什么明知道违背设计逻辑,还要这样编写框架呢? 那是因为,我们编写的是框架 ...

  5. Jquery如何序列化form表单数据为JSON对象 C# ADO.NET中设置Like模糊查询的参数 从客户端出现小于等于公式符号引发检测到有潜在危险的Request.Form 值 jquery调用iframe里面的方法 Js根据Ip地址自动判断是哪个城市 【我们一起写框架】MVVM的WPF框架(三)—数据控件 设计模式之简单工厂模式(C#语言描述)

    jquery提供的serialize方法能够实现. $("#searchForm").serialize();但是,观察输出的信息,发现serialize()方法做的是将表单中的数 ...

  6. WPF Prism框架下基于MVVM模式的命令、绑定、事件

    Prism框架下的自定义路由事件和命令绑定 BaseCode XAML代码: <Button x:Class="IM.UI.CommandEx.PrismCommandEx" ...

  7. 剖析手写Vue,你也可以手写一个MVVM框架

    剖析手写Vue,你也可以手写一个MVVM框架# 邮箱:563995050@qq.com github: https://github.com/xiaoqiuxiong 作者:肖秋雄(eddy) 温馨提 ...

  8. win10 uwp MVVM 轻量框架

    如果在开发过程,遇到多个页面之间,需要传输信息,那么可能遇到设计的问题.如果因为一个页面内包含多个子页面和多个子页面之间的通信问题找不到一个好的解决方法,那么请看本文.如果因为ViewModel代码越 ...

  9. 基于xlua和mvvm的unity框架

    1.框架简介 这两天在Github上发现了xlua的作者车雄生前辈开源的一个框架—XUUI,于是下载下来学习了一下.XUUI基于xlua,又借鉴了mvvm的设计概念.xlua是目前很火的unity热更 ...

随机推荐

  1. 从构建分布式秒杀系统聊聊Disruptor高性能队列

    前言 秒杀架构持续优化中,基于自身认知不足之处在所难免,也请大家指正,共同进步.文章标题来自码友 简介 LMAX Disruptor是一个高性能的线程间消息库.它源于LMAX对并发性,性能和非阻塞算法 ...

  2. 在linux服务器之间复制文件和目录命令scp

    scp是secure copy的简写,用于在Linux下进行远程拷贝文件的命令,和它类似的命令有cp,不过cp只是在本机进行拷贝不能跨服务器,而且scp传输是加密的.可能会稍微影响一下速度.当你服务器 ...

  3. 前端基础-CSS的各种选择器的特点以及CSS的三大特性

    一. 基本选择器 二. 后代选择器.子元素选择器 三. 兄弟选择器 四. 交集选择器与并集选择器 五. 序列选择器 六. 属性选择器 七. 伪类选择器 八. 伪元素选择器 九. CSS三大特性 一. ...

  4. 我的Python之旅第四天

    一 名称空间.作用域.取值顺序 1 名称空间 当程序运行时,代码从上至下依次执行,它会将变量与值得关系存储在一个空间中,这个空间就叫做名称空间,也叫命名空间.全局名称空间. 当程序遇到函数时,他会将函 ...

  5. H5 新特性之 fileReader 实现本地图片视频资源的预览

    大家好 !!  又见面了, 今天我们来搞一搞   H5的新增API    FileReader     真是一个超级超级方便的API呢!!!很多场景都可以使用.......... 我们先不赘述MDN文 ...

  6. Emmagee--APP性能测试工具的基本使用

    一.Emmagee介绍 Emmagee是监控指定被测应用在使用过程中占用机器的CPU.内存.流量资源的性能测试小工具.该工具的优势在于如同windows系统性能监视器类似,它提供的是数据采集的功能,而 ...

  7. 利用策略模式优化过多 if else 代码

    前言 不出意外,这应该是年前最后一次分享,本次来一点实际开发中会用到的小技巧. 比如平时大家是否都会写类似这样的代码: if(a){ //dosomething }else if(b){ //dosh ...

  8. ES 17 - (底层原理) Elasticsearch增删改查索引数据的过程

    目录 1 增删改document的流程 1.1 协调节点 - Coordinating Node 1.2 增删改document的流程 2 查询document的流程 1 增删改document的流程 ...

  9. 安装Mysql时端口号3306被占用,解决方法

    当我们在卸载mysql数据库重新安装的时候,会出现端口号3306被占用的情况 有两种解决方案: 一:可以不使用3306端口,也可以换成别的端口,如3307,3308等等 二:可以打开命令窗口 1.wi ...

  10. SQL Server内幕之数据页

    数据页是包含已添加到数据库表中的用户数据的结构. 如前所述, 数据页有三种, 每个都以不同的格式存储数据. SQL server 有行内数据页.行溢出数据页和 LOB 数据页. 与 SQL serve ...