SilverLight 5 数据绑定的高级话题(3)

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

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

 

SilverLight 5 数据绑定的高级话题(3)_第1张图片

 

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

SilverLight 5 数据绑定的高级话题(3)_第2张图片

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

SilverLight 5 数据绑定的高级话题(3)_第3张图片

 

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

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

SilverLight 5 数据绑定的高级话题(3)_第4张图片

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

 

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

SilverLight 5 数据绑定的高级话题(3)_第5张图片

 

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

 

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

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

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

 


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


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

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

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

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

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

< CollectionViewSource  x:Key ="testDataViewSource"  

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

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

复制代码
private  void UserControl_Loaded( object sender, RoutedEventArgs e)
{
     //  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属性。尽管这并不会告知为什么绑定失败,却可以协助证明绑定确定失败了:

 

< TextBox  Text =" {Binding Name, Mode=TwoWay, FallbackValue=Binding failed!} "   />

 

或者也可以选择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,或者在左侧边界上单击直到断点符号显示出来:

SilverLight 5 数据绑定的高级话题(3)_第6张图片

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

 

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

SilverLight 5 数据绑定的高级话题(3)_第7张图片

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

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

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

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

还可以禁用绑定断点而无需移除它们,方法是设置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所示:

SilverLight 5 数据绑定的高级话题(3)_第8张图片

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

 


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


 

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

复制代码
using System;
using System.Windows;
using System.Windows.Input;
 
namespace Chapter11Sample
{
     public  class TestCommand : ICommand
    {
         public  bool CanExecute( object parameter)
        {
             return  true;
        }
 
         public  event EventHandler CanExecuteChanged;
 
         public  void Execute( object parameter)
        {
            MessageBox.Show( " Command executed! ");
        }
    }
复制代码

}  

绑定到命令

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

 


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


 

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

复制代码
< UserControl  x:Class ="Chapter11Sample.MainPage"
    xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local
="clr-namespace:Chapter11Sample" >
    
     < UserControl.Resources >
         < local:TestCommand  x:Key ="testCommand"   />
     UserControl.Resources >
     < Grid  x:Name ="LayoutRoot"  Background ="White" >
         < Button  Content ="Test Command"  
                Command
=" {Binding Source={StaticResource testCommand}} "   />
     Grid >
复制代码

UserControl> 

向命令中传递参数

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

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

复制代码
< UserControl  x:Class ="Chapter11Sample.MainPage"
    xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local
="clr-namespace:Chapter11Sample" >
    
     < UserControl.Resources >
         < local:TestCommand  x:Key ="testCommand"   />
     UserControl.Resources >
     < Grid  x:Name ="LayoutRoot"  Background ="White"  Width ="200"  Height ="300" >
         < Grid.RowDefinitions >
             < RowDefinition  Height ="*"   />
             < RowDefinition  Height ="40"   />
         Grid.RowDefinitions >
        
         < ListBox  Name ="ProductList"   />
        
         < Button  Content ="Test Command"  Grid.Row ="1"
                Command
=" {Binding Source={StaticResource testCommand}} "
                CommandParameter
=" {Binding SelectedItem, ElementName=ProductList} "   />
     Grid >
复制代码

UserControl> 

委托命令

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

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

 

private  bool CanTest( object param)
{
     return  true;
}

 

 

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

 

 

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

 

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

 

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

 

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

 


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


 

小结

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

标签: Silverlight 5 绑定

你可能感兴趣的:(Silverlight)