下面我們接着WindowsPhone基礎瑣碎總結-----數據綁定(一)繼續探究數據綁定。
四、集合對象的數據綁定
通過上篇一個小例子我們基本了解了數據綁定原理,數值轉換器的使用,數據綁定模式等相關知識,可是我們該如何把批量的數據綁定到UI呢?即綁定到數據集合對象(如ListBox等)。實現批量數據的綁定才是數據綁定的最大魅力所在。需要說明的是在集合對象的數據綁定中,綁定源可以是任意實現了枚舉接口(IEnumerator)集合對象,而綁定目標一般是ItemControl控件類型的UI元素,本文我們將使用ListBox作為綁定的UI。
在開始綁定集合前我們先了解下ItemControl控件,這類的控件有兩個重要的屬性一個是:ItemsSource和DisplayMemberPath,其實很好理解的,我們類比數據綁定(一)中介紹的綁定對象(Binding)的Source和Path屬性。用法和道理基本一樣我不在多說,不懂的參考我上一篇日志。
1、顯示單列的信息
再動手做項目之前我們先考慮下需求,假如現在我們想顯示一列學生姓名的信息,我們很快想到用ListBox控件,可是我們該如何把我們的道德姓名集合綁定到ListBox控件上呢?下面我們一步步介紹,不過為了為后面更復雜的綁定打基礎,我們綁定一列班級信息到另一個頁面。當然我們還是首先設計下UI,為了連貫性,我們繼續在數據綁定(一)項目基礎上繼續。
第一步:我們首先新建一個頁面用來顯示班級信息,頁面名字為SingleColumnPage.xaml,界面上一個button和listbox控件,如下圖:
代碼如下:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Button Content="顯示班級名稱信息" Height="72" HorizontalAlignment="Left" Margin="99,420,0,0" Name="buttonShowName" VerticalAlignment="Top" Width="260" Click="buttonShowName_Click" />
<ListBox Height="349" HorizontalAlignment="Left" Margin="6,6,0,0" Name="listBox1" VerticalAlignment="Top" Width="444" />
</Grid>
然后我們主界面添加一個Button用於跳轉,如下圖:
代碼如下(我順便加上了頁面跳轉的后台代碼):
private void button1_Click(object sender, RoutedEventArgs e) { NavigationService.Navigate(new Uri("/SingleColumnPage.xaml",UriKind.RelativeOrAbsolute)); }
第二步:下面我們開始后台的編寫,既然要綁定班級名稱信息,當然還要建立一個班級信息類StudentClass。代碼如下:
public class StudentClass { public string ClassName { set; get; } }
僅僅這一句就夠舉例用了,當然你可以添加更多需要的信息。
下面我們單擊顯示班級名稱,在后台寫如下代碼:
private void buttonShowName_Click(object sender, RoutedEventArgs e) { ObservableCollection<StudentClass> Classes = new ObservableCollection<StudentClass>() { new StudentClass{ClassName=".Net1班"}, new StudentClass{ClassName=".Net2班"}, new StudentClass{ClassName=".Net3班"}, new StudentClass{ClassName=".Net2班"}, new StudentClass{ClassName="數據庫1班"}, new StudentClass{ClassName="數據庫2班"}, new StudentClass{ClassName="數據庫3班"}, new StudentClass{ClassName="數據庫4班"}, }; this.listBox1.ItemsSource = Classes; this.listBox1.DisplayMemberPath = "ClassName"; }
現在你也許會問ObservableCollection是什么?有什么用?我們先做個插曲介紹下:如果你認真看過上篇博客數據綁定(一)一定還記得我們在介紹OneWay和TwoWay的時候讓Student類實現了INotifyPropertyChanged接口。現在我要說明的是如果為一個集合實現OneWay和TwoWay的時候綁定的時候我們不僅僅要實現INotifyPropertyChanged接口,還得實現INotifyCollectionChanged接口,以便於將集合中的元素的更改通知到目標對象。這當然是繁瑣的,有沒有捷徑呢?幸運的是SilverLight已經內置了一個實現了這兩個接口的ObservableCollection<T>泛型類,位於using System.Collections.ObjectModel,命名空間下。一般情況下我們用這個類代替Collection<T>類即可實現更新通知。 了解這個類之后下面就好辦了我們只要設定listBox控件的兩個屬性(ItemsSource 和)即可,代碼已經在上面展示出來了。
現在運行程序后轉到顯示單列數據頁面並單擊顯示班級信息如下:
2、綁定多列數據
此時也許我們還想有這樣一個需求,我想要顯示多列,比如在一個頁面中顯示姓名,性別,年齡等,該怎么辦呢?由於WindowsPhone沒有像DataGridView類似的控件,所以我們必須使用自定義樣式來實現我們想要的數據了。實現自定義樣式需要設計數據模板這個知識了,我不打算做過多介紹本博文重在數據綁定,不能跑題,但是簡單的理解就是一個簡單的樣式模板,每個學生信息都會用同樣的樣式,為了重用和布局的統一才抽象出了這個數據模板類:DataTemplate。新建一個學生信息頁面,過程同顯示班級信息頁面, 我直接給AXML頁面代碼如下:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Button Content="顯示學生信息" Height="72" HorizontalAlignment="Left" Margin="12,459,0,0" Name="buttonShowStudentsInfo" VerticalAlignment="Top" Width="417" Click="buttonShowStudentsInfo_Click" />
<ListBox Height="422" Width="480" HorizontalAlignment="Left" Margin="-17,5,0,0" Name="listBox1" VerticalAlignment="Top" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Height="120" HorizontalAlignment="Left" Width="480" VerticalAlignment="Top" Orientation="Horizontal">
<Image Stretch="Fill" Height="80" Width="120" Source="{Binding Path=Picture, Converter={StaticResource StringToBitMapImageKey}}"></Image>
<TextBlock Height="35" Width="120" Text="{Binding Path=Name}"></TextBlock>
<TextBlock Height="35" Width="120" Text="{Binding Path=Sex}"></TextBlock>
<TextBlock Height="35" Width="120" Text="{Binding Path=Birthday}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
上面的代碼數據綁定方面我在第一篇博客都介紹過,應該都能夠理解。下面我給出ShowStudentsInfoPage.xaml頁面的后台代碼如下:
需要引入命名空間using System.Collections.ObjectModel;
public partial class ShowStudentsInfoPage : PhoneApplicationPage { //構造學生信息
ObservableCollection<Student> studentsInfo = new ObservableCollection<Student>() { new Student(){Picture="MM.jpg",Name="Marry",Sex="女",Birthday="1988-5-1"}, new Student(){Picture="MM.jpg",Name="小紅",Sex="女",Birthday="1989-5-1"}, new Student(){Picture="GG.jpg",Name="GavinDream",Sex="男",Birthday="1989-6-30"}, new Student(){Picture="GG.jpg",Name="小明",Sex="男",Birthday="1989-8-1"}, }; public ShowStudentsInfoPage() { InitializeComponent(); } //綁定學生信息
private void buttonShowStudentsInfo_Click(object sender, RoutedEventArgs e) { this.listBox1.ItemsSource = studentsInfo; } }
運行效果如下:
解釋一下,也就是說當我們選中Net1時候ListBox中顯示Net1班的學生信息,當我們選中Net2時候ListBox中顯示Net2班的學生信息;此刻你也許知道什么是分層數據顯示了,不過你更容易想到我們可是使用事件解決此問題啊,是的,可以用事件來達到同樣的效果,但是我們通常通過集合視圖類(CollectionViewSource)可能會更方便的實現綁定。下面我具體介紹:
首先了解下CollectionViewSource:是一個集合視圖類,可以根據不同的篩選、排序條件建立一個集合對象的多個視圖,其實就像關系型數據庫中的同一張表中建立多個視圖一樣(如果還是理解不了,我們舉個具體的例子:就像一個班級肯定是個學生的集合,學生可以根據不同條件分類,如按:男,女,20歲以上等條件分類,其實每一類得到的另一個學生集合就相當於一個視圖。這些視圖的源來自同一個集合就是這個班級)。下面具體介紹如何進行分層的數據綁定:
第一步:設計UI,如上圖,代碼我會在設置綁定目標時貼出來
第二步:構造數據源對象
既然還是數據綁定,那么還得先找到數據源,想把學生信息和班級名稱聯系起來,當然還得需要一個新的類,暫且起名為:ClassAndInfo吧,新建類,代碼如下:
public class ClassAndInfo { public string ClassName { set; get; } public ObservableCollection<Student> studentInfo { set; get; } }
這個類是聯系者,那么我們現在就可以開始構造一個數據源了,這個數據源必須存有學生信息和所在班級姓名,現在我們在新建一個類用於初始化數據源,類名為StudentsInfoList,如果把此類當做數據源集合必須繼承一個泛型類ObservableCollection<T>,當然此次是繼承
ObservableCollection<ClassAndInfo>;因為數據源為ClassAndInfo類型;代碼如下:
public class StudentsInfoList:ObservableCollection<ClassAndInfo> { ClassAndInfo infolist1 = new ClassAndInfo() { ClassName = "Net1", studentInfo = new ObservableCollection<Student>() { new Student(){Picture="MM.jpg",Name="Marry",Sex="女",Birthday="1988-5-1"}, new Student(){Picture="MM.jpg",Name="小紅",Sex="女",Birthday="1989-5-1"}, new Student(){Picture="GG.jpg",Name="GavinDream",Sex="男",Birthday="1989-6-30"}, new Student(){Picture="GG.jpg",Name="小明",Sex="男",Birthday="1989-8-1"}, } }; ClassAndInfo infolist2 = new ClassAndInfo() { ClassName = "Net2", studentInfo = new ObservableCollection<Student>() { new Student(){Picture="MM.jpg",Name="張三",Sex="女",Birthday="1988-5-1"}, new Student(){Picture="MM.jpg",Name="李四",Sex="女",Birthday="1989-5-1"}, new Student(){Picture="GG.jpg",Name="王五",Sex="男",Birthday="1989-6-30"}, new Student(){Picture="GG.jpg",Name="馬六",Sex="男",Birthday="1989-8-1"}, } }; public StudentsInfoList() { //這兩句是必須的,將數據對象加入當前類
this.Add(infolist1); this.Add(infolist2); } }
到此數據源解決了,那么就該數據綁定到UI了,xalm頁面代碼如下:
<!--ContentPanel - 在此處放置其他內容-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<TextBlock Height="37" HorizontalAlignment="Left" Margin="12,20,0,0" Name="textBlock1" Text="請選擇學生班級:" VerticalAlignment="Top" Width="163" />
<ListBox Name="classname" DisplayMemberPath="ClassName" ItemsSource="{Binding Source={StaticResource StudentInfoListView}}" Margin="12,64,6,434"></ListBox>
<TextBlock Height="37" HorizontalAlignment="Left" Margin="12,179,0,0" Name="textBlock2" Text="{Binding Source={StaticResource StudentInfoListView},Path=ClassName}" VerticalAlignment="Top" Width="72" Foreground="Red"/>
<TextBlock Height="37" HorizontalAlignment="Left" Margin="69,179,0,0" Name="textBlock3" Text="學生信息" VerticalAlignment="Top" Width="85"/>
<ListBox ItemsSource="{Binding Path=studentInfo,Source={StaticResource StudentInfoListView}}" Height="379" HorizontalAlignment="Left" Margin="0,222,0,0" Name="listBox1" VerticalAlignment="Top" Width="450" ItemTemplate="{StaticResource ListDataTemplate}" />
</Grid>
我需要給以上代碼說明的是:ItemsSource="{Binding Source={StaticResource StudentInfoListView}}",里面引用的靜態資源其實就是我們上面說的集合視圖,我將它作為靜態資源放在了xaml頁面里,便於引用;靜態資源代碼如下:
我們現在可以運行程序,效果如下:
擴展:此刻我們也許會想到數據上下文DataContext,我們是否能夠通過DataContext綁定到ListBox呢?我想是能的,那就試試吧:
我們把 this.listBox1.ItemsSource = studentsInfo; 換成 this.listBox1.DataContext = studentsInfo;運行程序,沒有像我想像的那樣,綁定沒有成功。經過調試和查找資料原來我們還得必須在Xaml頁面指定listBox1的ItemsSource屬性:ItemsSource="{Binding}",我認為其實這是再告訴編譯器,我要進行數據綁定了,可是現在還沒指定數據源(DataContext),一旦給我指定了DataContext我就去數據源中找應該綁定的屬性,暫且這樣理解吧。通過對比可以知道使用this.listBox1.ItemsSource = studentsInfo; 會更方便些。
3、分層數據顯示
通過綁定學生信息我們可以實現對集合數據的綁定,並且還能夠自定義UI樣式,也許在項目中多數情況這基本上已經夠用。但也可能會遇到這樣一個需求:分層顯示數據;這可能不好理解,那就拿此例子來說吧:我想把學生信息按班級分類,ListBox中顯示不同班級的學生信息。也許還不太明白我直接上圖吧:
解釋一下,也就是說當我們選中Net1時候ListBox中顯示Net1班的學生信息,當我們選中Net2時候ListBox中顯示Net2班的學生信息;此刻你也許知道什么是分層數據顯示了,不過你更容易想到我們可是使用事件解決此問題啊,是的,可以用事件來達到同樣的效果,但是我們通常通過集合視圖類(CollectionViewSource)可能會更方便的實現綁定。下面我具體介紹:
首先了解下CollectionViewSource:是一個集合視圖類,可以根據不同的篩選、排序條件建立一個集合對象的多個視圖,其實就像關系型數據庫中的同一張表中建立多個視圖一樣(如果還是理解不了,我們舉個具體的例子:就像一個班級肯定是個學生的集合,學生可以根據不同條件分類,如按:男,女,20歲以上等條件分類,其實每一類得到的另一個學生集合就相當於一個視圖。這些視圖的源來自同一個集合就是這個班級)。下面具體介紹如何進行分層的數據綁定:
第一步:設計UI,如上圖,代碼我會在設置綁定目標時貼出來
第二步:構造數據源對象
既然還是數據綁定,那么還得先找到數據源,想把學生信息和班級名稱聯系起來,當然還得需要一個新的類,暫且起名為:ClassAndInfo吧,新建類,代碼如下:
public class ClassAndInfo { public string ClassName { set; get; } public ObservableCollection<Student> studentInfo { set; get; } }
這個類是聯系者,那么我們現在就可以開始構造一個數據源了,這個數據源必須存有學生信息和所在班級姓名,現在我們在新建一個類用於初始化數據源,類名為StudentsInfoList,如果把此類當做數據源集合必須繼承一個泛型類ObservableCollection<T>,當然此次是繼承
ObservableCollection<ClassAndInfo>;因為數據源為ClassAndInfo類型;代碼如下:
public class StudentsInfoList:ObservableCollection<ClassAndInfo> { ClassAndInfo infolist1 = new ClassAndInfo() { ClassName = "Net1", studentInfo = new ObservableCollection<Student>() { new Student(){Picture="MM.jpg",Name="Marry",Sex="女",Birthday="1988-5-1"}, new Student(){Picture="MM.jpg",Name="小紅",Sex="女",Birthday="1989-5-1"}, new Student(){Picture="GG.jpg",Name="GavinDream",Sex="男",Birthday="1989-6-30"}, new Student(){Picture="GG.jpg",Name="小明",Sex="男",Birthday="1989-8-1"}, } }; ClassAndInfo infolist2 = new ClassAndInfo() { ClassName = "Net2", studentInfo = new ObservableCollection<Student>() { new Student(){Picture="MM.jpg",Name="張三",Sex="女",Birthday="1988-5-1"}, new Student(){Picture="MM.jpg",Name="李四",Sex="女",Birthday="1989-5-1"}, new Student(){Picture="GG.jpg",Name="王五",Sex="男",Birthday="1989-6-30"}, new Student(){Picture="GG.jpg",Name="馬六",Sex="男",Birthday="1989-8-1"}, } }; public StudentsInfoList() { //這兩句是必須的,將數據對象加入當前類
this.Add(infolist1); this.Add(infolist2); } }
到此數據源解決了,那么就該數據綁定到UI了,xalm頁面代碼如下:
<!--ContentPanel - 在此處放置其他內容-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<TextBlock Height="37" HorizontalAlignment="Left" Margin="12,20,0,0" Name="textBlock1" Text="請選擇學生班級:" VerticalAlignment="Top" Width="163" />
<ListBox Name="classname" DisplayMemberPath="ClassName" ItemsSource="{Binding Source={StaticResource StudentInfoListView}}" Margin="12,64,6,434"></ListBox>
<TextBlock Height="37" HorizontalAlignment="Left" Margin="12,179,0,0" Name="textBlock2" Text="{Binding Source={StaticResource StudentInfoListView},Path=ClassName}" VerticalAlignment="Top" Width="72" Foreground="Red"/>
<TextBlock Height="37" HorizontalAlignment="Left" Margin="69,179,0,0" Name="textBlock3" Text="學生信息" VerticalAlignment="Top" Width="85"/>
<ListBox ItemsSource="{Binding Path=studentInfo,Source={StaticResource StudentInfoListView}}" Height="379" HorizontalAlignment="Left" Margin="0,222,0,0" Name="listBox1" VerticalAlignment="Top" Width="450" ItemTemplate="{StaticResource ListDataTemplate}" />
</Grid>
我需要給以上代碼說明的是:ItemsSource="{Binding Source={StaticResource StudentInfoListView}}",里面引用的靜態資源其實就是我們上面說的集合視圖,我將它作為靜態資源放在了xaml頁面里,便於引用;靜態資源代碼如下:
<!--定義頁面資源-->
<phone:PhoneApplicationPage.Resources>
<local:StudentsInfoList x:Key="StudentInfoList"/>
<CollectionViewSource x:Key="StudentInfoListView" Source="{StaticResource StudentInfoList}"></CollectionViewSource>
<DataTemplate x:Key="ListDataTemplate">
<StackPanel Height="120" HorizontalAlignment="Left" Width="480" VerticalAlignment="Top" Orientation="Horizontal">
<Image Stretch="Fill" Height="80" Width="120" Source="{Binding Path=Picture, Converter={StaticResource StringToBitMapImageKey}}"></Image>
<TextBlock Height="35" Width="120" Text="{Binding Path=Name}"></TextBlock>
<TextBlock Height="35" Width="120" Text="{Binding Path=Sex}"></TextBlock>
<TextBlock Height="35" Width="120" Text="{Binding Path=Birthday}"></TextBlock>
</StackPanel>
</DataTemplate>
</phone:PhoneApplicationPage.Resources>
我們現在可以運行程序,效果如下:
點擊Net2
總結:利用三天的閑暇時間,終於把數據綁定(二)更新完了,對於windowsphone7自己也是剛剛接觸,所寫的僅僅是自己的瑣碎總結加入了自己的理解,文中的不足之處還請見諒,望各位園友批評指出。
---------------------------------------------------------------------------------------------------------------------------------------------
作者:GavinDream(GavinDream主頁 博客園)
出處:http://www.cnblogs.com/fuchongjundream/
任何轉載必須保留完整文章,在顯要地方顯示署名以及原文鏈接。如您有任何疑問或者授權方面的協商,請發郵件給我 或者 留言。