Xamarin.Forms Data Binding介绍

很多时候我们需要将某个View1 值的改变显示在另外一个View2 上,对View1的对应事件编码可实现我们想要的效果,如果只是想处理值的改变,可以通过连接两个View的对应属性即可,称为Data Binding。Data Binding在Model-View-ViewModel (MVVM)设计模式中起着重要作用。
Data Binding中设计两个概念SourceTarget。当Source的值发生改变时Data Binding会自动将这个新的值更新到Target。对Target和Source有特殊要求,Target必须继承BindableProperty类(VisualElement通过继承Element继承了BindableObject,所以Xamarin.Forms中视图的大部分属性都是BindableProperty类型),Source必须实现INotifyPropertyChanged接口提供一种通知机制监听Source值的改变(BindableObject实现了INotifyPropertyChanged接口)。

简单的Data Binding使用

本示例以Slider的Value属性作Source,Label的Opacity属性作Target,实现拖动滑块影响Label透明度的效果。

代码方式设置Data Binding:

核心代码设置Target对象的BindingContext属性(BindableObject类型)。再调用Target 对象的SetBinding方法设置绑定属性关系,第一个参数targetProperty为BindableProperty类型,表示目标属性。第二个参数string类型,表示BindingContext的哪个属性为Source。本例调用的是5个参数的方法,后三个参数为默认值。

代码运行效果:

XAML方式设置Data Binding:

<StackLayout>
     <Label Text="Opacity Binding Demo"
            FontSize="Large"
            VerticalOptions="CenterAndExpand"
            HorizontalOptions="Center"
            BindingContext="{x:Reference Name=slider}"
            Opacity="{Binding Path=Value}" />

     <Slider x:Name="slider" VerticalOptions="CenterAndExpand" />
</StackLayout>

查看Label定义,BindingContext属性通过x:Reference指定,Opacity为目标属性通过Binding扩展标记的Path设置。Path不仅可以是Property也可以是SubProperty或 Indexer.如Content.Children[4].Value.

在整个视图树中子View是会继承父布局的BindingContext属性。如子View没有单独设置BindingContext属性,会查找上级视图若发现BindingContext赋值会直接继承,如果上级视图同样没有BindingContext赋值且存在上级视图会继续搜索上级视图BindingContext的赋值。本例修改XAML布局代码将Label的BindingContext删除添加到StackLayout中,同样会实现我们想要的效果。

<StackLayout BindingContext="{x:Reference Name=slider}">
     <Label Text="Opacity Binding Demo"
            FontSize="Large"
            VerticalOptions="CenterAndExpand"
            HorizontalOptions="Center"
            Opacity="{Binding Path=Value}" />

     <Slider x:Name="slider" VerticalOptions="CenterAndExpand" />
</StackLayout>

同样可以使用属性节点定义方式设置Data Binding相关属性

<Label Text="Opacity Binding Demo"
       FontSize="Large"
       VerticalOptions="CenterAndExpand"
       HorizontalOptions="Center">
       <Label.BindingContext>
            <x:Reference Name="slider" />
        </Label.BindingContext>
    <Label.Opacity>
            <Binding Path="Value" />
        </Label.Opacity>
</Label>

Reference对应的C#类为ReferenceExtension,Binding对应的类为BindingExtension。两个类的定义都指定了Content Property,分别为Name和Path,所以可以简化代码:

BindingContext="{x:Reference slider}"
Opacity="{Binding Value}" 

前面是通过BindingContext指定Data Binding的Source,还可以通过Binding指定Source。对应的C#代码为SetBinding两个参数的方法:

BindingBase为abstract类,Forms提供了Binding类该类继承了BindingBase。
通过Binding指定Source,再将Binding对象作为参数传入SetBinding方法。

Binding 提供了重载的构造函数和静态方法Create<TSource>来创建Binding对象,不作介绍。


构造函数示例

Create<TSource>示例

同样XAML定义方式为,删除BindingContext属性赋值,修改Binding扩展标记。

根据内容属性简化XAML代码:

Opacity="{Binding Value , Source={x:Reference slider}}"

扩展标记定义在一对大括号内且大括号内不应出现双引号,指定多个属性值时通过逗号分隔。
关于内容属性定义的简化写法《Creating Mobile Apps with Xamarin.Forms》中有提到“Even though BindingExtension defines Path as its content property, the argument name can be eliminated only when that argument is the first among multiple arguments.”大概意思是要省略内容属性的参数名称必须将其放在第一个参数,但是测试发现Opacity="{Binding Source={x:Reference slider} , Value}"这种写法同样可以。

