SilverLight 5 数据绑定的高级话题

SilverLight 5 数据绑定的高级话题,第1张

概述编者注:本文译自Chirs Anderson的《Pro Business Applications With Silverlight 5》第11章,本人有意将全书译出,希望与有识之士合作,力求最终出版此书。平心而论,这本书无论从布局谋篇还是示例都很到位,是一本不可多得的深入学习SIlverlight5知识的教科书。 由于本章较长,计划分3个部分发表。今天发表第一部分。对于文中提到的诸如“第X章”,

编者注:本文译自Chirs Anderson的《Pro Business Applications With Silverlight 5》第11章,本人有意将全书译出,希望与有识之士合作,力求最终出版此书。平心而论,这本书无论从布局谋篇还是示例都很到位,是一本不可多得的深入学习SIlverlight5知识的教科书。

由于本章较长,计划分3个部分发表。今天发表第一部分。对于文中提到的诸如“第X章”,“如您所知”等字样,是与书中上下文相关的内容,在此不作过多解释。

有关字体的说明:

标题一律采用“标题4”,正文一律采用“normal”,注解一律采用上下水平线分割。

 

至此,您已经掌握如何将UI控件与对象进行绑定以使用户可以查看和编辑这些对象所暴露的数据。数据不是被推进控件的,而是通过XMAL提供的绑定将数据拉进控件里。换言之,绑定是控件读取数据的过程。在此前几章里已经介绍了如下信息:

l 将数据对象分配到控件的DataContext属性;

有三种不同的绑定模式:OneTime,OneWayTowWay(见第二章)。如果需要更新绑定源对象的属性要设置modeTowWay;

当绑定属性值发生变化时,可以通过实现InotifyPropertyChanged接口作出通知;

当绑定属性值(或被绑定对象本身)无效导致异常时也可以通过实现IDataErrorInfoINotifyDataErrorInfo接口作出通知;

ObservableCollection<T>类型可以用于维护集合中的项目,当项目发生增减时可以自动通知绑定的UI控件,从而自动进行更新。另外,也可以在集合类中实现INotifyCollectionChanged接口实现同样的行为;

可以封装Collection VIEw集合的视图,使得集合中的数据可以进行 *** 控(即过滤,排序,分组和分页)而无需要修改其依赖的集合。Collection VIEw还提供了一个当前记录的指针用于跟踪集合中的项目,使得多个控件可以绑定到同一Collection VIEw以保持同步;

为了有效开发Silverlight业务应用程序,需要清晰理解数据绑定机制。本章,我们来看看一些高级数据绑定特征,在此通过一些话题的展开,充分领略Sliverlight数据绑定引擎的强大之处。

分配绑定源

在第2章提到数据绑定时,已经指出数据绑定即要有source(下称源)又要有target(下称目标),绑定源来自于被绑定控件的DataContext属性。分配给DataContext属性的对象向下继承直到对象的每个层次,因此分配给GrID控件的DataContext属性,完全可以应用于该GrID控件包含的所有控件。

但是,如果你想要一个控件的属性绑定到控件的DataContext属性以外的属性时,例如资源,或另一个控件的某个属性,那么该怎样做呢?下面来看看。

 

使用绑定的Source属性

     设置绑定的Source属性,需要使用对象作为绑定的source而不是将对象分配给控件的DataContext属性。如果在XAML中实现,一般采取使用StaticResource标记扩展
 绑定到资源这种方式

     假定有一个Products对象被定义为资源,使用productResource作为其key。(“定义和实例化一个类作为资源用于绑定”在本章后面的“绑定到资源”一节中介绍)。绑定到这种资源的标准方法是使用StaticResource标记扩展将这种资源分配到目标控件的DataContext属性中。例如,可以将TextBox控件Text属性绑定到Products对象资源 name属性,使用如下的XMAL

< TextBox  DataContext =" {StaticResource productResource} "  Text {Binding name}   />

 

但是,有时如果想要将控件的属性绑定到一个给定的源时,没有给其子控件修改继承的数据上下文(此处的数据上下文已经在某处设置到更高级的层次结构中)或者没有为其他属性修改绑定源。你可以通过使用bindingSource属性将TextBox控件直接绑定到资源而无需使用TextBox控件的DataContext属性绑定到资源的方式。如下的代码示例展示了此种绑定方法:

Text {Binding name, Source={StaticResource productResource}} />

 

注:另外,如果在代码中创建数据绑定,可以分配任何对象到此属性中充当绑定的源。

 

Elementname绑定

56章,我们看到了如何通过过使用Elementname 绑定将各种控件的ItemsSource属性绑定到DomainDataSource控件的Data属性上,但是尚未对此进行深入探讨。DomainDataSource控件从服务器获取数据并通过其Data属性公开暴露数据。然后我们就通过将ListBoxDataGrIDDataForm控件的ItemsSource属性绑定到DomainDataSource控件的Data属性以利用了这些公开暴露的数据,实际上也就是一个控件的属性绑定到另一个控件的属性上。如果要绑定一个控件的属性到另一个在视图中的控件(已命名)的属性上,我们需要使用特殊的绑定方法称之为Elemnetname绑定。使用bingding Elementname属性,需要提供在同一命名空间里可以充作绑定源的控件的名称(而不是分配给控件的DataContext属性的对象)。下面一些例子展示了这种绑定。

一个简单的例子就是将两个TextBox控件的Text属性时行连接。当修改一个TextBox里的文本时,第二个TextBox的文本也会自动进行更新:

 

< StackPanel >  

    <TextBox name="FirstTextBox"/>
    ="SecondTextBox" Text="{Binding Text, Elementname=FirstTextBox}" />
</StackPanel>

注:如果将第二个TextBox的绑定模式设置为TowWay,修改第二个TextBox也会更新第一个TextBox。第一个TextBox只有在第二个TextBox失去焦点时才会更新。

类似地,可以将TextBlock控件的Text属性绑定到SlIDer控件的Value属性上,从而可以在TextBlock上显示SlIDer控件的当前值。

 <SlIDer name="sourceSlIDer" />

TextBlock  {Binding Elementname=sourceSlIDer, Path=Value} />

 

下面示例中的XMAL展示了ListBox控件的ItemsSource属性绑定到DomainDataSource控件(名为productSummaryDDS)的Data属性:

 

ListBox  ItemsSource =”{Binding  Elementname =productSummaryDDS,Path=Data}”/>
 

 

下面这个例子展示了BusyIndicator控件的IsBusy属性绑定到DomainDataSource控件(名为)的IsBusy属性。当DomainDataSource控件的IsBusy属性值为ture时会显示BusyIndicatior控件:

 controlsTollkit:BusyIndicator IsBusy=”{Binding ElementnamePath=IsBusy}” />

最后一个例子,将Lable控件的Target属性绑定到TextBox上去。注意,没有指定绑定的路径,而是将Target属性绑定到了TextBox自身:

