Beginning Silverlight 4 in C#-数据绑定和Silverlight List控件

参考源码

上一篇关注Silverlight包含的表单控件.这一篇,你会看到两个显示列表数据的控件:ListBox和DataGrid.这些是典型的通过数据绑定技术实现绑定数据的控件.

数据绑定

通过数据绑定,UI元素会从数据源中"绑定"数据,就像下图,当数据源改变,UI元素绑定的这些数据会更新已反应数据源的变化.数据可以来自不同类型的源,同时绑定目标也可以是任何UI元素,包括标准的Silverlight控件.

image

数据绑定简化了应用程序的开发.因为改变是自动反应的,你不要手动更新UI元素.所以,通过使用数据绑定,你可以把你的应用程序的UI从数据中分离出来,这样就可以得到干净的UI和更简易的可维护性.

绑定类

在Silverlight中,数据绑定是通过使用绑定类实现的.绑定类有两个组件-绑定源和绑定目标,还有一个属性决定绑定的方向,这个方向叫绑定模型(binding mode).绑定源就是要被绑定的数据,绑定目标就是要被数据去绑定的控件的属性,绑定模型就是定义绑定源和绑定目标之间怎么样传递数据(单向的(one-way),一次的(one-time,或者双向的(two-way))).你会在接下来的练习里看到它们是怎么工作的.

要定义控件属性的绑定,你可以使用XAML的标记扩展,例如{Binding <path>}.例如,要绑定一个TextBox的Text属性到一个数据源的FirstName字段,你可以使用下面的XAML:

<TextBox Text="{Binding FirstName}"/>

练习:Silverlight的简单数据绑定