那么问题来了,如果我们给BindingContext赋值的同时也为Binding的Source赋值,应该将哪个属性对应的对象作为数据源。符合就近原则Source的优先级高于BindingContext,即指定Source时不在考虑BindingContext。且Source使用更加灵活,如一个对象的多个属性使用不同对象作为数据源只能通过Source方式指定。


Binding Mode 介绍

现在要通过Data Binding实现两个Slider的Value相互影响。滑动一个Slider的同时另一个有相同变化。

愚蠢的办法是分别将两个Slider作为另一个的Source,即同时为两个Slider设置Data Binding。上一个Slider和Label的示例可以理解为Source影响Target,最简单的办法就是可以使Source和Target相互影响。BindingMode枚举可以帮助我们定义target 和 source之间的绑定模式。
BindingMode有四个枚举值:
• Default
• OneWay — Source 的改变影响Target的值(通常是这种情况).
• OneWayToSource — Target的改变影响Source的值.
• TwoWay — Source和Target值改变会相互影响.

对于可读写的BindableProperty对象默认BindingMode为OneWay,只读的BindableProperty对象默认BindingMode为OneWayToSource。
大多数BindableProperty对象BindingMode默认值为OneWay,以下控件的Property的BindingMode方式默认是TwoWay:

由于Slider的Value默认BindingMode为TwoWay,所以实现两个Slider连动XAMlL定义为:

<StackLayout>
    <Slider x:Name="slider" VerticalOptions="CenterAndExpand" />
    <Slider BindingContext="{x:Reference Name=slider}" Value="{Binding Path=Value}" VerticalOptions="CenterAndExpand" />
</StackLayout>

通过XAML明确指定BindingMode的值Value="{Binding Path=Value Mode=TwoWay}"。通过C#代码指定BindingMode的值slider.SetBinding(Slider.ValueProperty,"Value",BindingMode.TwoWay);


Binding StringFormat 介绍

再次把Slider作为Source,Label作为Target。将Slider的Value值绑定到Label的Text。Value值ToString后直接显示到Label上可能不是我们期望的,Binding类提供了StringFormat属性表示.NET格式化字符串。

StringFormat效果

XAML中StringFormat使用,因为StringFormat本身会包含一对大括号,所以StringFormat赋值时要包含一对单引号:

C#代码设置StringFormat:

label.SetBinding(Label.TextProperty, "Value", stringFormat: "Slider Value Is {0:F3}");

Binding IValueConvert 介绍

目前示例Target需要的数据为string,默认转换或StringFormat可以实现效果。但是Data Binding的Target接受数据类型为一个对象时如何处理?我们可以通过value converter类完成Source到Target的类型转换,需要实现IValueConverter接口,接口有ConvertConvertBack两个方法。

IValueConverter定义

当数据由Source转换到Target时调用Convert方法。Convert方法中value表示Source传递的值,你可以通过GetType来确定它的类型,也可以默认一种类型来处理。targetType表示Target需要的数据类型,Convert方法返回的类型应与targetType相同。parameter在Binding 中会用到,culture为CultureInfo类型需要和地域文化相关的转换时会用到。

ConvertBack方法会在数据由Target转换到Source 时调用,只有Binding Mode为TwoWay 或者 OneWayToSource时才有必要实现该方法,否则直接返回null即可。value参数表示target传递的值,targetType表示source的Type类型。

示例实现效果,一个Entry 和一个Button,当Entry中内容为空时Button不可用,点击Button清空Entry。

定义IntToBoolConverter实现IValueConverter,本示例中Entry为Source,Entry的Text.Length为Path,Button为Target,IsEnabled为绑定的属性。所以自定义的Converter应有Int转换为bool的能力(Convert方法,本例中ConvertBack可直接返回null)。

IntToBoolConverter类定义

XAML中使用Converter要先在Resources字典中定义IntToBoolConverter对象,指定key值,在Binding时通过StaticResource赋值。其中Resources相关内容在Style中介绍。

XAML使用IValueConverter

如果Converter只使用一次,不必在Resources中定义,直接在Binding中通过属性节点定义即可。


《Creating Mobile Apps with Xamarin.Forms》中提供了一个bool转泛型的Converter类,可以将bool值转换为我们想要的值。定义如下:

BoolToObjectConverter定义

在XAML定义时,通过 x:TypeArguments指定我们需要的类型,并设置TrueObject和FalseObject属性。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容