C# WPF 数据绑定同步

WPF里有几个关于后台数据绑定的核心对象,但有些时候其使用方式却容易让人迷惑,甚至混淆。现在就来总结梳理一下,DataContext、Itemsource、Command、Binding、INotifyPropertyChanged、ObservableCollection。

首先从XAML上最简单的前台单向Binding开始:

<TextBlock Grid.Column="0" Text="{Binding Name}"  />

看着这个,就会思考,这个到底是与后台哪个对象中的Name绑定,而这就是DataContext。那么如果是{Binding},那就是与其DataContext绑定,而不是其上面的属性。这种最简单的Binding常存在于MVVM设计模式下,因为一般开发一个项目不会多种设计模式混用,仔细思考一下,常规怎么去设定这个TextBlock,是用x:Name 在后台代码中去设置其Text,那么这两种方式,其实就形成了不同的代码风格。还有一个问题是,{Binding Name}是省略的写法,真正完整的是{Binding Path=Name},Binding.Path在MSDN上的说明是与指定路径的属性绑定,而不是其字段,这也就解释了很多人很好奇的为何必须要绑定到对象的Property上去,而不能直接绑定到一个public的字段上去。

虽然上面说的是最简单的,却不是最常用的,像我就非常不喜欢MVVM这种开发模式,而就很少去设置DataContext然后Binding。因为作为客户端来说,界面是必不可少的,更多的是根据界面业务逻辑和接口去写代码,没有必要完完全全彻彻底底的去把前后台解耦。更多来说,无论开发还是维护一个项目,当很久之后去看,往往需要与界面结合去看,不然可能光看ViewModel连自己也不知道在干什么。而且每一个视图都要建个ViewModel将其绑定到DataContext,在成百上千个页面下,会显得额外繁琐。

我觉得更常见的是这种形式:

<Window.Resources>
        <DataTemplate x:Key="listBoxTemplate">
            <StackPanel Margin="4">
                <DockPanel>
                    <TextBlock FontWeight="Bold" Text="城市名称:" DockPanel.Dock="Left" Margin="5,0,10,0"/>
                    <TextBlock Text=" "/>
                    <TextBlock Text="{Binding ProvinceName}" Foreground="Green" FontWeight="Bold"/>
                </DockPanel>
                <DockPanel>
                    <TextBlock FontWeight="Bold" Text="创建日期:" DockPanel.Dock="Left" Margin="5,0,5,0"/>
                    <TextBlock Text=" "/>
                    <TextBlock Text="{Binding DateCreated}" Foreground="DarkOrange"/>
                </DockPanel>
                <DockPanel>
                    <TextBlock FontWeight="Bold" Text="更新日期:" DockPanel.Dock="Left" Margin="5,0,5,0"/>
                    <TextBlock Text=" "/>
                    <TextBlock Text="{Binding DateUpdated}" Foreground="Cyan"/>
                </DockPanel>
            </StackPanel>
        </DataTemplate>
    </Window.Resources>

<ListBox Margin="17,8,15,26" Name="listBox1" ItemsSource="{Binding Tables[0]}"
 ItemTemplate="{StaticResource listBoxTemplate}"/>

像DataGrid、ListBox等常用控件,就会经常使用到ItemSource去指定一个集合,然后做一个DataTemplate将集合中对象的属性Binding即可。那么现在仔细想想这个过程,Itemsource的处理到底做了什么。虽然我没读过.NET源码,但我认为比较合理的实现是,ItemSource在默认的DataSource中,然后动态管理控件,将其控件列表的每一项Binding到其集合上对应项的属性。那么,这样看来,ItemSource属于一种DataContext的高级实现。

 

接下来,又来另一个问题,如果Name的值改变了,那么此时应该通知视图做相应的更新,因为视图是没有办法去监视变量的,属于需要将对象实现INotifyPropertyChanged 在set里面做通知。相反,例如当属性Binding在TextBox上,当TextBox发生更改时,视图是能够直接对属性做出更改的,此时不需要任何更新。当然Binding可以指定Mode,来决定是否要执行这个更改。

  • 无论是目标属性还是源属性,只要发生了更改,TwoWay 就会更新目标属性或源属性。

  • OneWay 仅当源属性发生更改时更新目标属性。

  • OneTime 仅当应用程序启动时或 DataContext 进行更改时更新目标属性。

  • OneWayToSource 在目标属性更改时更新源属性。

  • Default:使用目标属性的默认 Mode 值。

此时,还有另外个问题,那就是ItemSource,当Binding一个集合时,集合中的对象实现了INotifyPropertyChanged,那么当对象发生改变时,能够通知到界面,但当集合自身元素发生改变时,又如何去通知?用List的话看起来似乎并没有什么特别好的办法,我们应当在集合的内部做视图通知,所以需要一个自身实现了INotifyPropertyChanged的集合ObservableCollection。

现在,还有最后个问题,当集合变为另外个集合时,又该如何通知?终归还是要回到原理上来,那么需要自己弄一个对象实现INotifyPropertyChanged并使其成为DataContext,把集合放进去并使其成为ItemSource,在这个集合变为另外个时同理实现相应的界面通知。

至此,数据绑定这块基本就结束了,从整体上来看,只要知其稍微下层一点的原理,按着由简到繁的逻辑去梳理枚举,还是很容易理解所有情况下对应的处理方法。

最后,再说下Command,这个在MVVM里用得挺多的,除此之外很少用到。主要是将事件以Binding Command的形式触发以达到解耦的目的,主要体现有一些内置的Command命令以及自定义类实现ICommand接口,然后实现Execute/CanExecute,传参使用CommandParameter,多个参数主流MultiBinding+Converter

 

 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值