sdk:Label Content=”name:” TargetEmementname=ProductnameTextBox}” /> 

relativeSource绑定

绑定的标记扩展中有一个relativeSource属性,使得我们可以绑定到相对于目标的源上。前面介绍了的Elementname绑定是将一个控件的属性绑定到另一个控件的属性上。但是,只有当源控件进行了命名时Elementname绑定才可以使用。而relativeSource则可以绑定到相对于目标控件的未命名源控件上。

通过使用relativeSource标记扩展可以取得一个对源控件的引用,返回的值可以分配给绑定的relativeSource属性。relativeSource标记扩展有三种模式:SelfTemplateParentFindAncestor。下面依次看看。

 

Self 模式

Slef模式返回目标控件本身,用于绑定同一控件的两个属性。如果想要将控件自身的属性与控件的附加属性进行绑定时,这一模式就能发挥作用。例如,如果想要让用户在TextBox上停靠显示相关工具提示时,需要在提示中显示文本框内的所有文本。在这里,我们需要使用tooltipService.ToopTip附加属性(见第2章)然后将其绑定到TextBoxText属性,就需要使用relativeSource标记扩展的Self模式:

TextBox Text=CurrentItem}” tooltipService.tooltipText, relativeSource={relativeSource Self}}” /> 

TemplatedParent 模式

TemplatedParent模式仅应用于控件内包含控件模板或数据模板的控件。这种模式返回模板项目对象并且可以使模板项目的有关属性绑定到目标属性上。当在控件内使用数据模板时,例如在ListBox控件的Item数据模板,TemplatedParent模式就会返回相应模板项的显示内容。注意这一模式并不会返回ListBoxItem控件;数据模板实际上是应用于项目的表现器(Content Presenter),因此返回的是内容表现器;如果控件模板是针对ListBoxITem构建的,则TemplatedParent模式就可以返回ListBoxItem本身。

例如,如下的数据模板绑定表达式将会获取内容表现器的实际高度:

"{Binding relativeSource={relativeSource TemplatedParent}, Path=ActualHeight}" 

这种绑定的另一个有用场景是在需要获得模板项的数据上下文的情形。控件本身数据上下文当然可以向下覆盖到数据模板层,但是如果在控件中的数据模板分配一个不同的数据上下文,如GrID,你应该使用这种方法再次获取和绑定原始数据上下文到项目中。

 注:如果使用控件模板,绑定表达式:“{Binding relativeSource={relativeSource TemplatedParnet}}”等价于“{TemplateBinding}”标记扩展。与TemplateBinding标记扩展只支持单向绑定不同,relativeSource标记扩展可以用于实现双向绑定,这在很多场景下都很有用,特别是在创建定制控件的控件模板时。在12章我们会深入讨论。

 

FindAncestors 模式

Silverlight 5 引入了一种新的relativeSource 绑定模式—FindAncestor模式。这种模式允许你在XMAL层级结构中向上查找给定类型的控件。假设有如下的XMAL

 

GrID  Background ="Green"  WIDth ="200"  Height > 

    GrID Background="Red" margin="20">
        ="Blue">
              <margin="20"/>
        GrID>
    >

正如所见,这个XMAL代码中包含了四个嵌套的GrID控件。如果现在想要将最里面的GrID控件的背景颜色设置为与上面各层级GrID控件之一的相同,就可以使用relativeSource绑定的FindAncesotr模式来进行。relativeSource标记扩展有两个属性:AncestorsTypeAncestorLevel。前者用于指定供搜索的控件类型,后者用于指定在控件被选定前有多少次指定控件类型应该在控件层级中暴露。如下XMAL展示了BackGround属性的绑定(最内侧的GrID控件与最外侧的GrID控件相应属性进行绑定),一共跳过两个GrID实例,因此共有3次暴露:

 

< GrID  Background ="Green"  WIDth ="200"  Height ="200" >
     ="Red"  margin ="20" >
         ="Blue" >
             =" {Binding Background, 
                                       relativeSource={relativeSource FindAncestor,
                                                                      AncestorType=GrID,
                                                                      AncestorLevel=3}}
"  
                  margin
="20"   />
         </ GrID >
>

 

如果将AncestorLever属性调整为12,会看到最内侧GrID控件的背景会与相应GrID控件的属性进行变化。

注:relativeSourceFindAncestors模式还有很多潜在用途。比如,如果想要分配一个viewmodel对象到视图的DataContext属性(即在PageUserControl控件),一个层级低于控件层次结构的控件其DataContext没有设置到viewmodel对象如果控件包含在ListBox项里,仍需要绑定到viewmodel对象 的一个属性上。relativeSource绑定的FindAncestor模式就能够很容易获得对PageUserControl或其他顶级控件的引用,可以直接获得viewmodel对象的DataContext属性的引用。另一个应用是能够使用ListBox内部的控件项目数据模板可以获得ListBoxtItem控件的引用(因为包含在其中)。这就使得控制可以访问ListBoxItemIsSelected属性,使其可以根据ListBox项目是否被选定而改变其状态。 

直接绑定控件属性到其Data Context

你可以直接将控件的属性与分配给其的DataContext属性对象进行绑定,可以直接分配到控件的属性上,也可以分配到向下的对象层级里,只需要简单设置其值到“{Binding}”。这一场景适用于绑定大量控件到同一数据上下文的情况。例如,一个GrID 控件的数据上下文绑定到了一个集合,在该GrID内的多个控件都可以继承这个数据上下文将其作为绑定源。因此,为了将GrID控件内的ListBox控件的ItemsSource属性绑定到这个集合,可以直接设置绑定表达式为“{Binding}”。

作为示例,如下XAMLTextBox控件的Text属性被绑定到TextBox控件的DataContext属性,结果在TextBox中的Text就会显示“Hello”字样:

 

="Hello" {Binding} />

 

注:“{Binding}”绑定表达式等价于{Binding Path=.},也等价于{Binding Path=} 

检测DataContext的值何时变更

假如有一个视图用于处理由viewmodel对象(分配给视图的DataContext属性)引发的事件,如果一个新的viewmodel对象被分配到VIEwDataContext属性上,视图需要取消订阅前面的事件并处理新viewmodel对象的事件。在这种场景里,视图需要知道DataContext属性是否发生变化以便作出相应的处理。

在早期版本的Silverlight里,没有方便的方法来确定控件DataContext值是否发生了变化 ,使得这种场景处理起来相当棘手。而Silverlight5 引入了DataContextChanged事件,只有当DataContext属性变更时才会触发。你可以处理这种事件并作出相应响应。

现在来看一个例子。如下的XMAL包含了一个button控件和一个TextBox控件 TextBox控件 Text属性绑定到其DataContext,而TextBox控件的DataContextChaged事件在后置代码中进行处理。button控件的Click事件也在后置代码进行处理,因为我们将使用这一事件来改变 TextBox控件的DataContext属性:

 

StackPanel >
     button  name ="ChangeContextbutton"  Content ="Change Context"  
            Height
