利刃 MVVMLight 4:繫結和繫結的各種使用場景
一、繫結:
主要包含元素繫結和非元素繫結兩種。
1、元素繫結,是繫結的最簡單形式,源物件是WPF的元素,並且源物件的屬性是依賴項屬性。
根據我們之前的知識 ,依賴項屬性具有內建的更改通知支援。所以當我們的源物件中改變依賴項屬性的值時,會立即更新目標物件中的繫結屬性。
以上篇的例子來重寫,我們不用額外定義全域性公開的屬性來支援資料的顯示。
如下:
1 <StackPanel Orientation="Vertical" HorizontalAlignment="Left" > 2 <TextBox x:Name="WelcomeText" Width="200" Margin="10,10,0,0"></TextBox> 3 <TextBlock Text="{Binding ElementName=WelcomeText,Path=Text,StringFormat='Hello \{0\}'}" Margin="10,10,0,0"></TextBlock> 4 </StackPanel>
TextBlock 綁定了名稱為WelcomeText的元素,並且將Path指向Text屬性,所以他的值會跟著 WelcomeText的變化而變化。
2、非元素型別繫結:
2.1 Source屬性:繫結具體的資料物件:如系統資訊跟我們定義的資源資料。
定義Window下的全域性資源
1 <Window.Resources> 2 <SolidColorBrush x:Key="BorderBrush">Red</SolidColorBrush> 3 </Window.Resources>
應用到檢視中
1 <StackPanel Margin="10,50,0,0" Orientation="Vertical" > 2 <TextBlock Text="{Binding Source={x:Static SystemFonts.IconFontFamily},Path=Source}" ></TextBlock> 3 <TextBlock Text="{Binding Source={StaticResource BorderBrush}}" Foreground="{Binding Source={StaticResource BorderBrush}}" ></TextBlock> 4 </StackPanel>
結果:
2.2 RelativeSource 屬性:設定該屬性 可以根據當前目標物件的相對關係指向源目標。比如獲取當前物件的父親物件、兄弟物件或者自身的其他屬性等一些資料。
1 <StackPanel Margin="10,50,0,0" Orientation="Vertical" ToolTip="top" > 2 3 <StackPanel Orientation="Horizontal" > 4 <TextBlock Width="150" Text="獲取自身寬度:" ></TextBlock> 5 <TextBlock Width="200" Text="{Binding Path=Width,RelativeSource={RelativeSource Mode=Self}}" ></TextBlock> 6 </StackPanel> 7 8 9 <StackPanel Orientation="Horizontal" ToolTip="parent" > 10 <TextBlock Width="150" Text="查詢上一層ToolTip:" ></TextBlock> 11 <TextBlock Text="{Binding Path=ToolTip,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type StackPanel}}}"></TextBlock> 12 </StackPanel> 13 14 15 <StackPanel Orientation="Horizontal"> 16 <TextBlock Width="150" Text="查詢上二層ToolTip:" ></TextBlock> 17 <TextBlock Text="{Binding Path=ToolTip,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type StackPanel},AncestorLevel=2}}"></TextBlock> 18 </StackPanel> 19 20 </StackPanel>
程式碼很容易理解,這邊在建立RelativeSource的時候,mode模式有四種類型:
Mode成員名稱 | 說明 |
---|---|
FindAncestor |
引用資料繫結元素的父鏈中的上級。 這可用於繫結到特定型別的上級或其子類。 若要指定 AncestorType 和/或 AncestorLevel,這就是應使用的模式。 |
PreviousData |
允許在當前顯示的資料項列表中繫結上一個資料項(不是包含資料項的控制元件)。 |
Self |
引用正在其上設定繫結的元素,並允許你將該元素的一個屬性繫結到同一元素的其他屬性上。 |
TemplatedParent |
引用應用了模板的元素,其中此模板中存在資料繫結元素。 這類似於設定 TemplateBindingExtension,且僅在 Binding 位於模板內部時適用。 |
注意:AncestorType 指得是查詢的物件型別,AncestorLevel 代表搜尋的層級的位置,如果是3,則忽略前兩個發現的元素。
結果:
2.3 DataContext 屬性:如果想將一個物件繫結到一個由多個元素組成的檢視塊或者複合元素中,用DataContext 會更好開發和維護。如下
1 <StackPanel Orientation="Vertical" DataContext="UInfo" > 2 3 <StackPanel Orientation="Horizontal" > 4 <TextBlock Text="名稱:" Width="100" ></TextBlock> 5 <TextBox Text="{Binding Name}" Width="100" ></TextBox> 6 </StackPanel> 7 8 <StackPanel Orientation="Horizontal"> 9 <TextBlock Text="性別:" Width="100" ></TextBlock> 10 <TextBox Text="{Binding Sex}" Width="100" ></TextBox> 11 </StackPanel> 12 13 </StackPanel>
二、繫結的各種使用場景:
資料繫結有普通的控制元件繫結應用:比如 下拉框、單選框、複選框、普通文字框 、日期框等; 複雜的繫結有資料列表繫結,使用者控制元件資訊繫結等,比如 ListBox,DataGrid,UserControl繫結等。1、下拉框:
View程式碼:
1 <StackPanel Margin="10,20,0,50"> 2 <TextBlock Text="下拉框" FontWeight="Bold" FontSize="12" Margin="0,5,0,5" ></TextBlock> 3 <DockPanel x:Name="Combbox" > 4 <StackPanel DockPanel.Dock="Left" Width="240"> 5 <ComboBox Width="200" HorizontalAlignment="Left" ItemsSource="{Binding CombboxList}" SelectedItem="{Binding CombboxItem}" DisplayMemberPath="Text" SelectedValuePath="Key" ></ComboBox> 6 </StackPanel> 7 8 <StackPanel DockPanel.Dock="Right" Width="240" Orientation="Horizontal" DataContext="{Binding CombboxItem}" > 9 <TextBlock Text="{Binding Key,StringFormat='結果:\{0\}'}" Margin="0,0,15,0" ></TextBlock> 10 <TextBlock Text="{Binding Text}"></TextBlock> 11 </StackPanel> 12 13 </DockPanel> 14 </StackPanel>
Model程式碼:
1 public class ComplexInfoModel:ObservableObject 2 { 3 private String key; 4 /// <summary> 5 /// Key值 6 /// </summary> 7 public String Key 8 { 9 get { return key; } 10 set { key = value; RaisePropertyChanged(()=>Key); } 11 } 12 13 private String text; 14 /// <summary> 15 /// Text值 16 /// </summary> 17 public String Text 18 { 19 get { return text; } 20 set { text = value; RaisePropertyChanged(()=>Text); } 21 } 22 }
ViewModel程式碼:
1 #region 下拉框相關 2 private ComplexInfoModel combboxItem; 3 /// <summary> 4 /// 下拉框選中資訊 5 /// </summary> 6 public ComplexInfoModel CombboxItem 7 { 8 get { return combboxItem; } 9 set { combboxItem = value; RaisePropertyChanged(() => CombboxItem); } 10 } 11 12 13 private List<ComplexInfoModel> combboxList; 14 /// <summary> 15 /// 下拉框列表 16 /// </summary> 17 public List<ComplexInfoModel> CombboxList 18 { 19 get { return combboxList; } 20 set { combboxList = value; RaisePropertyChanged(()=>CombboxList); } 21 } 22 #endregion
說明:CombboxItem是一個全域性的屬性,作用在當前頁面的資料上下文中,結果顯示的內容指向下拉框中的選中值,達到共用一個數據的目的。
這邊有四個地方需要注意的:ItemsSource:資料來源;SelectedItem:選中的項;DisplayMemberPath:繫結時顯示的所屬值;SelectedValuePath :繫結時候key的所屬值。結果:
2、單選框
1 <StackPanel Margin="10,0,0,50"> 2 <TextBlock Text="單選框" FontWeight="Bold" FontSize="12" Margin="0,5,0,5" ></TextBlock> 3 <DockPanel x:Name="RadioButton" > 4 <StackPanel DockPanel.Dock="Left" Width="240"> 5 <RadioButton Content="{Binding SingleRadio}" IsChecked="{Binding IsSingleRadioCheck}" HorizontalAlignment="Right" Width="240" > 6 </RadioButton> 7 </StackPanel> 8 <StackPanel DockPanel.Dock="Right" Width="240" Orientation="Horizontal"> 9 <TextBlock Text="{Binding IsSingleRadioCheck,StringFormat='結果:\{0\}'}" ></TextBlock> 10 </StackPanel> 11 </DockPanel> 12 </StackPanel>
說明:注意這邊使用到了兩個屬性: SingleRadio,IsSingleRadioCheck,一個用於顯示單選框內容,一個用於表示是否選中
結果:
3、組合單選框
我們一般會用單選框做組合表示唯一選項,比如性別包含男女,但是隻能選擇一個。而更多的場景是包含多個選項,但是隻能單選的,這時候就需要做單選框組。1 <StackPanel Margin="10,0,0,50"> 2 <TextBlock Text="組合單選框" FontWeight="Bold" FontSize="12" Margin="0,5,0,5"></TextBlock> 3 <DockPanel x:Name="GroupRadioButton" > 4 <StackPanel DockPanel.Dock="Left" Width="240"> 5 <ItemsControl ItemsSource="{Binding RadioButtons}"> 6 <ItemsControl.ItemTemplate> 7 <DataTemplate> 8 <RadioButton Content="{Binding Content}" IsChecked="{Binding IsCheck}" GroupName="RadioButtons" 9 Command="{Binding DataContext.RadioCheckCommand,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ItemsControl}}"> 10 </RadioButton> 11 </DataTemplate> 12 </ItemsControl.ItemTemplate> 13 </ItemsControl> 14 </StackPanel> 15 16 <StackPanel DockPanel.Dock="Right" Width="240" Orientation="Horizontal"> 17 <TextBlock Text="{Binding RadioButton.Content,StringFormat='結果:\{0\}'}" ></TextBlock> 18 </StackPanel> 19 </DockPanel> 20 </StackPanel>
這邊使用了ItemsControl,可以根據模板來定義內容,我們在模板中放置我們需要用到的內容。這邊需要注意的是:GroupName用一樣的,來代表這是一個單選控制元件組合。
這邊有是三個屬性進行繫結相關: RadioButtons:單選框列表資料(迴圈繫結);Content:單選框顯示的內容;IsCheck:單選框的是否選中。結果:
4、複選框,複選框與單選框的使用情況類似:
1 <StackPanel Margin="10,0,0,50"> 2 <TextBlock Text="複合框" FontWeight="Bold" FontSize="12" Margin="0,5,0,5" ></TextBlock> 3 <DockPanel x:Name="GroupCheckButton" > 4 <StackPanel DockPanel.Dock="Left" Width="240"> 5 <ItemsControl ItemsSource="{Binding CheckButtons}" x:Name="cbt" > 6 <ItemsControl.ItemTemplate> 7 <DataTemplate> 8 <CheckBox Content="{Binding Content}" IsChecked="{Binding IsCheck}" 9 Command="{Binding DataContext.CheckCommand,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ItemsControl}}"/> 10 </DataTemplate> 11 </ItemsControl.ItemTemplate> 12 </ItemsControl> 13 </StackPanel> 14 15 <StackPanel DockPanel.Dock="Right" Width="240" Orientation="Horizontal"> 16 <TextBlock Text="{Binding CheckInfo,StringFormat='結果:\{0\}'}" ></TextBlock> 17 </StackPanel> 18 </DockPanel> 19 </StackPanel>
結果:
5、樹形控制元件
View程式碼:
1 <StackPanel Margin="10,0,0,50"> 2 <TextBlock Text="樹" FontWeight="Bold" FontSize="12" Margin="0,5,0,5" ></TextBlock> 3 <DockPanel x:Name="TreeButton" > 4 <StackPanel DockPanel.Dock="Left" Width="240"> 5 <TreeView ItemsSource="{Binding TreeInfo}" x:Name="tree" BorderThickness="0"> 6 <TreeView.ItemTemplate> 7 <HierarchicalDataTemplate ItemsSource="{Binding Children}"> 8 <TextBlock Text="{Binding NodeName}"/> 9 </HierarchicalDataTemplate> 10 </TreeView.ItemTemplate> 11 </TreeView> 12 </StackPanel> 13 14 <StackPanel DockPanel.Dock="Right" Width="240" Orientation="Horizontal" DataContext="{Binding SelectedItem,ElementName=tree}"> 15 <TextBlock Text="結果:"/> 16 <TextBlock Text="{Binding NodeID,StringFormat='NodeID:\{0\}'}" Margin="0,0,20,0" /> 17 <TextBlock Text="{Binding NodeName,StringFormat='NodeName:\{0\}'}"/> 18 </StackPanel> 19 </DockPanel> 20 </StackPanel>
Model程式碼
1 public class TreeNodeModel : ObservableObject 2 { 3 public string NodeID { get; set; } 4 public string NodeName { get; set; } 5 public List<TreeNodeModel> Children { get; set; } 6 }
ViewModel程式碼
1 #region 樹控制元件 2 3 private List<TreeNodeModel> treeInfo; 4 /// <summary> 5 /// 樹控制元件資料資訊 6 /// </summary> 7 public List<TreeNodeModel> TreeInfo 8 { 9 get { return treeInfo; } 10 set { treeInfo = value; RaisePropertyChanged(()=>TreeInfo); } 11 } 12 13 14 #endregion
結果:
6、ListBox
當我們需要用到迴圈的列表內容,並且模板化程度高的時候,建議使用ListBox來做繫結。 View程式碼:1 <StackPanel Margin="10,0,0,50" Orientation="Vertical" > 2 <TextBlock Text="ListBox模板" FontWeight="Bold" FontSize="12" Margin="0,5,0,5" ></TextBlock> 3 <DockPanel > 4 <StackPanel HorizontalAlignment="Left" DockPanel.Dock="Left" > 5 <ListBox x:Name="lb" ItemsSource="{Binding ListBoxData}" Width="500" BorderThickness="0" > 6 <ListBox.ItemsPanel> 7 <ItemsPanelTemplate> 8 <WrapPanel Width="{Binding ActualWidth,RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}"/> 9 </ItemsPanelTemplate> 10 </ListBox.ItemsPanel> 11 12 <ListBox.ItemTemplate> 13 <DataTemplate> 14 <StackPanel> 15 <Image Source="{Binding Img}" Width="96" Height="96"/> 16 <TextBlock HorizontalAlignment="Center" Text="{Binding Info}"/> 17 </StackPanel> 18 </DataTemplate> 19 </ListBox.ItemTemplate> 20 </ListBox> 21 </StackPanel> 22 23 <StackPanel DockPanel.Dock="Right" DataContext="{Binding SelectedItem,ElementName=lb}" Margin="15" Orientation="Vertical" > 24 <TextBlock Text="{Binding Info,StringFormat='選中:\{0\}'}" ></TextBlock> 25 </StackPanel> 26 </DockPanel> 27 </StackPanel>
ViewModel程式碼:
1 #region ListBox 模板 2 3 private IEnumerable listBoxData; 4 /// <summary> 5 /// LisBox資料模板 6 /// </summary> 7 public IEnumerable ListBoxData 8 { 9 get { return listBoxData; } 10 set { listBoxData = value;RaisePropertyChanged(()=>ListBoxData); } 11 } 12 13 #endregion
初始資料:
1 private void InitListBoxList() 2 { 3 ListBoxData = new ObservableCollection<dynamic>(){ 4 new { Img="/MVVMLightDemo;component/Images/1.jpg",Info="櫻桃" }, 5 new { Img="/MVVMLightDemo;component/Images/2.jpg",Info="葡萄" }, 6 new { Img="/MVVMLightDemo;component/Images/3.jpg",Info="蘋果" }, 7 new { Img="/MVVMLightDemo;component/Images/4.jpg",Info="獼猴桃" }, 8 new { Img="/MVVMLightDemo;component/Images/5.jpg",Info="檸檬" }, 9 }; 10 }
結果:
7、使用者控制元件的集合繫結:
ListBox的列表繫結遠遠不能滿足我們實際工作中的需求, 出於對靈活性、複用性以及程式碼精簡的考慮,需要保證迴圈列表中的單個元素是獨立的元素片段,類似Web中的區域性檢視。 這時候,使用使用者控制元件會好很多。 我們先寫一個使用者控制元件,分別設定了他的樣式和繫結的屬性值,如下:1 <UserControl x:Class="MVVMLightDemo.Content.FruitInfoView" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 5 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 6 mc:Ignorable="d" 7 d:DesignHeight="300" d:DesignWidth="300"> 8 <Grid> 9 <Grid.Resources> 10 <Style TargetType="{x:Type StackPanel}"> 11 <Style.Triggers> 12 <Trigger Property="IsMouseOver" Value="True"> 13 <Setter Property="RenderTransform"> 14 <Setter.Value> 15 <RotateTransform Angle="10"></RotateTransform> 16 </Setter.Value> 17 </Setter> 18 <Setter Property="Background" Value="#3B9CFB" /> 19 </Trigger> 20 </Style.Triggers> 21 </Style> 22 </Grid.Resources> 23 24 25 <StackPanel Orientation="Vertical" Margin="10"> 26 <Image Source="{Binding Img}" Width="96" Height="96" /> 27 <TextBlock HorizontalAlignment="Center" Text="{Binding Info}"/> 28 </StackPanel> 29 30 </Grid> 31 </UserControl>
在目標檢視頁面註冊並使用:
1 xmlns:Content="clr-namespace:MVVMLightDemo.Content"
1 <StackPanel Margin="10,0,0,50" Orientation="Vertical" > 2 <TextBlock Text="使用者控制元件模板列表" FontWeight="Bold" FontSize="12" Margin="0,5,0,5" ></TextBlock> 3 <StackPanel HorizontalAlignment="Left" Width="500" > 4 <ItemsControl ItemsSource="{Binding FiList}" HorizontalAlignment="Left" > 5 <ItemsControl.ItemTemplate> 6 <DataTemplate> 7 <Content:FruitInfoView /> 8 </DataTemplate> 9 </ItemsControl.ItemTemplate> 10 11 <!-- 面板顯示模板 --> 12 <ItemsControl.ItemsPanel> 13 <ItemsPanelTemplate> 14 <WrapPanel Orientation="Horizontal"> 15 </WrapPanel> 16 </ItemsPanelTemplate> 17 </ItemsControl.ItemsPanel> 18 19 </ItemsControl> 20 21 </StackPanel> 22 </StackPanel>
結果:
後記:這篇更確切的說是繫結的相關知識,只是應用了MVVM模式來實現。
工作太忙了,寫的太慢,其實後面幾篇都已經成稿了,一直放在Note裡面等待認真檢查,品質太差怕誤導其他開發人員。
點選下載程式碼
轉載請標明出處,謝謝