欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

【我们一起写框架】MVVM的WPF框架(二)—绑定

程序员文章站 2022-04-25 18:16:53
MVVM的特点之一是实现数据同步,即,前台页面修改了数据,后台的数据会同步更新。 上一篇我们已经一起编写了框架的基础结构,并且实现了ViewModel反向控制Xaml窗体。 那么现在就要开始实现数据同步了。 DataContext—数据上下文 在实现数据同步前,我们要了解一个知识点——DataCon ......

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>

界面效果如下:

【我们一起写框架】MVVM的WPF框架(二)—绑定

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

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

这句话的意思就是让textblock的text属性绑定headername属性,其中binding就是绑定的意思。【注意,这里只能是属性绑定属性】

headername是我们在vm中刚刚定义的属性,那么text是怎么绑定到了headername上的呢?

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

在xaml中,默认的绑定是单向绑定,就是说,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框架(二)—绑定