="33"  WIDth ="143"  Click ="ChangeContextbutton_Click" />
     ="MyTextBox"
             DataContextChanged
="MyTextBox_DataContextChanged" />
</ >

 

       在后置代码里,我们现在需要处理button控件的Click事件和TextBox控件的DataContextChanged事件。在button控件的Click事件处理方法中,将TextBox控件的DataContext属性值进行变更。如下代码为其分配了一个新的GUID

 

private  voID ChangeContextbutton_Click( object sender, RoutedEventArgs e)
{
    MyTextBox.DataContext = GuID.NewGuID();
}

 

TextBox控件的DataContextChanged事件处理代码中,我们简单地显示了一个信息框指出事件已经引发:

 

voID MyTextBox_DataContextChanged( " My data context has changed! ");
}

 

运行代码,你会发现每次点击按钮都会d出一个信息框。作为练习,分配GUID值到视图的DataContext属性而不是TextBox控件的:

 

{
     this.DataContext = GuID.NewGuID();
}

 

TextBox控件会继承这一数据上下文,这样在视图的数据上下文属性发生变化TextBoxDatContext属性也会发生变化。因此,DataContextChanged事件仍然会引发。

 

VIEw的后置代码中绑定到属性

前面介绍了属性可以绑定到对象,资源或视图中的其他控件,但是如果你要绑定的属性源实际上是视图的后置代码类怎么办?见如下的XAML

 

UserControl  x:Class ="Chapter11Workshop.MainPage"
    xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d
="http://schemas.microsoft.com/Expression/blend/2008"
    xmlns:mc
="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable
="d"
    d:DesignHeight
="300"  d:DesignWIDth ="400" >
    
     TextBlock  WIDth ="100" />
UserControl >

 

视图的后置代码定义了一个属性名为Username,如下:

using System.windows.Controls;
namespace Chapter11Workshop
{
     public  partial  class MainPage : UserControl
    {
         public MainPage()
        {
            InitializeComponent();
        }
 
         string Username
        {
             get {  return  " Chris Anderson "; }
        }
    }

TextBlock控件可以使用relativeSource绑定的FindAncestor模式来找到视图的根元素并将其作为绑定源,如下所示:

TextBlock WIDth="100" Height="20" Text="{Binding Username, relativeSource={relativeSource FindAncestor, AncestorType=UserControl}}"/> 

另外,也可以使用Elementname绑定获得同样的效果。这必须给XAML文件的根元素用name属性进行命名。此例中将其命名为Root。这样就可以使用Elementname绑定了:

 

WIDth =”100”  Height =”20”  Username , Elementname =Root}”  />

 

 

注:通常,任何视图需要绑定的属性都应在VIEwModle类中进行定义。(为MVVM设计模式的一部分)。属性通常在后置代码中定义;模型提供数据;XAML可以直接通过绑定获取这些数据而无需后置代码的交互(除了属性值的getset访问器的设置以外)。对于MVVM的详细分析见第12章的讨论。 

XAML中实例化类

可以在XAML中实例化类,将其定义为资源或者作为控件属性的值。下面来看看。

创建一个可绑定的类

我们先在XAML中创建类的实例。在你的项目中创建一个viewmodels文件夹。增加一个新的类,命名为Productviewmodel。添加一些属性(name,ProductNumber等) 以便我们进行绑定。在类的构造器中为这些属性设置一些默认值。

 

class Productviewmodel
{
     string name {  getset; }
     string ProductNumber {  set; } 
 
     public Productviewmodel()
{
name =  Helmet ";
        ProductNumber =  H01 ";
    }
}
 

 

注:为了在XMAL中实例化类,必须有一个默认构造器(无参构造器)。如果尚未定义任何构造器,将会为你自动创建默认构造器,否则必须向类中明确添加。 

XAML中实例化类并作为资源然后进行绑定

在XAM实例化类的第一步是需要 声明 一个名称 空间在XAML文件中:

   Xmlns:vm=”clr-namespace:AdventureWorks.viewmodels”

下一步是定义 类的实例作为资源,记住要给出一个key:

 

UserControl.Resources vm:Productviewmodel  x:Key ="productResource" >

 

甚至可以分配一个值给类中的属性(这将会覆盖类构造器的默认值):

vm:Productviewmodel x:Key="productResource" name="Bike" ProductNumber="B001"/> 

注:在XAML中只能给少数几种数据类型赋值,如string,Boolean,IntDouble。其他复杂类型如DecimalDateTime等,需要类型转换以便能够适应待处理的语句。创建和使用类型转换器见第12章。

 

类被实例化并定义为资源后,就可以作为对象资源绑定到控件上了上了。方法是使用StaticResource标记扩展:

 

/>

 

注:任何在类中硬编码的数据或作为资源的属性都会在设计时显示在被绑定的控件上。

 

实例化类并作为控件属性的值

可以直接在XAML中实例化类并将对象分配到控件的属性上去,无需定义对象为资源。例如,可能想要分配一个Productviewmodel类的实例到视图的DataContext属性上。就可以采用如下代码实现:

this.DataContext =  new Productviewmodel();

 

XAML中,采用如下元素语法 *** 作:

 

UserControl.DataContext vm:Productviewmodel  >

 

这是声明vIEwviewmodel的连接方式比较好的选择,在使用MVVM设计模式时更是这样。

 

在后置代码中定义资源

现在你已经看到可以在XMAL中定义资源,但有时使用代码定义资源也是有用的。例如,可以有一个factory类需要进行实例化,但是该类没有无参构造函数(这是XAML实例化类所必须的)。在这种情况下就需要在代码中定义资源。

在本书贯穿始终的Silverlight业务应用程序项目:AdventureWoks就有这样的例子。如果打开AdventureWorks项目的App.xaml.cs文件就会发现Application_Startup事件处理函数,并可以找到如下代码:

 

This.Resources.Add(“WebContext”,WebContext.Current);

 

这行代码添加了一个WebContext对象作为应用程序范围内的资源。如你所见,其简单地获取了一个对资源字典的引用(在本例中是Application对象资源字典)并调用Add方法指定了资源keyWebContext)和一个作为资源的对象(WebContext.Current)。可以在XAML中像其他资源一样进行绑定和使用。

 

Buttton  Content =”Click  Me!”
           IsEnabled
Path =User.IsAuthenticated,
Source ={StaticResource  WebContext} }”  />
 

 

绑定到嵌套属性

    通常在创建绑定时,需要绑定到源对象的单个属性。例如TextBox控件的DataContext属性可能被分配了一个Personal对象,而你可能想要绑定该属性的Firstname属性,因为Firstname属性返回了一个字符串:

 

Firstname , Source personResource}}”  />

 

但是,如果Persona对象并没有Firstname属性而只有name属性,该属性返回Personname对象,而该对象有Firstname属性,如何处理这种嵌套属性呢?这可以使用与C#代码相同风格的点式语法访问下级对象:

 