为了理解Silverlight的数据绑定,让我们建立一个非常简单的应用程序.这个应用程序会包含一个Book对象,对象包含两个属性:Title和ISBN.这些属性会被绑定到两个TextBox控件.

  1. 使用VS2010创建一个名为BasicDataBinding的Silverlight应用程序.
  2. 为MainPage.xaml文件的根表格定义2行和6列.第一列每行放置一个TextBlock控件,第二列每行放一个TextBox控件.代码如下:
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <TextBlock Text="Book Title" VerticalAlignment="Center" Margin="5" />
        <TextBlock Text="ISBN-13" VerticalAlignment="Center" Margin="5" Grid.Row="1" />
        <TextBox Text="{Binding Title}" Height="24" Margin="5" Grid.Column="1" />
        <TextBox Text="{Binding ISBN}" Height="24" Margin="5" Grid.Column="1" Grid.Row="1" />
        <TextBlock Text="Book Title" VerticalAlignment="Center" Margin="5" Grid.Row="2" />
        <TextBlock Text="ISBN-13" VerticalAlignment="Center" Margin="5" Grid.Row="3" />
        <TextBox Text="{Binding Title}" Height="24" Margin="5" Grid.Column="1" Grid.Row="2" />
        <TextBox Text="{Binding ISBN}" Height="24" Margin="5" Grid.Column="1" Grid.Row="3" />
    </Grid>
  3. 接着,编辑后置代码文件MainPage.xaml.cs.为应用程序添加一个Loaded事件处理程序.
  4. 添加一个类来定义Book对象.
  5. 有了Book对象的定义后,创建一个Book的实例同时把它设置到LayoutRoot的DataContext属性里.代码如下:
    public MainPage() {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(MainPage_Loaded);
        }
    
        void MainPage_Loaded(object sender, RoutedEventArgs e) {
            var b = new Book {
                Title = "Beginning Silverlight 4: From Novice to Professional",
                ISBN = "978-1430229889"
            };
            this.LayoutRoot.DataContext = b;
        }
    }
    
    public class Book
    {
        public string Title { get; set; }
        public string ISBN { get; set; }
    } 

    当你要为不同的控件设置绑定定义时,控件不会知道到哪里取得数据.这个DataContext属性就是为控件设置参与数据绑定的数据上下文.这个DataContext属性可以直接在控件里设置.如果控件没有指定DataContext属性,它会到父控件里查找数据上下文.这个模型的一个好处是如果你查看页面的XANL,你会很容易地发现控件是从哪里获得数据的.这样提供了最大程度的代码分离,允许设计师设置XAML的UI,同时开发者与设计师可以协同工作,以定义控件怎么绑定到数据源.

  6. 到这里,你会看到如下所示效果.
    image

  7. 在程序运行时,在第一个Textbox中改变book的title为"Beginning Silverlight".
    你可能会认为因为第三个Textbox是绑定到相同数据的,所以第三个Textbox会自动更新.但是要实现这种双向绑定要做两件事.
    第一个问题是,当前,这个Book类不支持当它的属性改变时通知绑定客户端.换句话说,当Book的一个属性改变时,Book类不会把这个属性的改变通知到Textbox的实例.你可以为每个属性添加一个change事件来实现.这样做很糟糕;好彩,这里有一个接口,当类实现这个接口时可以处理这件事.这个接口被称为INotifyPropertyChanged.让我们使用它.
    代码如下:

    public class Book : INotifyPropertyChanged
    {
        private string _Title;
        public string Title {
            get { return _Title; }
            set {
                _Title = value;
                FirePropertyChanged("Title");
            }
        }
    
        private string _ISBN;
        public string ISBN {
            get { return _ISBN; }
            set {
                _ISBN = value;
                FirePropertyChanged("ISBN");
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
    
        void FirePropertyChanged(string property) {
            if (PropertyChanged != null) {
                PropertyChanged(this,
                    new PropertyChangedEventArgs(property));
            }
        }
    }

    修改Book,继承并实现INotifyPropertyChanged接口.注意需要using System.ComponentModel.接着,创建一个辅助方法类触发事件,在set方法里调用.还有把简单的属性定义改为添加一个私有字段形式的完整属性定义.
    这样完成后,你的类已设为可把Title和ISBN属性改变通知到绑定客户端.但是你需要在做多一步.默认,当你绑定数据源到目标时,BindingMode是单向的(OneWay)绑定,意思是数据源会把数据发送到目标,但是目标不会发送数据回数据源,为了获得数据来更新数据源,你需要实现双向绑定(TwoWay).

  8. 要改为双向绑定,要在控件定义绑定时添加Mode=TwoWay参数.如下:

    <TextBox Text="{Binding Title,Mode=TwoWay}" Height="24" Margin="5" Grid.Column="1" />
  9. 运行应用程序,改变第一个Textbox,把焦点离开控件.你会发现触发了双向绑定.第三个Textbox也会更新.
    image

恭喜贺喜!你刚刚创建了一个实现了双向绑定的Silverlight应用程序.现在我们去看看绑定列表数据到Silverlight提供的两个控件:DataGrid和ListBox.

元素到元素的绑定

除了绑定数据外,元素可以直接绑定到其它元素,这意味着可以改进你代码的可读性和效率.绑定一个元素的语法与绑定数据非常相似,唯一的不同是绑定的ElementName要明确指定,这与给设置元素设置ItemsSource属性非常相似.例如,如果你想绑定一个控件的IsEnabled属性到一个checkbox的IsChecked属性.绑定语法如下:

<CheckBox Name="HadRead"/>
<Button IsEnabled="{Binding IsChecked,Mode=OneWay,ElementName=HadRead}"/>

注意,这个绑定就像是绑定到一个数据源一样,除了我们添加了ElementName=HadRead.让我们在练习中试试.

练习:元素绑定元素

为了解释Silverlight中元素到元素的绑定,让我们建立一个非常简单的应用程序.这个应用程序只包含一个按钮和checkbox.当checkbox选中时,按钮可用,checkbox没有选中时,按钮不可用.Go.

  1. 使用VS2010创建一个名为ElementBinding的Silverlight应用程序.
  2. 在根表格控件里添加一个StackPanel,添加一个ToggleButton和一个名为EnableButton的CheckBox到StackPanel里.
  3. next,我们需要绑定ToggleButton的IsEnabled属性到Checkbox的IsChecked属性,我们按照前面的知识来实现单向绑定.代码如下:
    <Grid x:Name="LayoutRoot" Background="White">
        <StackPanel Margin="20">
                <ToggleButton Margin="5" Content="Click to Toggle" IsEnabled="{Binding IsChecked, Mode=OneWay, ElementName=EnableButton}" />
                <CheckBox x:Name="EnableButton" IsChecked="true" Margin="5" Content="Enable Button" />
        </StackPanel>
    </Grid>
  4. 就这样.这个demo里不需要任何代码.运行程序即可看到效果:
    image

DataGrid控件

数据表格类型的控件已经有很多年历史同时也是开发者用于显示大量数据的首选.Silverlight提供的DataGrid控件不只是一个标准的数据表格,它还包含了大量在以前只会在第三方表格组件才有的用户功能.例如,Silverlight的DataGrid控件可以调整表格列的大小和排序表格列.

练习:创建一个简单的DataGrid

让我们通过一个简单的例子学习DataGrid.

  1. 使用VS2010创建一个名为SimpleDatabaseGrid的Silverlight应用程序.
  2. 给你的应用程序添加一个DataGrid.这样就简单地在XAML里添加了一个DataGrid到你的根表格控件.到设计视图里右击DataGrid,选择"重置视图"=>"全部",然后给DataGrid命名为grid.注意,默认,表格控件的AutoGenerateColumns属性是true.如果你已经手动睇设置了列,你可能想设置这和属性为false.但是,因为你想这个表格自动生成列,你可以简单地忽略这个属性(删除掉 AutoGenerateColumns="False").DataGrid的代码如下:
    <Grid x:Name="LayoutRoot" Background="White">
        <sdk:DataGrid AutoGenerateColumns="False" Name="dataGrid1" />
    </Grid>
  3. next,创建一个会绑定给DataGrid的类.名为GridData,有三个属性:Name,Age,Male.为了简单起见,创建一个会返回一个ObservableCollection(需要引用System.Collections.ObjectModel)包含简单数据的静态方法,数据会绑定到DataGrid.
  4. 现在你哋XAML和类都定义好了,可以把它们连接起来.首先添加Loaded事件处理程序.然后把数据绑定到DataGrid的ItemsSource属性,代码如下:
    public partial class MainPage : UserControl
    {
        public MainPage() {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(MainPage_Loaded);
        }
    
        void MainPage_Loaded(object sender, RoutedEventArgs e) {
            this.grid.ItemsSource = GridData.GetData();
        }
    }
    public class GridData
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public bool Male { get; set; }
    
        public static ObservableCollection<GridData> GetData() {
            return new ObservableCollection<GridData> {
                new GridData{Name = "John Doe", Age = 30, Male = true},
                new GridData{Name = "Jane Doe", Age = 32, Male = false},
                new GridData{Name = "Jason Smith", Age = 54, Male = true},
                new GridData{Name = "Kayli Jayne", Age = 25, Male = false}
            };
        }
    }

    结果如下:
    image

    让我们花一些时间来研究一下这个DataGrid的特性.首先,如果你点击任何列表头,你会发现它会自动排列那一列.
    第二,如果你把鼠标放到列的边缘,你可以使用鼠标点击和拖动来调整列表的大小.重申,这个功能是DataGrid免费提供的富客户端功能.
    最后,如果你点击和保持鼠标在一列的头部,然后拖到左或右边的列可以调整列顺序.
    你会肯定会同意,实在太好了,一行代码就可以得到这么强大的功能:
    <sdk:DataGrid Margin="10" Name="grid" />

列集合

在上一个例子,你允许DataGrid基于绑定的数据自动生成列.这并不是新的概念,ASP.NET的表格数据组件就是这样的.但你想要对创建的列添加一些额外的控制时应该怎么办呢?如果添加的列包含一些复杂的信息怎么办呢?例如显示一张图片?你可以首先设置AutoGenerateColumns属性为false,然后手动地创建列.

在DataGrid里定义列是使用Columns集合.下面是在XAML里设置Columns集合的一个例子.注意,它设置了AutoGenerateColumns属性为false,如果你忽略了这个,你就会在Columns里额外地多了一些自动创建的列.

<Grid x:Name="LayoutRoot" Background="White">
    <sdk:DataGrid Margin="10" AutoGenerateColumns="False" Name="grid">
        <sdk:DataGrid.Columns>
        </sdk:DataGrid.Columns>
    </sdk:DataGrid>
</Grid>

你可以放置三列到Columns集合里:一个文本列(DataGridTextColumn),一个checkbox列(DataGridCheckBoxColumn),和一个模版列(DataGridTemplateColumn).所有这些列的类型都是继承自DataGridColumn.下面是一些这三个列类型值得注意的一些属性.

属性 描述
CanUserReorder 是否可以让用户拖动列虫排序它们
CanUserResize 是否可以让用户用鼠标调整列宽度
DisplayIndex 决定在DatabaseGrid的列顺序.
Header 定义列表头的内容
IsReadOnly 决定列是否可以被用户编辑
MaxWidth 用px设置列的最大宽度
MinWidth 用px设置列的最小宽度
Visibility 决定用户是否能看到列
Width 设置列的宽度,或者可以在automatic sizing模式里被自动设置

DataGridTextColumn

DataGridTextColumn在你的表格里定义一列来显示简单的文本.这个与ASP.NET的DataGrid的BoundColumn等价.DataGridTextColumn首先要设置的属性是Header,决定列头部要显示的文本,还有DisplayMemberBinding()属性,决定要绑定数据源的那个属性要绑定到列.下面是一个例子:(我没有找到DisplayMemberBinding,只有Binding属性)

<Grid x:Name="LayoutRoot" Background="White">
    <sdk:DataGrid Margin="10" AutoGenerateColumns="False" Name="grid">
        <sdk:DataGrid.Columns>
            <sdk:DataGridTextColumn Header="姓名" Binding="{Binding Name}"/>
        </sdk:DataGrid.Columns>
    </sdk:DataGrid>
</Grid>

DataGridCheckBoxColumn

没错,就像你想的一样,DataGridCheckBoxColumn会包含一个Checkbox.如果你有想在表格里用Checkbox显示的数据,这就是那个控件.这里就是一个使用DataGridCheckBoxColumn的例子,列标题为Male?,绑定数据源的Male属性:

<sdk:DataGrid Margin="10" AutoGenerateColumns="False" Name="grid">
    <sdk:DataGrid.Columns>
        <sdk:DataGridCheckBoxColumn Header="Male?" Binding="{Binding Male}"/>
    </sdk:DataGrid.Columns>
</sdk:DataGrid>

DataGridTemplateColumn

如果你想你的表格列显示的既不是简单的文本,也不是一个Checkbox,DataGridTemplateColumn提供一个一种方式来让你自己定义列的内容.DataGridTemplateColumn可以包含一个CellTemplate和CellEditingTemplate,这个用来决定什么内容会显示和决定表格是正常模式还是编辑模式.

注意,当你要在其它类型(DataGridTemplateColumn)的DataGrid列得到诸如自动排序的特性时,需要额外添加来允许排序.

让我们来考虑一个例子,有两个字段:FirstName和LastName.假设你现在在正常的视图模式,你想数据在两个TextBlock里并列显示.但是当编辑这个列时,你想显示两个TextBox控件,允许用户分别显示FirstName和LastName.

<sdk:DataGrid Margin="10" AutoGenerateColumns="False" Name="grid">
    <sdk:DataGrid.Columns>
        <sdk:DataGridTemplateColumn Header="姓名">
            <sdk:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Padding="5,0,5,0" Text="{Binding FirstName}"/>
                        <TextBlock Text="{Binding LastName}"/>
                    </StackPanel>
                </DataTemplate>
            </sdk:DataGridTemplateColumn.CellTemplate>
            <sdk:DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBox Padding="5,0,5,0" Text="{Binding FirstName}"/>
                        <TextBox Text="{Binding LastName}"/>
                    </StackPanel>
                </DataTemplate>
            </sdk:DataGridTemplateColumn.CellEditingTemplate>
        </sdk:DataGridTemplateColumn>
    </sdk:DataGrid.Columns>
</sdk:DataGrid>

练习:用自定义列绑定DataGrid

我想,把扑克的起手牌绑定到一个DataGrid应该挺好玩的.如果你在电视里看到过别人打扑克,你最有可能听到有关选手的事情是"pocket rockets"和"cowboys".这是它们给起手牌的一个简单的绰号.要做的就是用DataGrid显示这起手牌.

  1. 使用VS2010创建一个名为DataGridCustomColumns的Silverlight应用程序.
  2. 新建一个名为StartingHands.cs的类文件.
  3. 现在定义StartingHands类,它包含4个属性:NickName,Notes,Card1和Card2.还有添加一个静态的方法用于返回一些ObservableCollection类型的StartingHands实例数据.代码如下:
    public class StartingHands
    {
        public string NickName { get; set; }
        public string Notes { get; set; }
        public string Card1 { get; set; }
        public string Card2 { get; set; }
    
        public static ObservableCollection<StartingHands> GetHands() {
            return new ObservableCollection<StartingHands>(){
                      new StartingHands{ NickName = "Big Slick", Notes = "Also referred to as Anna Kournikova.", Card1 = "As", Card2 = "Ks" },
                      new StartingHands{ NickName = "Pocket Rockets",Notes = "Also referred to as Bullets.", Card1 = "As", Card2 = "Ad" },
                      new StartingHands{ NickName = "Blackjack",Notes = "The casino game blackjack.",Card1 = "As", Card2 = "Js" },
                      new StartingHands{ NickName = "Cowboys",Notes = "Also referred to as King Kong",Card1 = "Ks",Card2 = "Kd"},
                      new StartingHands{ NickName = "Doyle Brunson",Notes = "Named after poker great Doyle Brunson",Card1 = "Ts", Card2 = "2s"}
            };
        }
    }
  4. 回到MainPage.xaml文件,把UserControl的宽度改为500,双击工具箱添加一个名为grdData的DataGrid到根Grid控件.设置DataGrid的AutoGenerateColumns为False.
  5. next,在DataGrid里定义列.添加DataGrid.Columns来定义列.
    DataGrid的第一行包含手牌的离歌Card.要实现这个,可以使用DataGridTemplateColumn.在DataGridTemplateColumn里,添加一个CellTemplate来包含一个有两列的表格,每一列都包含一个Border,Rectangle和TextBlock,每一个都会重叠在一起(组成一个圆角矩形).代码如下:
    <sdk:DataGrid AutoGenerateColumns="False" Name="grdGrid">
        <sdk:DataGrid.Columns>
            <sdk:DataGridTemplateColumn Header="Hand">
                <sdk:DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition/>
                                <ColumnDefinition/>
                            </Grid.ColumnDefinitions>
                            <Border Margin="2" CornerRadius="4" BorderBrush="Black" BorderThickness="1"/>
                            <Rectangle Margin="4" Fill="White" Grid.Column="0"/>
                            <Border Margin="2" CornerRadius="4" BorderBrush="Black" BorderThickness="1" Grid.Column="1" />
                            <Rectangle Margin="4" Fill="White" Grid.Column="1" />
                            <TextBlock Text="{Binding Card1}" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="0" />
                            <TextBlock Text="{Binding Card2}" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="1" />
                        </Grid>
                    </DataTemplate>
                </sdk:DataGridTemplateColumn.CellTemplate>
            </sdk:DataGridTemplateColumn>
            <sdk:DataGridTextColumn Header="NickName" Binding="{Binding NickName}"/>
            <sdk:DataGridTextColumn Header="Notes" Binding="{Binding Notes}"/>
        </sdk:DataGrid.Columns>
    </sdk:DataGrid>
  6. 最后,把数据源和控件连接起来.转到MainPage.xaml.cs文件和添加Loaded事件,只需简单地设置DataGrid的ItemsSource属性等于StartingHands.GetHands() 返回的数据.代码如下:

    public MainPage() {
        InitializeComponent();
        this.Loaded += new RoutedEventHandler(MainPage_Loaded);
    }
    
    void MainPage_Loaded(object sender, RoutedEventArgs e) {
        this.grdGrid.ItemsSource = StartingHands.GetHands();
    }
  7. 编译和运行应用程序.如果正常的话,得到如下效果:
    image

就这完成了DataGrid使用自定义列的例子.当然,在实际的应用程序里,你会用其他地方获取这些手牌的数据,例如一个web服务或者一个xam文件.

ListBox控件

以往,一个列表类型控件被认为是编程中最普通的控件之一,没有比下拉框更特别的控件了.但是,在Silverlight里,有些变化.ListBox可能是其中一个用于显示列表数据的最灵活的控件.实际上,参考ASP.NET的控件,Silverlight的ListBox更像ASP.NET的DataList,而不是ASP.NET的ListBox控件.让我们偷偷看看这个强大的控件.

默认和自定义的ListBox子项

如果我们把前面再前面那个例子的Person数据绑定到ListBox,你会发现.默认,ListBox真的只是一个标准的ListBox.

ListBox Margin="10" x:Name="list" DisplayMemberPath="Name"/>

这个ListBox里,你可能注意到有个额外的属性就是DisplayMemberPath.如果你只是定义一个简单的字符串ListBox,ListBox控件需要知道显示那个数据.因为Person类包含三个属性(Name,Age和Male),我们需要告诉它要显示Name.结果如下图:

image然而,ListBox控件比只显示简单的字符串强大得多.实际,如果你为ListBox定义一个自定义模版,你可以用一个更有趣的方式呈现数据.下面是一个例子:

<ListBox Margin="10" x:Name="list">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock FontSize="17" FontWeight="Bold" Text="{Binding Name}"/>
                <StackPanel Margin="5,0,0,0" Orientation="Horizontal">
                    <TextBlock Text="Age: "/>
                    <TextBlock Text="{Binding Age}"/>
                    <TextBlock Text=",Male:"/>
                    <TextBlock Text="{Binding Male}"/>
                </StackPanel>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

结果如下图:

image

练习:绑定自定义内容到ListBox

让我们用让一个练习的数据来显示扑克的起手牌,看看用ListBox显示有多Cool.

  1. 使用VS2010创建一个名为ListBoxCustom的Silverlight应用程序.
  2. 帮让一个练习的StartingHands.cs复制过来,修改它的命名空间为ListBoxCustom.
  3. Next,你需要定义ListBox的ItemTemplate.这个ItemTemplate会包含一个水平发现的StackPanel,这个StackPanel包含一个表格来显示两个Card.下面是代码:
    <Grid x:Name="LayoutRoot" Background="White">
        <ListBox Margin="10" x:Name="list">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Margin="5" Orientation="Horizontal">
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition/>
                                <ColumnDefinition/>
                            </Grid.ColumnDefinitions>
                            <Border Margin="2" CornerRadius="4" BorderBrush="Black" BorderThickness="1" />
                            <Rectangle Margin="4" Fill="White" Grid.Column="0" Width="20" />
                            <Border Margin="2" CornerRadius="4" BorderBrush="Black" BorderThickness="1" Grid.Column="1" />
                            <Rectangle Margin="4" Fill="White" Grid.Column="1" Width="20" />
                            <TextBlock Text="{Binding Card1}" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="0" />
                            <TextBlock Text="{Binding Card2}" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="1" />
                        </Grid>
                        <StackPanel Orientation="Vertical">
                            <TextBlock Text="{Binding NickName}" FontSize="16" FontWeight="Bold"/>
                            <TextBlock Text="{Binding Notes}"/>
                        </StackPanel>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
  4. 国际惯例,到后置代码里在Loaded事件里绑定数据源:
    public MainPage() {
        InitializeComponent();
        this.Loaded += new RoutedEventHandler(MainPage_Loaded);
    }
    
    void MainPage_Loaded(object sender, RoutedEventArgs e) {
        this.list.ItemsSource = StartingHands.GetHands();
    }
  5. 最后,效果如下:
    image
    正如你所看到的,ListBox控件的灵活性可以让开发者用非常cool的方式呈现列表数据.

Silverlight 4的新绑定特性

在前面你也看到元素绑定需求的基本类型变为DependencyObject类.另外,在Silverlight 4里还有大量的关于数据绑定的新特性,这里其中的三个.三个都是新的绑定扩展.第一个是字符串格式化.然后是TargetNullValue和FallBackValue的扩展.

数据绑定和字符串格式化

在前面版本的Silverlight里,为了在数据绑定时格式话字符串,你需要在绑定里写一个转换器.在Silverlight 4,你现在只需在XAML里就可以格式化数据.增加字符串的格式就是简单地在XAML里增加一个StringFormat扩展.这个StringFormat扩展支持String类型的Format方法一样的参数.

考虑下面XAML,这里显示4个文本框,所有4个在后置代码里绑定相同的属性.不同的是,每个都在StringFormat扩展的基础上实现不同的显示.第一个显示原始数据,第二个显示三位小数,第三个用科学记数法显示,用货币格式显示第四个:

<Grid x:Name="LayoutRoot" Background="White">
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
        <TextBox Margin="5" Grid.Row="0" Text="{Binding someDecimal}"/>
        <TextBox Margin="5" Grid.Row="1" Text="{Binding someDecimal, StringFormat='##.###'}"/>
        <TextBox Margin="5" Grid.Row="2" Text="{Binding someDecimal, StringFormat='E'}"/>
        <TextBox Margin="5" Grid.Row="3" Text="{Binding someDecimal,StringFormat='c'}"/>
</Grid>

结果如下:

image

TargetNullValue和FallBackValue扩展

在Silverlight 4里添加了两个扩展到基础绑定类里:TargetNullValue和FallBackValue.这些扩展允许你当绑定的数据不是期望的数据时指定情况来显示.TargetNullValue提供一个当绑定值为空时使用的值,下面是一个使用例子:

<TextBox Margin="5" Grid.Row="0" Text="{Binding someDecimal, TargetNullValue='空值!'}"/>

FallBackValue是当绑定的数据丢失或者如果出现不匹配时使用.例子如下:

<TextBox Margin="5" Grid.Row="0" Text="{Binding someDecimal, FallbackValue='找不到值!'}"/>

结语

这一章,你看到怎么绑定数据给Silverlight的列表数据控件.然后,你研究了两个典型的控件:DataGrid和ListBox.你看到这些控件的灵活性和可以用很神奇的方式显示数据.但是所有的例子都是静态的数据,在实际的项目里,数据可能来自web服务或者一个xml文件.

posted @ 2011-12-18 18:13  zhangweiwen  阅读(2104)  评论(3编辑  收藏  举报