Windows App开发之集合控件与数据绑定
為ListView和GridView添加數(shù)據(jù)
ListView采用垂直堆疊得方式顯示數(shù)據(jù),而GridView則采用水平堆疊得方式。
長相的話嘛,它們都差不多。
<Grid Name="grid1" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><ListView x:Name="listView1" SelectionChanged="listView1_SelectionChanged"><x:String>Item 1</x:String><x:String>Item 2</x:String></ListView><GridView x:Name="gridView1" SelectionChanged="gridView1_SelectionChanged"><x:String>Item 1</x:String><x:String>Item 2</x:String></GridView></Grid>當然,也可以在后臺代碼上添加。我只是為了將它們放在一起比較而已,這些代碼堆一起肯定是很丑的。
ListView listView1 = new ListView(); listView1.Items.Add("Item 1"); listView1.Items.Add("Item 2"); listView1.Items.Add("Item 3"); listView1.SelectionChanged += listView1_SelectionChanged; grid1.Children.Add(listView1); GridView gridView1 = new GridView(); gridView1.Items.Add("Item 1"); gridView1.Items.Add("Item 2"); gridView1.SelectionChanged += gridView1_SelectionChanged; grid1.Children.Add(gridView1);如果只是像上面這樣來添加內(nèi)容會不會比較麻煩呢,我們也可以把這些Item 1、Item 2之類的全部放在List中。
List<String> itemsList = new List<string>(); itemsList.Add("Item 1"); itemsList.Add("Item 2");ListView listView1 = new ListView(); listView1.ItemsSource = itemsList; listView1.SelectionChanged += listView1_SelectionChanged;grid1.Children.Add(listView1);這樣一來所顯示的ListView就是兩行,非常簡陋,完全不能夠滿足要求。那么我們可以用它的ItemTemplate屬性來再里面添加一些東西,如下所示,我們可以在Grid中寫一個Image綁定頭像,用TextBlock綁定用戶的ID,再來一個TextBlock綁定用戶的消息,還可以來寫邊框呀什么的。而這些亂七八糟的Binding之類的,以后我們也會一起講的哦,現(xiàn)在只要它們是數(shù)據(jù)綁定就好。
<Page.Resources><CollectionViewSource x:Name="collectionVS" Source="{Binding Items}"/> </Page.Resources><Grid Name="grid1" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><ListView x:Name="listView1" ItemsSource="{Binding Source={StaticResource collectionVS}}"SelectionChanged="listView1_SelectionChanged"><ListView.ItemTemplate><DataTemplate><Grid></Grid></DataTemplate></ListView.ItemTemplate></ListView> </Grid>還可以像下面這樣哦,通過WrapGrid來決定這些Item的擺放方式。
<Grid Name="grid1" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><ListView VerticalAlignment="Bottom"><ListView.ItemsPanel><ItemsPanelTemplate><WrapGrid Orientation="Vertical" MaximumRowsOrColumns="2"/></ItemsPanelTemplate></ListView.ItemsPanel><Rectangle Height="100" Width="100" Fill="Wheat" /><Rectangle Height="100" Width="100" Fill="White" /><Rectangle Height="100" Width="100" Fill="Gainsboro" /><Rectangle Height="100" Width="100" Fill="Violet" /><Rectangle Height="100" Width="100" Fill="DarkBlue" /><Rectangle Height="100" Width="100" Fill="RosyBrown" /><Rectangle Height="100" Width="100" Fill="SaddleBrown" /><Rectangle Height="100" Width="100" Fill="AliceBlue" /><Rectangle Height="100" Width="100" Fill="Fuchsia" /><Rectangle Height="100" Width="100" Fill="Aqua" /><Rectangle Height="100" Width="100" Fill="Tan" /></ListView> </Grid>當然啦,對于ListView和GridView而言,知道用戶選擇了哪一項是很重要的。SelectionMode屬性決定了ListView和GridView的選擇模式:單個、多個、無、擴展。
下面這個函數(shù)將選擇的項給了selectedItems啦。我們還可以通過IsItemClickEnabled來啟用ListView和GridView的點擊事件,但是務(wù)必要注意將SelectionMode設(shè)置為None。
private void listView1_SelectionChanged(object sender, SelectionChangedEventArgs e) {selectedItems = (List<object>)e.AddedItems; }為ListView和GridViewt添加分組
本文承接“為ListView和GridView添加數(shù)據(jù)”。
在上一節(jié)中我們已經(jīng)了解了怎樣將數(shù)據(jù)綁定到ListView或GridView,但既然要用到這兩個控件往往是因為數(shù)據(jù)繁多,那么幾乎就不可避免的要讓其能夠分組。我們所綁定的數(shù)據(jù)源可能是項列表,其中的每個項甚至還有其自己的項,那么問題就來了。
一時不會也想不出什么宏偉的例子,就做一個簡單的鬧鐘的時間表的ListView和GridView吧。那么先在項目中添加一個類,最好在Shared下。內(nèi)容都是很簡易的,鬧鐘的標題、時間、備注等,為了增加一級目錄就加了一個AlarmMode,就算作學(xué)習(xí)和生活吧,學(xué)習(xí)生活兩不誤……
public class Alarm {public string Title { get; set; }public DateTime AlarmClockTime { get; set; }public string Description { get; set; }public string AlarmMode { get; set; } } public class AlarmMode {public AlarmMode(){alarmMode = new ObservableCollection<Alarm>();}public string Name { get; set; }public ObservableCollection<Alarm> alarmMode { get; private set; } }首先,先來定義一個全局的時間,然后在頁面加載時加載兩個函數(shù)(將在下一步定義)。
DateTime globalTime; protected override void OnNavigatedTo(NavigationEventArgs e) {DateTime.TryParse("1/1/2115", out globalTime);AddAlarm();AddAlarmMode(); }一大波數(shù)據(jù)正在靠近!
private void AddAlarm() {List<Alarm> listAlarm = new List<Alarm>();listAlarm.Add(new Alarm(){Title = "Alarm1",Description = "First Alarm",AlarmClockTime = globalTime.AddHours(1),AlarmMode = "Alarm In Life"});listAlarm.Add(new Alarm(){Title = "Alarm2",Description = "Second Alarm",AlarmClockTime = globalTime.AddHours(11),AlarmMode = "Alarm In Life"});listAlarm.Add(new Alarm(){Title = "Alarm3",Description = "Third Alarm",AlarmClockTime = globalTime.AddDays(1),AlarmMode = "Alarm In Life"});listAlarm.Add(new Alarm(){Title = "Alarm1",Description = "First Alarm",AlarmClockTime = globalTime.AddHours(12),AlarmMode = "Alarm In Study"});listAlarm.Add(new Alarm(){Title = "Alarm2",Description = "Second Alarm",AlarmClockTime = globalTime.AddHours(15),AlarmMode = "Alarm In Study"});listAlarm.Add(new Alarm(){Title = "Alarm3",Description = "Third Alarm",AlarmClockTime = globalTime.AddMonths(1),AlarmMode = "Alarm In Study"});ar alarmSetting = from ala in listAlarmgroup alaby ala.AlarmModeinto alaSettingorderby alaSetting.Keyselect alaSetting;collectionVSAlarm.Source = alarmSetting; }private void AddAlarmMode() {List<AlarmMode> listAlarmMode = new List<AlarmMode>();AlarmMode am1 = new AlarmMode();am1.Name = "Alarm In Life";am1.alarmMode.Add(new Alarm(){Title = "Alarm1",Description = "First Alarm",AlarmClockTime = globalTime.AddHours(1),});am1.alarmMode.Add(new Alarm(){Title = "Alarm2",Description = "Second Alarm",AlarmClockTime = globalTime.AddHours(11),});am1.alarmMode.Add(new Alarm(){Title = "Alarm3",Description = "Third Alarm",AlarmClockTime = globalTime.AddDays(1),});listAlarmMode.Add(am1);AlarmMode am2 = new AlarmMode();am2.Name = "Alarm In Study";am2.alarmMode.Add(new Alarm(){Title = "Alarm1",Description = "First Alarm",AlarmClockTime = globalTime.AddHours(12),});am2.alarmMode.Add(new Alarm(){Title = "Alarm2",Description = "Second Alarm",AlarmClockTime = globalTime.AddHours(15),});am2.alarmMode.Add(new Alarm(){Title = "Alarm3",Description = "Third Alarm",AlarmClockTime = globalTime.AddMonths(1),});listAlarmMode.Add(am2);collectionVSAlarmMode.Source = listAlarmMode; }這些數(shù)據(jù)都是亂七八糟啦,大家湊合著看。這是兩個函數(shù),數(shù)據(jù)我都是用List<>來定義的,將數(shù)據(jù)通過Add函數(shù)添加到listAlarm和listAlarmMode中即可。最后再從listAlarm中根據(jù)AlarmMode挑出數(shù)據(jù)到alaSetting,同時還要根據(jù)Key值進行排序最后選出并連接到collectionVSAlarm的Source屬性中。這個是需要在MainPage.xaml中定義的哦,就像
<UserControl.Resources><CollectionViewSource x:Name="collectionVSAlarm" IsSourceGrouped="True"/><CollectionViewSource x:Name="collectionVSAlarmMode" IsSourceGrouped="True" ItemsPath="alarmMode"/></UserControl.Resources>然后我們還需要創(chuàng)建一個ListGridGroupStyle類來繼承GroupStyleSelector,重載它的SelectGroupStyleCore方法,并且返回ListGridGroupStyleResource資源,這個資源在博客后文中有定義,其定義在App.xaml中。相應(yīng)的代碼如下咯:
public class ListGridGroupStyle : GroupStyleSelector{protected override GroupStyle SelectGroupStyleCore(object group, uint level){return (GroupStyle)App.Current.Resources["ListGridGroupStyleResource"];}}方法重載好之后就需要在前面的UserControl.Resources中加上以下這條代碼啦。
<local:ListGridGroupStyle x:Key="ListGridGroupStyleResource"/>然后我們來一系列的基本樣式到App.xaml中就好啦,關(guān)于資源文件的使用我們在后面會系統(tǒng)的來學(xué)習(xí)。這里的DataTemplate和GroupStyle都在資源字典中,前者是Template模板,后者是Style風(fēng)格。內(nèi)容的排版大家都隨意啦,記得設(shè)置好Key值。
<Application.Resources><ResourceDictionary><DataTemplate x:Key="dataTemplateListView"><StackPanel Width="700" Margin="10"><StackPanel Orientation="Horizontal"><TextBlock Text="{Binding Title}" FontWeight="Bold" Margin="12"/><TextBlock Text="{Binding AlarmClockTime}" TextWrapping="NoWrap" Margin="12"/><TextBlock Text="{Binding Description}" TextWrapping="NoWrap" Margin="12"/></StackPanel></StackPanel></DataTemplate><GroupStyle x:Key="ListGridGroupStyleResource"><GroupStyle.HeaderTemplate><DataTemplate><Grid Background="LightGray" ><TextBlock Text='{Binding Key}' Foreground="CornflowerBlue" Margin="12" /></Grid></DataTemplate></GroupStyle.HeaderTemplate></GroupStyle></ResourceDictionary></Application.Resources>那么這些各種資源都定義好了之后就在MainPage.xaml把下面這些敲進去。各種資源的調(diào)用在這里尤其需要注意,其實對于稍微復(fù)雜一丁點的程序而言,名稱就已經(jīng)變得讓人崩潰了。所以擁有一個良好的命名習(xí)慣很重要。
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><Grid.ColumnDefinitions><ColumnDefinition Width="auto"/><ColumnDefinition Width="auto"/></Grid.ColumnDefinitions><GridView Grid.Column="0" ItemsSource="{Binding Source={StaticResource collectionVSAlarmMode}}" Margin="12,120,12,12" MaxHeight="600" ><GridView.ItemTemplate><DataTemplate><StackPanel Margin="18"><TextBlock Text="{Binding Title}" FontWeight="ExtraBold" /><TextBlock Text="{Binding AlarmClockTime}" FontWeight="Light" TextWrapping="NoWrap" /><TextBlock Text="{Binding Description}" TextWrapping="NoWrap" /></StackPanel></DataTemplate></GridView.ItemTemplate><GridView.ItemsPanel><ItemsPanelTemplate><ItemsWrapGrid MaximumRowsOrColumns="2"/></ItemsPanelTemplate></GridView.ItemsPanel><GridView.GroupStyle><GroupStyle><GroupStyle.HeaderTemplate><DataTemplate><Grid Background="Green" Margin="12"><TextBlock Text='{Binding Name}' Foreground="Bisque" Margin="36"/></Grid></DataTemplate></GroupStyle.HeaderTemplate> </GroupStyle></GridView.GroupStyle></GridView><ListView Grid.Column="1" ItemsSource="{Binding Source={StaticResource collectionVSAlarm}}" ItemTemplate="{StaticResource dataTemplateListView}" GroupStyleSelector="{StaticResource ListGridGroupStyleResource}" Margin="120" /> </Grid>我這寫的真是太丑了哎,做產(chǎn)品的時候可得好好調(diào)調(diào)了。
縮放視圖SemanticZoom
相信用過Windows Phone或者Windows 8/8.1/10的朋友對下面這張截圖肯定不陌生。這就是通過SemanticZoom來實現(xiàn)的,當數(shù)據(jù)過多時,這種控件尤其適用。它有一個放大視圖ZoomedInView和一個縮小試圖ZoomedOutView,前者主要用來顯示當前頁面的詳細信息,后者則致力于快速導(dǎo)航。
那么我就自己來動手實踐咯,首先我們在XAML中添加大致的界面,就像畫畫要先畫輪廓一樣。
<Grid Name="grid1" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><SemanticZoom x:Name="semanticZoom" VerticalAlignment="Center" HorizontalAlignment="Center"> <SemanticZoom.ZoomedOutView></SemanticZoom.ZoomedOutView><SemanticZoom.ZoomedInView></SemanticZoom.ZoomedInView></SemanticZoom></Grid>然后分別在這兩個視圖中添加你想要加入的東西。這里的核心就是,ZoomedOutView和ZoomedInView都是使用的同一個CollectionViewSource對象作為自己的數(shù)據(jù)集的。而這個屬性我們在“為ListView和GridView分組”談到過。
我們先把后臺代碼寫好,我就像一篇那樣裝模作樣寫一個類吧。
public class Alarm{public string Title { get; set; }public DateTime AlarmClockTime { get; set; }public string Description { get; set; } }然后用一個函數(shù)來添加一大堆數(shù)據(jù)……一大堆數(shù)據(jù)。
private Alarm[] AddAlarmData() {return new Alarm[]{new Alarm {Title="Alarm 1",AlarmClockTime=globalTime.AddHours(17),Description="First Alarm for Study" },new Alarm {Title="Alarm 2",AlarmClockTime=globalTime.AddHours(2),Description="Second Alarm for Study" },new Alarm {Title="Alarm 3",AlarmClockTime=globalTime.AddHours(7),Description="Third Alarm for Study" },new Alarm {Title="Alarm 4",AlarmClockTime=globalTime.AddHours(4),Description="4th Alarm for Study" },new Alarm {Title="Alarm 5",AlarmClockTime=globalTime.AddHours(5),Description="First Alarm for Fun" },new Alarm {Title="Alarm 6",AlarmClockTime=globalTime.AddHours(1),Description="First Alarm for Fun" },new Alarm {Title="Alarm 7",AlarmClockTime=globalTime.AddHours(15),Description="Second Alarm for Fun" },new Alarm {Title="Alarm 8",AlarmClockTime=globalTime.AddHours(9),Description="Third Alarm for Fun" },new Alarm {Title="Alarm 9",AlarmClockTime=globalTime.AddHours(20),Description="4th Alarm for Fun" },new Alarm {Title="Alarm 10",AlarmClockTime=globalTime.AddHours(14),Description="Second Alarm for Sleep" },new Alarm {Title="Alarm 11",AlarmClockTime=globalTime.AddHours(9),Description="First Alarm for Sleep" }}; }因為我們最后要把放大視圖變成縮小視圖,記得縮小視圖上面有一些ABCD之類的字母么,這里我們用的是時間,就分成中午晚上等好啦。就通過下面這樣的一個函數(shù)來搞定。其用了一個鍵值對,用time作為參數(shù)。后面再將這些數(shù)據(jù)篩選出來,綁定到新添加的CollectionViewSource中。至于gridView1和gridView2是即將添加到XAML中,這里可以先不填,一回再補上。
Func<int, string> SwitchTime = (time) => {if (time <= 10 && time >= 6) return "上午";else if (time > 10 && time < 14) return "中午";else if (time >= 14 && time <= 20) return "下午";else return "晚上"; }; var varTime = from t in AddAlarmData()orderby t.AlarmClockTime.Hourgroup t by SwitchTime(t.AlarmClockTime.Hour); CollectionViewSource collectionVS = new CollectionViewSource(); collectionVS.IsSourceGrouped = true; collectionVS.Source = varTime; this.gridView1.ItemsSource = collectionVS.View.CollectionGroups; this.gridView2.ItemsSource = collectionVS.View;我們先來寫主視圖(也就是放大視圖)。
<GridView x:Name="gridView2" IsSwipeEnabled="True" HorizontalAlignment="Center" VerticalAlignment="Center" ScrollViewer.IsHorizontalScrollChainingEnabled="False" Width="1800" Height="1000"><GridView.ItemTemplate><DataTemplate><StackPanel Orientation="Horizontal" Margin="12" HorizontalAlignment="Left" Background="White"><TextBlock Text="{Binding Title}" TextWrapping="Wrap" Foreground="Red" FontFamily="Harrington"Width="150" Height="100" FontSize="26" FontWeight="Light"/><TextBlock Text="{Binding AlarmClockTime}" Foreground="Red" TextWrapping="Wrap" Width="150" Height="100" FontFamily="Harrington" FontSize="26" FontWeight="Light"/><TextBlock Text="{Binding Description}" Foreground="Red" TextWrapping="Wrap" Width="150" Height="100" FontFamily="Harrington" FontSize="26" FontWeight="Light"/></StackPanel></DataTemplate></GridView.ItemTemplate><GridView.ItemsPanel><ItemsPanelTemplate><ItemsWrapGrid MaximumRowsOrColumns="8"/></ItemsPanelTemplate></GridView.ItemsPanel><GridView.GroupStyle><GroupStyle><GroupStyle.HeaderTemplate><DataTemplate><TextBlock Text='{Binding Key}' Foreground="{StaticResource ApplicationForegroundThemeBrush}" Margin="12" FontSize="30" FontFamily="華文彩云" FontWeight="ExtraBold" /></DataTemplate></GroupStyle.HeaderTemplate></GroupStyle></GridView.GroupStyle> </GridView>相信大家都能看得懂,另外稍后我會在截圖中添加一些注釋的哦。然后是縮小視圖。
<GridView Name="gridView1" Background="Wheat" ScrollViewer.IsHorizontalScrollChainingEnabled="False" HorizontalAlignment="Center" VerticalAlignment="Center" Width="600" Height="200"><GridView.ItemTemplate><DataTemplate><TextBlock Width="100" Height="100" Text="{Binding Group.Key}" FontFamily="華文行楷" FontWeight="Normal" FontSize="24" /></DataTemplate></GridView.ItemTemplate><GridView.ItemsPanel><ItemsPanelTemplate><ItemsWrapGrid ItemWidth="100" ItemHeight="100" MaximumRowsOrColumns="2"/></ItemsPanelTemplate></GridView.ItemsPanel><GridView.ItemContainerStyle><Style TargetType="GridViewItem"><Setter Property="Margin" Value="12" /><Setter Property="Padding" Value="3" /><Setter Property="BorderThickness" Value="1" /><Setter Property="Background" Value="Green"/></Style></GridView.ItemContainerStyle> </GridView>那么代碼就到這里為止了,接下來自然就是截圖了。
(這種圖片如果看不清的話可以保存到電腦上再看。)
想了解字體相關(guān)的信息,可以看第九章的“使用更多字體”。
數(shù)據(jù)綁定介紹
簡單的數(shù)據(jù)綁定示例
相比于理論,我更傾向于從實踐中開始博客,尤其是對于數(shù)據(jù)綁定。那么,我們先來看看幾個簡單的例子。
1.數(shù)據(jù)綁定到TextBox
我們依舊使用前面的鬧鐘類來開始。在下面的代碼中,我們有屬性、構(gòu)造函數(shù),還有一個ToString()方法的重載。之所以重載這個方法是因為我們想在最后綁定的時候,這三個屬性能夠在TextBox上顯示得更加工整。
public class Alarm{public string Title { get; set; }public string Description { get; set; }public DateTime AlarmTime { get; set; }public Alarm() { }public Alarm(string title, string description,DateTime alarmTime){Title = title; Description = description;AlarmTime = alarmTime;}public override string ToString(){return "Title: " + Title +"\n"+ "Time: "+ AlarmTime.ToString("d") + "\n"+ "Description: " + Description;}}接下來再在XAML中添加TextBox控件如下,因為TextBox此時是用作顯示而非輸入,所以建議設(shè)置其的只讀屬性。數(shù)據(jù)綁定的核心就是Text屬性中的那么一個Binding關(guān)鍵字。
<TextBox x:Name="textBox1" FontSize="28" Height="150" Width="400"TextWrapping="Wrap" Text="{Binding}" IsReadOnly="True"/>但是光這樣還不夠,我們還需要在后臺代碼中將數(shù)據(jù)綁定到textBox1的DataContext(數(shù)據(jù)上下文)中。
textBox1.DataContext = new Alarm("First Alarm", "I need to study!", new DateTime(2015, 4, 11));相信大家并不為覺得這個很難,相反我在學(xué)數(shù)據(jù)綁定的時候一上來就是一大堆理論,以至于我對數(shù)據(jù)一詞有了陰影——所以我學(xué)數(shù)據(jù)結(jié)構(gòu)非常痛苦。
2.數(shù)據(jù)綁定到ComboBox
才保存一個鬧鐘沒太大意思,我們多來幾個。
public ObservableCollection<Alarm> UsefulAlarm = new ObservableCollection<Alarm>();public MainPage(){this.InitializeComponent();UsefulAlarm.Add(new Alarm("First Alarm", "I need to study!", new DateTime(2015, 4, 11)));UsefulAlarm.Add(new Alarm("First Alarm", "Read a magzine!", new DateTime(2015, 4, 12)));UsefulAlarm.Add(new Alarm("First Alarm", "Write a blog!", new DateTime(2015, 4, 15)));UsefulAlarm.Add(new Alarm("First Alarm", "Travel", new DateTime(2015, 5, 15)));textBox1.DataContext = UsefulAlarm;}但是……
很顯然我們用了ObservableCollection< T >類,它為數(shù)據(jù)綁定提供了一個集合,這是因為它實現(xiàn)了INotifyPropertyChanged和INotifyCollectionChanged接口。顧名思義,當屬性改變時,它可以通知它所綁定的控件,并且如果你希望該空間能夠同步更新,則將用于綁定的對象也實現(xiàn)INotifyPropertyChanged接口。這個類好歸好,但相對于TextBox而言算有些高端了,以至于它無法顯示出來。但是我們可以用ComboBox來代替它,我們的類并不需要修改,前面的UsefulAlarm實例化也都不用改,只需要將textBox1改成comboBox1即可。以下是新的ComboBox代碼。
<ComboBox Name="comboBox1" ItemsSource="{Binding}" FontSize="28" Height="150" Width="400"><ComboBox.ItemTemplate><DataTemplate> <StackPanel Orientation="Vertical" Margin="8"><TextBox Width="350" TextWrapping="Wrap" Text="{Binding Title}" IsReadOnly="True"/><TextBox Width="350" TextWrapping="Wrap" Text="{Binding Description}" IsReadOnly="True"/><TextBox Width="350" TextWrapping="Wrap" Text="{Binding AlarmTime}" IsReadOnly="True"/></StackPanel> </DataTemplate></ComboBox.ItemTemplate> </ComboBox>在圖示中我們也容易發(fā)現(xiàn)TextBox和ComboBox兩個控件的Width屬性的應(yīng)用區(qū)別。在TextBox中,我們將數(shù)據(jù)綁定到Text中;而在ComboBox中,我們則是將數(shù)據(jù)綁定到ItemsSource中,簡單的說就是ComboBox拿來所有的數(shù)據(jù),再將它們分成小的細節(jié)發(fā)給它的子對象,這些子對象都在ComboBox的DataTemplate(數(shù)據(jù)容器)中。
在這里我們并沒有用到前面所重載的ToString()函數(shù),因為我們已經(jīng)分別將Title、Description、AlarmTime綁定到相應(yīng)的TextBox控件了。那圖示中又為什么這些數(shù)據(jù)都是一行一行的表示呢,這都是布局控件StackPanel的功勞,全靠它的Orientation屬性。如果將這個屬性設(shè)置成Horizontal呢,那標題、描述已經(jīng)時間就是全排在一行了。
3.數(shù)據(jù)綁定到ListBox
聽說ListBox和ComboBox很類似哦,它們都是Box……XBox呀。博主我有點懶,那可不可以直接將ComboBox的名字改成ListBox就直接運行呢,答案是可以哦!那么區(qū)別到底在哪里呢?看看這張圖就知道啦。
咦?怎么只有一條鬧鐘了?別驚慌……拖動右邊的滾動條就可以查看到全部的鬧鐘咯。我真的只把ComboBox改成ListBox還有相應(yīng)的Name屬性(包括后臺代碼中的名字哦),以下就是完整的代碼啦,我會騙你?
<ListBox Name="listBox1" ItemsSource="{Binding}" FontSize="28" Height="150" Width="400"><ListBox.ItemTemplate><DataTemplate><StackPanel Orientation="Vertical" Margin="8"><TextBox Width="350" TextWrapping="Wrap" Text="{Binding Title}" IsReadOnly="True"/><TextBox Width="350" TextWrapping="Wrap" Text="{Binding Description}" IsReadOnly="True"/><TextBox Width="350" TextWrapping="Wrap" Text="{Binding AlarmTime}" IsReadOnly="True"/></StackPanel></DataTemplate></ListBox.ItemTemplate></ListBox>4.數(shù)據(jù)綁定到ListView
看了前面的代碼相信我沒有騙你吧,童鞋們看到ListBox有沒有想到ListView呢?我要是想說還是和前面一樣只用改名字等就可以用ListView,你還是不信么?
<ListView Name="listView1" ItemsSource="{Binding}" FontSize="28" Height="150" Width="400"><ListView.ItemTemplate><DataTemplate><StackPanel Orientation="Vertical" Margin="8"><TextBox Width="350" TextWrapping="Wrap" Text="{Binding Title}" IsReadOnly="True"/><TextBox Width="350" TextWrapping="Wrap" Text="{Binding Description}" IsReadOnly="True"/><TextBox Width="350" TextWrapping="Wrap" Text="{Binding AlarmTime}" IsReadOnly="True"/></StackPanel></DataTemplate></ListView.ItemTemplate></ListView>當然了,還是用右邊的滾動條來下拉以查看所有的數(shù)據(jù)。不過ListView君的最佳姿勢不是這樣哦,將Height改為600才是呢??聪聢D——這才是高大上的ListView君嘛!
好了不玩了,GridView也是可以這樣弄得,不信你試試。
再談數(shù)據(jù)綁定
1.我們?yōu)槭裁匆脭?shù)據(jù)綁定
很顯然,我們不可能把所有的數(shù)據(jù)全部固定在特定的控件上。比如,游戲的積分、設(shè)定的鬧鐘、天氣預(yù)報甚至的通訊類的消息,它們都并非是一成不變的。但是也并非所有的控件都需要綁定,比如你的App的名字、發(fā)送消息時所用的發(fā)送按鈕上面的文本等。
2.那數(shù)據(jù)和UI之間又有哪些關(guān)系呢
首先我們得明確,數(shù)據(jù)的顯示和其后臺的管理是不一樣的。數(shù)據(jù)與UI綁定之后,我們的數(shù)據(jù)就可以在這兩者之間進行溝通,如果數(shù)據(jù)發(fā)生變化時,綁定到數(shù)據(jù)的UI則會自動將相應(yīng)的屬性進行調(diào)整,不僅僅是前面用到的Text屬性,還有FontSize、Width、Foreground、Image屬性都可以。
3.數(shù)據(jù)綁定到底是綁定什么
首先,我們得有綁定源,這些就是我們需要綁定的數(shù)據(jù),沒有數(shù)據(jù),即使你綁定了,它也顯示不出來。
其次,我們還需要綁定目標,也就是Framework類的DependencyProperty屬性,說得白話文點就是將數(shù)據(jù)綁定到UI的相應(yīng)屬性上。
最后,我們還需要一個Binding對象,它就像是搬運工,沒有它,數(shù)據(jù)也是無法動彈的。它能夠幫助我們將數(shù)據(jù)從數(shù)據(jù)源移動到綁定目標,并且將綁定目標的相應(yīng)消息通知給綁定源。它還有一些巧妙的工具,能夠?qū)⒔壎ㄔ吹臄?shù)據(jù)加工成特定的格式。
4.綁定源有哪些
所有的公共語言運行時對象,我們前面用的Alarm類就是這種對象,另外UI元素也是哦。
5.聽說有的搬運工只能將數(shù)據(jù)源的數(shù)據(jù)一次性搬到綁定目標后就不再搬了,而有的搬運工則會在數(shù)據(jù)修改后再搬一次,甚至還有的能夠在綁定目標更改后再將數(shù)據(jù)搬回到數(shù)據(jù)源
OneTime綁定:這個搬運工的工作就是第一種,它只負責(zé)在創(chuàng)建時將源數(shù)據(jù)更新到綁定目標。
OneWay綁定:這是系統(tǒng)默認的搬運工,它是第二種,負責(zé)在創(chuàng)建時以及源數(shù)據(jù)發(fā)生更改時更新綁定目標。
TwoWay綁定:這個搬運工則是第三種,它能夠在綁定源和綁定目標的一邊發(fā)生更改時同時更新綁定源和綁定目標。但它在一種時候卻會偷懶,那就是對于TextBox.Text每次點擊之后,它就不會將這個Text屬性的更改更新到綁定源。不過如果碰到Boss,它也只能繼續(xù)搬了。那就是將Binding.UpdateSourceTrigger設(shè)置成PropertyChanged。而默認情況下,只有TextBox失去焦點時才會去更新。
以下分別是OneWay和TwoWay的例子:
<StackPanel Width="240" Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center"><Slider Name="slider1" Minimum="0" Maximum="100"/><TextBox FontSize="30" Text="{Binding ElementName=slider1,Path=Value,Mode=OneWay}" /></StackPanel>拖動滑動條,就可以看到在TextBox中顯示它的值的變化了。如果希望它只變化一次,那就將代碼中的OneWay改成OneTime即可。
<StackPanel Width="240" Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center"><TextBox FontSize="30" Name="textBox" Height="60" Text ="{Binding ElementName=listBox1, Path=SelectedItem.Content, Mode=TwoWay}"></TextBox> <ListBox FontSize="30" Name="listBox1"><ListBoxItem Content="Item 1"/><ListBoxItem Content="Item 2"/><ListBoxItem Content="Item 3"/><ListBoxItem Content="Item 4"/></ListBox></StackPanel>如下圖所示,點擊Item 1后TextBox則會顯示相應(yīng)的Item 1,將TextBox中的Item 1修改為Item 5后再ListBox中也自動修改成了Item5。
簡單示例:Foreground的數(shù)據(jù)綁定
前面已經(jīng)說到了Foreground也可以綁定,想不想試試呢。我們現(xiàn)在TextBox中寫一個TextBox,然后在后臺代碼中添加一個綁定就可以了。這個和前面的比較簡單,這里只是用來引出后面的東東哦
<TextBox Name="textBox" Width="200" Height="100" IsReadOnly="True"FontSize="32" Text="Text" Foreground="{Binding ForeBrush}"/> textBox.Foreground = new SolidColorBrush(Colors.BlueViolet);更改通知
1.Silder綁定到TextBlock,不使用更改通知
首先定義一個簡單的類BindingSlider,同時在XAML中作如下定義。
public class BindingSlider{private int sliderValue;public int SliderValue{get{return sliderValue;}set{sliderValue = value;}}} <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center"><Slider Name="slider1" Minimum="0" Maximum="100" Width="200" Value="{Binding SliderValue,Mode=TwoWay}"/><Button x:Name="button" Content="Button" Width="200" Click="button_Click"/><TextBlock Name="textBlock" FontSize="30"/> </StackPanel>雖然這里只是用到了OneWay傳遞,但還是需要使用TwoWay。因為在這里OneWay是指從BindingSlider類的SliderValue屬性單向傳遞到Slider控件的Value屬性。但我們需要的則是Slider控件的Value屬性單向傳遞到BindingSlider類的SliderValue屬性,所以才得使用TwoWay方式。
BindingSlider bindingSlider = new BindingSlider();public MainPage(){this.InitializeComponent();slider1.DataContext = bindingSlider;}private void button_Click(object sender, RoutedEventArgs e){textBlock.Text = bindingSlider.SliderValue.ToString();}首先實例化BindingSlider類,再在后臺代碼中獎bindingSlider對象綁定到slider1的數(shù)據(jù)上下文。最后通過Click事件來將bindingSlider對象的SliderValue屬性傳遞給textBlock控件的Text屬性。
這里的效果就是,拖動Slider但是TextBlock不會有變化,而需要Button來不斷的更改TextBlock的Text。如果想要TextBlock的Text能夠根據(jù)Slider實時的更改,這就需要”更改通知“了。
2.Silder綁定到TextBlock,使用更改通知
既然要使用通知更改的技術(shù),那就可以在XAML代碼中將Button控件刪除掉了,包括后臺代碼中的Click事件。
緊接著來修改BindingSlider類,首先得使用INotifyPropertyChanged接口。這個接口有PropertyChanged事件,而這個事件則會告知綁定目標綁定源已經(jīng)發(fā)生修改,這樣綁定目標也會實時的進行更改。在新的set中,我們將SliderValue值傳遞到NotifyPropertyChanged中。
public class BindingSlider :INotifyPropertyChanged{private int sliderValue;public int SliderValue{get{return sliderValue;}set{sliderValue = value;NotifyPropertyChanged("SliderValue"); }} public event PropertyChangedEventHandler PropertyChanged;public void NotifyPropertyChanged(string propertyName){if (PropertyChanged != null){PropertyChanged(this,new PropertyChangedEventArgs(propertyName));}}}最后我們還需要將bindingSlider對象綁定到textBlock的數(shù)據(jù)上下文。
BindingSlider bindingSlider = new BindingSlider();public MainPage(){this.InitializeComponent();slider1.DataContext = bindingSlider;textBlock.DataContext = bindingSlider; }這樣一來就全部更改完成了,試試就會發(fā)現(xiàn)TextBlock的Text會根據(jù)Slider的拖動而實時修改了。
值轉(zhuǎn)換器
有時候默認的輸出方式不能滿足我們的需要,比如前面的OneWay示例,可能我們需要的是在TextBox中顯示“開始加載“、”加載一半了“、”很快就加載完了“以及”已經(jīng)加載好“等,甚至還可以讓其能夠轉(zhuǎn)換成英文哦。
那么首先新建一個類SliderValueConverter.cs,然后實現(xiàn)IValueConverter接口。然后按自己的需要寫它的Converter方法即可。
public class SliderNotifyAndConverter : IValueConverter {public object Convert(object value, Type targetType, object parameter, string language){string valueTextBlock;string parameterValue = parameter.ToString();double valueSlider = (double)value;if (valueSlider > 0&&valueSlider<=5){if (parameterValue == "zh-cn")valueTextBlock = "開始加載";elsevalueTextBlock = "Starts to load";}else if (valueSlider >= 45 && valueSlider <= 55){if (parameterValue == "zh-cn")valueTextBlock = "加載一半了";elsevalueTextBlock = "loaded half";}else if (valueSlider >= 90&&valueSlider<100){if (parameterValue == "zh-cn")valueTextBlock = " 很快就加載完了";elsevalueTextBlock = "finished loading very quickly";}else if (valueSlider == 100){if (parameterValue == "zh-cn")valueTextBlock = " 已經(jīng)加載好";elsevalueTextBlock = "loaded";}else{if (parameterValue == "zh-cn")valueTextBlock = "加載中";elsevalueTextBlock = "Loading";}return valueTextBlock;}public object ConvertBack(object value, Type targetType, object parameter, string language){throw new NotImplementedException();}}最后還需要在XAML中添加如下代碼哦,值轉(zhuǎn)換器Converter所使用的靜態(tài)資源已經(jīng)在
<Page.Resources><local:SliderNotifyAndConverter x:Key="SliderNotifyAndConverterResources"/></Page.Resources><Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"><StackPanel Name="stackPanel" Width="450" Orientation="Vertical"HorizontalAlignment="Center" VerticalAlignment="Center"><Slider Name="slider1" Minimum="0" Maximum="100"Value="95"/><TextBlock FontSize="30" Text="{Binding ElementName=slider1, Path=Value, Converter={StaticResource SliderNotifyAndConverterResources}, ConverterParameter='zh-cn'}"/><TextBlock FontSize="30" Text="{Binding ElementName=slider1, Path=Value, Converter={StaticResource SliderNotifyAndConverterResources}, ConverterParameter='en-us'}"/></StackPanel></Grid>以下是Slider的Value取不同值時TextBlock的不同顯示。
這里僅僅是一個比較簡單的示例,在項目產(chǎn)品中往往有數(shù)據(jù)綁定的地方都會有值轉(zhuǎn)換器的,就像C++中經(jīng)常重載ostream一樣。
總結(jié)
以上是生活随笔為你收集整理的Windows App开发之集合控件与数据绑定的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: win7系统自带截图工具快捷键是什么?怎
- 下一篇: Shell与ShellScript