name.Firstname,0)">personResource} }”  /> 

 

可以根据需要遍历任何层级的对象。 


绑定到索引属性

除了绑定到嵌套属性以外,也可以绑定到索引属性,即可以是整数索引也可是字符串索引。索引选择语法与C#类似-简单地在集合属性名后附加一个方括号作为索引号。例如,Person对象有一个Address属性返回了一个Address对象集合,就可以用如下代码绑定到第一个Address对象:

 DataContext=”{StaticResource personResource}”          TextAddress[0].Addressline1}” />

如果集合具有字符串式索引,如Dictionary<string,string>,可以采用字符串式索引引用语法,如下:

personResource}”             TextAddress[HOME].Addressline1}”  在上例中,HOMEAddress对象返回的一个字符串索引。如果给定的索引未发现,绑定就会失败,在VS的输出窗口就会出现一个错误提示。

 

绑定到动态属性

有时,需要绑定到设计时并不明确结构的数据上,如XMLJsON数据。这种情况下,应该创建一个运行时对象,然后反序列化数据并进行绑定。Silverlight4添加了对动态类型的支持,解决了这一问题。但是,the downsIDe to creating dynamic objects is that you can’t

actually bind to them,因为Silverlight的绑定引擎并不能识别动态属性为真正的属性。任何直接针对动态属性绑定的尝试都会失败。为了克服这一难题,SIlverlight5引入了ICustomTypeProvIDer接口,可以在类中实现以告知绑定引擎对象的结构。

ICustomTypeProvIDer接口位于System.Reflection命名空间,只需要实现一个方法:GetCustomType。但是,这一方法的实现过程非常复杂,需要你从头创建一个类型。Type类在Silverlight5是非封闭类,可以从这个类继承并覆写其属性和方法。

Alexandra RusinaSilverlight团队的项目经理创建了一个Helper类避免了自已实现ICustomTypeProvIDer接口的复杂性,你可以从以下网址下载这个类:http://blogs.msdn.com/b/silverlight_sdk/archive/2011/04/26/binding-to-dynamic-propertIEs-with-icustomtypeprovIDer-silverlight-5-beta.aspx. 

使用这个helper类,就可以创建一个即继承于此类又实现ICustomTypeProvIDer接口的类,或者创建委托GetCustomType方法到helper类中。后一个方法用于目标类已经继承于其他类的情况。

下面举例说明,如何创建一个Products类并在运行时定义其结构:

1、 从创建类开始,并继承自范型CutomTypeHelper类:

public  class Product : CustomTypeHelper<Product> { }

 

2、 通过调用静态的AddProperty方法(继承自CustomTypeHelper<T>类)添加属性到类中,为每个属性提供一个命名和类型:

Product.AddProperty( " Productname "typeof( string));
Product.AddProperty( category QtyAvailable int));

3、 在实例化类的时候,很显然不能像以往在设计时定义类的方式一样进行值的设置。而是应该调用SetPropertyValue方法(也是继承自CustomTypeHelper<T>类),传递属性名作为参数并为其赋值:

Product product =  new Product();
product.SetPropertyValue( Helmet ");
product.SetPropertyValue( AccessorIEs 10);

4、 现在就可以使用与在标准CLR对象中相同的方法绑定到这一对象及其属性了。幸亏有helper类,属性自动实现 INotifyPropertyChanged接口,因此不必担心这一问题。必要的话,也可以调用 GetPropertyValue方法获取值:

string productname = ( string)product.GetPropertyValue( ");
string category = ( int qtyAvailable = ( int)product.GetPropertyValue( ");
 

 


注:Matt DuffIEld也提供了实现 ICustomTypeProver接口的实现方法,见:http://mattduffIEld.wordpress.com/2011/09/16/using-icustomtypeprovIDer-in-

silverlight-5-with-xml. 

增强的数据绑定

Binding类有很多属性可用于进一步配置数据绑定,而不仅仅是将目标属性的值与源属性直接相连接。这些属性是使得Silverlight数据绑定功能更加强大和灵活的关键所在,我们来看看:

字符串格式

Bindings有一个StringFormat属性可以用于在数据传递到目标控件之前格式化被绑定属性的值。这是一个很有用的功能,特别是绑定到非字符串属性时,如DateTimeDecimal类型。可以使用StringFormat属性来设置格式 ,使用与在代码中ToString方法相同的格式化字符串值的方法,即可以使用标准格式也可以定制格式。比如,如下绑定使用标准格式显示被绑定属性的值在TextBox中,将文件格式为现金格式:

< TextBox  Text =”{Binding  TotalCost,Mode =TwoWay,stringFormat=C}”  />

 

或者你可以将日期格式化为长日期格式 (如Sunday,May 16,2011:

=" {Binding StartDate, Mode=TwoWay, StringFomat=D}” />

或者使用定制的格式(如2011-05-16):

"   也可以格式化decimal,singl,doublefloat为一种给定小数位数的格式(如3.14):

{Binding PI, StringFormat=0.00} 如果想要在字符串中包含 一些特殊字符,将格式化字符串包含在单引号内(不能用引号,因为引号表示属性值):

另外,也可以在指定字符串前使用反斜线:

Alternatively,you can simply escape them by inserting a backslash (\) prior to the special character:

注:可用的格式化字符串的标准和定制符号的说明详见MSDNhttp://msdn.microsoft.com/ en-us/library/26etazsy.aspx.其他资源示例也可以在VS2010 data binding Expression builder中找到。


 

另一个整洁的数据绑定技巧是使用绑定的StringFormat属性连接绑定值与其他文本。比如,你可能想要在一个值前显示一个标签。假定你绑定到了一个属性,返回“Chris Anderson”作为其值,想要在视图中进行如下的显示:

name: Chris Anderson

你就可以使用StackPanel控件将两个TextBlock控件放在一 起获得此效果:

StackPanel  OrIEntation ="Horizontal" >
     TextBlock  ="name: " />
     {Binding name} />
</ StackPanel >

或者使用TextBlock控件的Inlines属性:

TextBlock TextBlock.Inlines >
         Run  />
         >
甚至可以创建一个Value转换器或定制的标记扩展来实现。但是,最简单和优雅的方法是使用Binding对象的StringFormat属性套用字符串占位符来实现 ,与String类中的StringFormat属性的语法一样:

{0}占位符取代了被绑定的值,从而有效地实现了两个字符串的连接。

下面是另一个例子,在这里被绑定值将会包含在两个括号中间(也就是说TextBlock控件会显示"(Chris Anderson)");

当然,你只有一个可供替换的值,因为绑定只返回一个值,但是如果你愿意,这是一个非常干净的方法将被绑定的值与其他值相连接,而无需要一些复杂的实现过程。

当源属性值为空时的可选值

如果一个属性被绑定到一个空值,可以获得一个可替代的值,方法是通过bindingTargetNullValue属性。这很有用,特别是想要为未提供的数据提供默认值时:

TotalAmount ,TargetNullValue=0}” /> 

回调值

如果绑定的源为空,就意味着控件的DataContex属性和bindingSource属性都为空,或者说被绑定的属性在对象中并不存在,你可以使用FallbackValue属性指定一个替代值。比如,如下的绑定将显示“Oops!”:

UpdateSourceTrigger

Binding对象的UpdateSourceTrigger属性可以在双向绑定模式下指定源属性何时更新以响应目标属性的变化。通常在对TextBox控件的Text属性进行绑定时,UpdateSourceTrigger属性允许指定是在TextBox控件失去焦点时控件进行更新,还是选择文件一旦发生变化立即更新,或是只有当代码显示指定时更新。现在我们逐个来看看:

默认模式

UpdateSourceTrigger属性设置为Default,源属性将在TextBox控件失去焦点进行更新,比方说用户使用Tab键切换。由于是默认模式,无需在XAML文件中显示指定。当然为了完整,也可以像如下进行设定:

PropertyChanged模式

Silverlight5现在支持新的UpdateSourceTrigger模式:PropertyChanged模式。这种模式使得被绑定(源)属性在控件属性变更时立即进行变更;

/> 

正如在第6章已经看到的,这种模式特别适用于实现文本框筛选查询功能,可以根据用户输入的文本内容即时过滤数据,从而创建良好的用户体验。

 
显示模式

7章,已经讨论过实现IEditableObject接口以添加transaction行为到类中(begin,acceptcancel changes)。借助IEditableObject接口支持的行为,可以在属性变更的各个阶段设置逻辑,可以选择显示声明后再进行更新 *** 作,比如用户点击OK按钮等。这就需要设置Binding对象 UpdateSourceTrigger属性为Explicit

="nameTextBox"  Text 注意我们设置的是TextBox控件的name属性。这一属性在后置代码进行调用时将发挥作用。

如果现在就运行程序,你会发现源属性永远不会更新。要让绑定的源属性发生更新,必须在代码中手工进行更新的初始化。假如在视图中有一个OK按钮,当按钮点击后需要将字段的变更更新到被绑定的对象上。为达此目的,需要在后置代码中处理按钮的Click事件,并使用文本框的Text属性(使用GetBindingExpression方法,返回BindingExpression对象)。你可以通过调用BindingExpression对象的UpdateSource方法显示地强制绑定的源属性更新,如下所示:

 

BindingExpression expr = nameTextBox.GetBindingExpression(TextBox.TextProperty);
expr.UpdateSource();

 

数值转换

你已经看到了如何使用BindingStringFormat属性格式化被绑定的数值,这种格式化是在传递到目标控件之前进行的,如果想要将数值转化为完全不同的数值该怎么办呢?一个例子是尝试绑定Boolean值到控件的Visibility属性。正常情况下这是不行的,因为Visibility属性只接受Visibility枚举值(VisibleCollapsed)。可以修改需要绑定对象的属性以便返回一个Visibility枚举值,但这并非总是可行的,也并不理想。有一种方法可以在绑定过程中将Boolean值转化为Visibility枚举值,这种方法就是value converter(下称值转换器),值转换器需要实现IValueConverter接口,共有两个方法:

Convert方法传递绑定源对象的属性值(作为参数),你可以实现必须的逻辑将该值转换为可供分配到目标属性的值(作为方法的返回结果)。

ConvertibleBack方法用于双向绑定以转换UI上修改的目标值(作为参数),返回一个可以分配给被绑定属性(源属性)的值;

创建一个简单的值转换器

下面实现一个简单的值转换器:从Boolean值转换为Visibility枚举值:

using System;
using System.windows;
using System.windows.Data;
 
public  class BoolToVisibilityValueConverter : IValueConverter
{
     object Convert( object value, Type targettype,  object parameter, 
                          System.Globalization.CultureInfo culture)
    {
         return ( bool)value ? Visibility.Visible : Visibility.Collapsed;
    }
 
     object ConvertBack( throw  new NotImplementedException();
    }
}
 

 

注意我们没有实现 ConvertBack的逻辑,是因为对控件Visibility属性的绑定只有one-timeone-way绑定模式。因此,这一方法不会用到,没有必要实现它。但是,对其他转换,有可能会用到此方法,就需要相应的具体实现了。

注:在使用Silverlight业务应用程序项目模板作为应用程序的基础时,你会发现在项目中已经实现了一些值转换器(在Helpers文件夹中),例如NotoperationValueConverter值转换器将ture值转换为false,而false则转换为true;


使用值转换器

一旦创建了值转换器,就需要在对象的某一层级的资源上实例化它。可以是page,user control的资源,也可以分配到Application对象的资源(在App.xaml文件里)从而可以在整个应用程序中使用。首先,声明一个命名空间指向值转换器所在空间:

xmlns:vc="clr-namespace:AdventureWorks.ValueConverters"

然后,实例化值转换器作为某个对象层级的资源:

vc:BoolToVisibilityValueConverter  x:Key ="VisibilityConverter" 现在,就可以通过数据绑定的Converter属性使用值转换器了,使用StaticResource标记扩展获得对其的引用。如下示例示意了如果被绑定对象的IsDirty属性在对象处于编辑状态将Save按钮的Visibility值设置为true(经过值转换器转换为Visibility.Visible)

button ="Save"="23" WIDth="75"   Visibility{Binding IsDirty, Converter={StaticResource VisibilityConverter}} 向值转换器传递参数

有时需要向值转换器传递附加的参数才能使其正常工作。可以在XAML定义中向值转换器传递参数值,使用BindingConverterParameter属性;

SIlverlight的早期版本中,值转换器主要用于格式化被绑定的数值,直到SIlverlight4引入StringForamt属性才有所改观,这样值转换器在字符串转换上的用途就不是很大了,但是在某些场景仍然是很有用的。与前述讨论有所不同的一个场景是在第7章讨论过的对Radiobutton控件的绑定,在此如果想要将Radiobutton控件绑定到一个枚举类型的被绑定对象上,并且每个Radiobutton控件分别显示一个不同的枚举值。正如在第7章讨论的一样,并没有一个可以直接使用的标准数据绑定模型,如果在绑定中使用值转换器并传递一个参数就使这种场景成为可能。如下的值转换器展示了一个范型的枚举到布尔值的转换器,通过比较所传递数值与所传递的参数,如果匹配就返回true,反之返回false.

using System.windows.Data;
class EnumEqualityValueConverter : IValueConverter
{
     return value.ToString() == parameter.ToString();
    }
 
     return Enum.Parse(targettype, parameter.ToString(),255)">true);
    }
}
 

注:在上述场景中采用了TwoWay绑定,因此需要能够将所选定的单选按钮返回所代表的枚举值,需要为此转换器实现ConvertBack方法。


这样我们就可以使用该值转换器了。假如已经声明了命名空间并将转换器实例化为资源,那么只需要简单地将值转换器设置到Radiobutton控件的Binding对象的Converter属性,并为每个Radiobutton控件分配一个枚举值,设置在ConverterParameter属性上,就可以通过对绑定属性值和参数值的比较来实现IsChecked是否返回true的 *** 作。

Radiobutton  ="&lt; 20"  IsChecked {Binding AgeBracket, 
    Converter={StaticResource EnumConverter}, ConverterParameter=BelowTwenty}
="20 to 29" ="30 to 39" ="40 Plus" />
 

传递文化属性到值转换器

CultureInfo对象也可作为参数传递给值转换器。可用于根据给定的区域与语言类型显示所需要的数据。

默认情况下,传递到值转换器的区域与语言信息是从控件 Language属性获取的,使用的是标准的区域与语言字符串,如en-US,表示英语美国)。该值向下覆盖到全部层级,因此如果在XAML文件中的根结点指定这一属性,XAML文件内的的所有控件就都继承了此值。如果区域与语言未经此属性显示指定,默认为美国英语(en-US)(译者注:默认应为服务器所使用的区域与语言信息)。你可以通过Binding对象的ConverterCulture属性显示地传递区域与语言字符串到值转换器,该字符串必须使用culture参数进行传递。

传递整个对象到值转换器

如果想要使用源对象的多个属性转换为一个单一的输出,例如将first namesurname属性组合成单一字符串,也可以用值转换器实现。如下代码所示:

class FullnameValueConverter : IValueConverter
{
     as Person;
         return person.Firstname +    " + person.Surname;
    }
 
     注:这种场景中,最好是添加一个Fullname属性在viewmodel中(指MVVM设计模式)并对其进行绑定。但是,如果没有实现MVVM设计模式,值转换器提供了一种可选择的方案以修改绑定的对象使其适用于UI的需要。


使用属性元素语法进行绑定

尽管很少使用,事实上也可以使用属性元素语法进行绑定 *** 作。如下代码所示:

TextBox TextBox.Text Binding  Path ="name" 多绑定

有时,想要绑定控件的属性到源对象的多个属性,WPF中称为多绑定,但这一特性在Silverlight中并不可用。前面已经讨论了使用值转换器将多个属性合并的方法,可以采用这种方法取代多绑定。Colin Eberhardt创建了可以在Silverlight中实现的多绑定方法,可在此获取:http://www.scottlogic.co.uk/blog/colin/2010/08/silverlight-multibinding-updated-adding-support-for-elementname-and-twoway-binding/.

注:如果使用MVVM设计模式,很少会用到多绑定,因为可以在VIDwModel中添加属性来暴露组合属性值。


在代码中绑定数据

有时,需要在后置代码中对控件的属性进行绑定。这可以使用控件的GetBindingExpression 和 SetBinding方法实现。

为了创建新的绑定并分配给控件的属性,简单实例化一个Binding对象(位于System.windows.Data名称空间),分配绑定配置到所需要的属性上,然后使用控件的SetBinding方法应用绑定到目标控件的属性上。SetBinding方法需要一个Binding对象和与目标控件需要绑定的属性相连的依赖属性两个参数,记住你只能给依赖属性分配数据绑定值。

例如,需要绑定Product实体的name属性到名为nameTextBoxTextBox控件的Text属性:

Binding binding= new  Binding(“name”); 

Binding.Mode=BindingMode.TwoWay;
nameTextBox.SetBinding(TextBox.TextProperty,binding);

在代码中创建绑定很简单,也可以使用binding对象的其他属性,如StringFormat,TargetNullValue,FallbackValue和 Source等

可以使用控件的GetBindingExpression方法来获取分配给控件的绑定属性:

BindingExpression expr=nameTextBox.GetBindingExpression(TextBox.TextProperty); 

这一方法返回一个BindingExpression对象。BindingExpression类有两个属性:ParentBindingDataItem,以及前面提到的UpdateSource方法。ParentBinding属性提供了分配给控件属性的Binding对象,而DataItem属性提供了为绑定充当源的对象。

在代码中获取和设置附加属性

尽管这是一个与数据绑定不同的主题,但是获取和设置控件的附加属性与获取和设置绑定对象相类似。使用控件的GetValue方法,传递给这个方法与附加属性相关的依赖属性,就能获得指定的附加属性值。比如,使用如下代码获取一个名为nameTextBox的控件在GrID控件内的行号(GrID.Row附加属性值):

Int row=(int)nameTextBox.GetValue(GrID.RowProperty);

可以为附加属性指定新值,使用SetValue方法,该方法需要附加属性以及新的值作为参数:

nameTextBox.SetValue(GrID.RowProperty,row);

 上述代码就可以用于对控件在窗体中布局的动态调整。

 Visual Studio Data Binding Expression BuilderVisual Studio数据绑定表达式构建器)

      VS2010提供了一个数据绑定表达式构建器,可以协助我们编写数据绑定表达式。覆盖了数据绑定表达式编写的全部内容。

要使用Visual Studio Data Binding Expression Builder,在设计器中选定目标控件,在属性窗体中找到需要绑定的目标属性,点击“高级”图标。从d出的菜单中选择应用数据绑定菜单项,见图11-1

 


 

这样就打开了数据绑定表达式构建器,如图11-2所示。注意构建器有一个手风琴样式的布局界面,可以点击每个header打开相应的面板,选择相应的选项。第一步需要选择绑定的源:


下一步选择绑定的路径,如图11-3所示:


 

注意:默认情况下绑定源包含的属性并不会在图11-3中显示,除非构建器清楚地获知绑定的是何种对象。正如在图11-211-3看到的那样,构建器已经知道绑定源是PagedEntityCollectionVIEw对象,封装了一个RegistrationData对象集合。知晓这一问题是由于数据源继承自控件的数据上下文,而控件的数据上下文则被绑定到了DomainDataSource控件的Data属性,而DomainDataSource控件又支持设计时数据属性(已经在第10章讨论过)。另外,如果绑定到一个在XAML中作为资源定义和实例化的类上,构建器也可以确定源可供显示的属性。如果绑定到视图中的另一个控件,构建器也会知道哪个属性可供绑定。但是,在无法确定绑定源的对象类型时,比如在后置代码中为绑定分配数据,也没有使用支持设计时数据属性的控件,就不能使用构建器自动选择绑定的源属性,除非能够为要绑定的数据源提供设计时数据定义。唯一可选方案是在XAML中手工输入数据绑定表达式到目标属性上。

如果你愿意,也可以选择值转换器用于绑定,见图11-4


注:一旦转换器已经创建为资源,你就可以在右侧的资源列表(Resources)中进行选择。如果尚未定义,点击“创建新资源”(Create New…),可以为转换器指定参数,创建所需的转换器。

 

最后一步是选择任何可以应用在binding上的附加选项,如图11-5显示:


 

选项选择完毕后,在构建器外侧任意位置点击鼠标,d出窗口就会关闭,控件属性的绑定表达式就生成了。

 

使用数据源工作窗口创建需要绑定的资源和控件

5章,你已经看到可以通过拖拽实体简单快速创建视图,这一实体是通过RIA服务创建的。在设计界面进入数据源窗体,就会找到自动创建的DomainDataSource控件,这一控件可以用来显示和编辑实体的相关属性。事实上你也可以使用下面的步骤利用数据源工具窗口方便快速地构建绑定到viewmodelModel类的控件:

1、添加想要在视图中绑定到数据源窗口的类。在数据源窗口的工具栏上(顶端左侧)点击添加新数据源按钮。

 

注:确保项目已经编译过,否则新类不会出现在选择列表中。

2、在出现的对话框里,选择对象作为数据源类型,点击下一步

3、从列表中选择想要绑定到视图上的类,点击Finish。该类就会出现在数据源工作窗口中,

4.选择如何生成控件,方法是点击类上的下拉箭头,可以选择生成的类型。通常,可以选择DataGrID控件或细节布局。也可以更改各个属性在视图中的控件类型,方法是点击字段侧边的下拉箭头,从下拉列表中选择适当的控件类型;

5、现在,从数据源窗体中拖动类,放置在视图的设计界面上。就自动为每个属性生成了相应的控件并绑定到已经在视图资源中定义的有关资源上。

你会注意到控件绑定的资源实际上是CollectionVIEwSource(详见第6章)。比如,一个命名为TestData的类旋转到设计界面上将会创建如下的资源,以便控件进行绑定:

CollectionVIEwSource  ="testDataVIEwSource"  

                      d:DesignSource{d:DesignInstance my:TestData, CreateList=True}/> 

在这里仍然需要为CollectionVIEwSource设置源,你会发现从数据源窗口上拖动类到设计界面后自动在VIEw的后置代码中的Loaded事件处理方法里添加了注释过的代码,如下所示:

private  voID UserControl_Loaded( //  Do not load your data at design time.
    
 if (!System.ComponentModel.DesignerPropertIEs.GetIsInDesignmode(this))
    
 {
    
   Load your data here and assign the result to the CollectionVIEwSource.
    
  System.windows.Data.CollectionVIEwSource myCollectionVIEwSource = 
    
            (System.windows.Data.CollectionVIEwSource)
    
                this.Resources["Resource Key for CollectionVIEwSource"];
    
  myCollectionVIEwSource.source = your data
    
 }

调试数据绑定问题

创建绑定很容易出现错误,如属性名的拼写错误,或者当重构一个类充当数据源时打破了正在工作中的绑定,比如重命名属性。有时候,绑定并不按所期望的方式工作。

这些数据绑定错误很难及时解决,甚至当绑定失败后也很难识别错误的存在位置,绑定失败并不会在应用程序中抛出异常。但也有一些技术可以帮助识别和跟踪数据绑定错误,我们来看看:

 

在输出窗口中查看数据绑定错误日志

第一个方法就是在Visual Studio的输出(OutPut)窗口中看看哪个数据绑定错误发生。(如果无法找到请在主菜单上选择查看输出)。当程序调试时绑定的错误自动记录到输出窗口,并附带有相应的错误信息,绑定源对象和属性,绑定目标对象及属性。比如,设置绑定路径到Nam属性(拼写错误,应该为name)将会造成如下的错误输出:

 System.windows.Data Error: BindingExpression path error: 'Nam' property not found on

'AdventureWorks.Models.Product' 'AdventureWorks.Models.Product' (HashCode=29083993).
BindingExpression: Path='Nam' DataItem='AdventureWorks.Models.Product' (HashCode=29083993) ;
target element is 'System.windows.Controls.TextBox' (name='nameTextBox') ;  target property is'Text' (type 'System.String')

这只提供了关于错误的少量信息,在某些情况可以协助你跟踪。

更重要的是,在非调试场合,不能使用这种技术。可选方案是分配值到bindingFallBackValue属性。尽管这并不会告知为什么绑定失败,却可以协助证明绑定确定失败了:

 

/>

 

或者也可以选择Karl Shifflett开发的helper类称为Glimpse。这个类的特征是可以显示无源的绑定。可以从

http://karlshifflett.wordpress.com/2009/06/08/glimpse-for-silverlight-vIEwing-exceptions-and-binding-errors/下载。

 

在数据绑定上设置断点

Silverlight 5 Tools引入了在XAML文件中设置断点的能力,可以轻易跟踪数据绑定问题。见图11-6,可以看到应用到绑定的断点失败,同样你也可以在代码中设置断点,通过需要在代码行中放置光标并按F9,或者在左侧边界上单击直到断点符号显示出来:


注:你会发现XMAL设计器只允许为数据绑定设置断点。另外注意你不能在同一行上为两个绑定设置断点。假如你在同一元素有两个绑定想要调试,要让每个绑定分别处于不同行上,

 

运行程序,断点就会触发。打开Locals窗口(调试窗口---Locals)来检查绑定的状态,图11-7展示了断点触发时的Locals窗口。可以看到,在绑定中出现了一个错误,Nam属性没有在源对象中发现。我们可以利这一信息确定绑定路径拼写错误。


BindingState对象的三个属性在调试数据绑定时很有用,它们是ErrorFinalSourceUpdateTargetPipeline属性。Error属性显示任何发生在绑定过程中的错误,可以识别绑定失败的直正原因。FinalSource属性则显示正在绑定的数据位置,可以用来确认绑定是否真正获取到正桷的源;UpdateTargetPipeline属性当尝试确定绑定是否获得最终数值时特别有用。这一属性还可以用于观察初始值,经过值转换器的值,经过字符串格式化后的值,或者使用目标空值等等。

还可以在Locals窗口通过BindingState获知双向绑定里何种行为(源→目标还是目标→源)正在发生。从源获取数值后,BindingStateAction属性将会设置为UpdatingTarget,当源被更新时,相应值设置为UpdatingSource

在断点被激活时,如果想要知道哪个代码触发了绑定,检查调用堆栈窗口(调试窗口调用堆栈)很有用。

值得注意的是,断点绑定可以像标准代码的断点一样使用,可以添加约束、条件以及相关配置。通过在在XAML编辑器边界左键点击红色的断点标记,从d出的上下文菜单中选择适当的选项可以实现这些功能。

还可以禁用绑定断点而无需移除它们,方法是设置Binding类的静态IsDeBUGgingEnable属性为false:

Binding.IsDeBUGgingEnabled=false;

注:还有一个方法是在绑定里添加一个值转换器,然后将断点设置在ConvertConvertBack方法里。这是在绑定完成之前调试数据绑定的通用方法。这样做就可以进入绑定过程来分析绑定中的各种问题。

 

其他故障排除技巧

即使绑定正确,也会遇到其他原因的失败。如下是几个典型问题需要进行检查的步骤:

当源属性的值更新时被绑定控件没有更新。确定绑定模式没有设置为OneTime。然后,检查属性set访问器是否引发PropertyChanged事件,实现INotifyPropertyChanged接口,并检查传递到事件的属性名是否与属性的命名相匹配。

UI的值发生变化时源属性未更新。如果向控件输入 了一个新值,例如文本框控件,但是与控件Text属性绑定的源属性未相应地更新,首先要确定已经将绑定模式设定为TwoWay。然后检验属性的set访问器是否未设置为private。最后检查bindingUpdateSourceTrigger属性是否未设置为Explicit,如果是,确定UpdateSource的方法在后置代码中被调用。

不显示验证信息。如果控件不显示任何验证错误信息,检查是否将bindingValIDatesOn属性设置为true,关于valIDateson方法,需要设置对应源对象使用了公开报告验证错误的方法(即valIDatesonexceptions / valIDatesondataerrors / valIDatesonnotifydata错误)。更多信息见第7章:“定义验证规则”节。

控件没显示正确(或任何)数据。确保binding的数据上下文是你想要控件显示的;然后进一步观察控件层尚未分配过其他的数据上下文,而不是从上层继承而来的。

 

命令

命令提供了一种封装逻辑的方法,可以使得逻辑可重用。命令的一个关键优势在于允许在XAML中绑定到多个控件。换言之,在Silverlight中控件不仅可以绑定到数据上也可绑定到 *** 作上!比如,一个viewmodel可能暴露了一个Save命令,可供VIEw中的按钮进行绑定,当按钮按下时得到执行。

 

注:命令通常用于MVVM设计模式,使得viewmodel可以暴露 *** 作给VIEw

 

创建命令

为了构建命令类,必须实现ICommand接口,如图13-6所示:


CanExecute方法指定命令是否可以执行。当命令绑定到按钮时,按钮会相应激活或禁用。为了保证按钮激活状态随着属性更新而更新,命令的CanExecuteChanged事件应该被引发。当命令通过Execute方法执行时,任何逻辑都会付诸执行。

 

注:常见的问题是在变更CanExecute属性后忘记调用CanExecuteChanged事件。如果你发现尽管已经更新了CanExecute属性的值,按钮仍处于禁用状态,这多半是由此引起的。

 

为了创建命令,创建一个新类并实现ICommand接口。如下代码展示了一个简单的命令,当执行时会显示一个信息框:

using System;
using System.windows;
using System.windows.input;
 
namespace Chapter11Sample
{
     class TestCommand : ICommand
    {
         bool CanExecute( object parameter)
        {
             return  true;
        }
 
         event EventHandler CanExecuteChanged;
 
         voID Execute( object parameter)
        {
            MessageBox.Show( Command executed! ");
        }
    }

}  

绑定到命令

Silverlight很多控件都有这样一对属性:CommandCommandParameter—buttonHyperlinkbuttonTogglebuttonRepeatbutton控件。这些控件都可以将Command属性绑定到命令上,当点击时就会执行被绑定的命令。

 

注:SilverlightToolKit里的ContextMenu控件也可以将其菜单项绑定到命令上去。但不幸的是,在Silverlight中没有其他的控件支持命令了,但是仍然可以使用Expression Blender Interactivity类库的InvokeCommandAction行为来激活命令以响应任何控件引发的事件。(比如响应一个ListBox控件某一项被选定的事件)。

 

如下代码展示了TestCommand命令作为资源并绑定到控件上的示例:

="Chapter11Sample.MainPage"
    xmlns:local
="clr-namespace:Chapter11Sample" UserControl.Resources local:TestCommand  x:Key ="testCommand" />
     x:name ="LayoutRoot"  Background ="White" button  Content ="Test Command"  
                Command
{Binding Source={StaticResource testCommand}} >

</UserControl> 

向命令中传递参数

你可能注意到Execute方法还有一个类型为object的参数。可以通过绑定到控件的CommandParameter属性可以将任何值作为参数传递到CanExecuteExecute方法。

比如,可以绑定这一个属性到另一个控件的属性上(如ListBox控件 SelectedItem属性),然后控件的属性值将会作为参数传递到命令Execute方法。如下的XAML示意了这种场景:

="White" ="300" GrID.RowDeFinitions RowDeFinition  Height ="*" />
             ="40" >
        
         ListBox  name ="ProductList" />
        
          GrID.Row ="1"
                Command

                CommandParameter
{Binding SelectedItem, Elementname=ProductList} > 

委托命令

前面介绍如何封装一部分逻辑在命令中,但你可能不想总是在自已的类中设置命令。比如,想要从viewmodel类暴露一些逻辑到VIEw上去,通过命令进行绑定,但在viewmodel类内维持这些逻辑就阻碍了在程序的其他地方使用这些逻辑。在这种情况下,就应该实现命令为DelegateCommandDelegateCommandICommand的一个实现,允许委托命令的实现到viewmodel类的方法中。可以在VIEDWModel类将DelegateCommand对象作为属性进行暴露,允许在MVVM设计模式中轻松地从viewmodel中以命令的形式暴露 *** 作。如下代码就是DelegateCommand类的示例:

viewmodel类中,需要定义两个方法:一个返回一个Boolean值指出命令是否可以执行,另一个将在命令实际执行中加以调用。如果将前面实现的TestCommand命令配置为DelegateCommand,需要开始于在viewmodel类中添加如下两个方法:

 

bool CanTest( object param)
{
     return  true;
}

 

 

 
voID Test( object param)
{
    MessageBox.Show( Command executed! ");
}

 

 

注:在在viewmodel类中实现方法封装为DelegateCommand时,方法必须按照相同的参数和返回值进行设置。

 

下一步是创建DelegateCommand类的实例,将两个方法作为构造器的参数传递入,并作为VIEwModle类的属性返回结果对象:

 

public ICommand TestCommand
{
   get {  new DelegateCommand(Test, CanTest); }
}

 

VIEw中的按钮控件 Command属性可绑定到viewmodelTestCommand属性上。当按钮点击时,命令就会被执行,调用了viewmodel中的Test方法。

 

注:另外一种比较流行的使用DelegateCommand的方法是使用RelayCommandRelayCommand的一个实现可以从MVVM light ToolKit获取(一个流行的MVVM框架,见第13章框架部分的列表。)

 

小结

这一章提供了几个有关绑定到数据的高级话题和技术。SIlverlight的最强有力的功能之一就是可以利用强大的数据绑定引擎构建业务应用程序,本章探讨了该项技术的所有方面,可以帮助你最大程度理解这些问题。很多功能会经常使用,有些则很少用到,但不管怎样,都会增强在实际开发Silverlight应程序中的能力。

总结

以上是内存溢出为你收集整理的SilverLight 5 数据绑定的高级话题全部内容,希望文章能够帮你解决SilverLight 5 数据绑定的高级话题所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/1020356.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-23
下一篇 2022-05-23

发表评论

登录后才能评论

评论列表(0条)

保存