有很多文章討論系結的概念,并講解如何使用StaticResources和DynamicResources系結屬性,這些概念使用WPF提供的資料系結運算式,在本文中,讓我們研究WPF提供的不同型別的資料系結運算式,
介紹
資料系結是一種強大的技術,它允許資料在UI元素和業務模型之間流動,當業務模型中的資料發生變化時,它會自動將更改反映到UI元素上,
| Models | Description |
|---|---|
| OneWay | Source → Destination |
| TwoWay | Source ←→ Destination |
| OneWayToSource | Source ← Destination |
| OneTime | Source → Destination (only once) |
這可以通過WPF提供的不同型別的資料系結運算式來實作,
資料系結運算式的型別如下所示,
- DataContext系結
- RelativeSource系結
- ItemSource系結
1、DataContext系結
DataContext是一個依賴屬性,它是系結的默認源,Datacontext沿著邏輯樹繼承,因此,如果您設定一個DataContext來控制邏輯樹中的所有子元素,它也將參考同一個DataContext,除非并且直到顯式指定了另一個源,
讓我們舉個例子來更詳細地理解它,
1.1 創建一個類Book,如下所示,
public class Book
{
public string Name
{
get;
set;
}
public string Author
{
get;
set;
}
}
1.2 添加一個XAML檔案DataContextBinding.XAML并放置四個TextBlock,如下所示,
<Grid VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="40" />
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="Book Name:" FontWeight="Bold" />
<TextBlock Grid.Column="1" />
<TextBlock Text="Author:" FontWeight="Bold" Grid.Row="1" />
<TextBlock Grid.Row="1" Grid.Column="1" />
</Grid>
現在,讓我們看看如何使用這個DataContext屬性來顯示資料,
它有兩種用法,如下所示,
- 1.使用{Binding}運算式
用于直接系結DataContext,
創建類Book的實體,初始化其屬性,并將類的Name屬性分配給Window的DataContext屬性,
public partial class DataContextBinding: Window
{
public DataContextBinding()
{
InitializeComponent();
//Create the instance
Book book = new Book();
//initialize the properties
book.Name = "Computer Networking";
//Assign the Property as DataContext
this.DataContext = book.Name;
}
}
由于DataContext是沿著邏輯樹和資料book繼承的,因此Name被系結到Control Window,Window的所有子元素也將參考同一個物件(book.Name),
要顯示資料,請將DataContext與Textblock系結,如下所示,
<TextBlock Text="Book Name:" FontWeight="Bold"/>
<TextBlock Text="{Binding}" Grid.Column="1" />
輸出

- 使用{Binding Property}運算式
系結Datacontext的屬性,
創建類Book的實體,初始化其屬性并將類的實體(Book)分配給Window的DataContext屬性,
Book book = new Book();
//initialize the properties
book.Name = "Computer Networking";
book.Author = "James F. Kurose";
//Assign the instance as DataContext
this.DataContext = book;
現在,讓我們看看輸出,

由于系結運算式{Binding}用于系結Book型別的DataContext物件,因此呼叫ToString()方法,并將資料顯示為字串,為了以正確的格式顯示資料,我們必須將資料物件的屬性與TextBlock系結,如下所示:
<TextBlock Text="Book Name:" FontWeight="Bold"/>
<TextBlock Text="{Binding Name}" Grid.Column="1" />
<TextBlock Text="Author:" FontWeight="Bold" Grid.Row="1" />
<TextBlock Text="{Binding Author}" Grid.Row="1" Grid.Column="1"/>
系結運算式{Binding Name}用于系結DataContext系結的Name屬性,
輸出

2、RelativeSource 系結
RelativeSource是一個屬性,它用相對關系設定系結源以系結目標,此擴展主要用于必須將元素的一個屬性系結到同一元素的另一個屬性時,
RelativeSource有四種型別,如下所示,
- Self
- FindAncestor
- TemplatedParent
- PreviousData
讓我們一個一個詳細地探討一下,
2.1 Self
Self用于系結源和系結目標相同的場景中,物件的一個屬性與同一物件的另一個屬性系結,
例如,讓我們取一個高度和寬度相同的橢圓,
在XAML檔案中添加下面給出的代碼,寬度屬性與高度屬性相對系結,
<Grid>
<Ellipse Fill="Black" Height="100" Width="{Binding RelativeSource={RelativeSource Self},Path=Height}">
</Ellipse>
</Grid>
輸出

如果改變橢圓的高度,寬度也會相對變化,
2.2 FindAncestor
顧名思義,當系結源是系結目標的祖先(父級)之一時使用此選項,使用FindAncestor擴展,可以找到任何級別的祖先,
讓我們舉個例子來更清楚地理解它,
步驟
創建XAML,它表示下面給出的元素的邏輯樹,

<Grid Name="Parent_3">
<StackPanel Name="Parent_2">
<Border Name="Parent_1">
<StackPanel x:Name="Parent_0" Orientation="Vertical">
<Button></Button>
</StackPanel>
</Border>
</StackPanel>
</Grid>
現在,讓我們使用FindAncestor擴展將祖先的Name屬性系結到子元素button的Content屬性,
<Grid Name="Parent_3">
<StackPanel Name="Parent_2" HorizontalAlignment="Center" VerticalAlignment="Center" Width="100">
<Border Name="Parent_1">
<StackPanel x:Name="Parent_0" Orientation="Vertical">
<Button Height="50" Content="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type StackPanel},
AncestorLevel=2},Path=Name}"></Button>
</StackPanel>
</Border>
</StackPanel>
</Grid>
輸出

AncestorType為“StackPanel”與AcestorLevel為“2”組合,將button的content屬性與StackPanel的Name屬性(Parent_2)系結在一起,
2.3 TemplatedParent
TemplatedParent是一個屬性,它使您能夠創建一個包含少量未知值的控制元件模板,這些值取決于應用ControlTemplate的控制元件的屬性,
讓我們舉個例子來更詳細地理解它
步驟
- 為按鈕創建一個ControlTemplate,如下所示,
<Window.Resources>
<ControlTemplate x:Key="template">
<Canvas>
<Ellipse Height="110" Width="155"
Fill="Black"/>
<Ellipse Height="100" Width="150"
Fill="{Binding RelativeSource={RelativeSource TemplatedParent},Path=Background}">
</Ellipse>
<ContentPresenter Margin="35"
Content="{Binding RelativeSource={RelativeSource TemplatedParent},Path=Content}"/>
</Canvas>
</ControlTemplate>
</Window.Resources>
在上面給出的代碼中,橢圓的Fill屬性和ContentPresenter的Content屬性依賴于將應用此模板的控制元件的屬性值,
- 添加一個按鈕并對其應用模板,
<Button Margin="50" Background="Beige" Template="{StaticResource template}" Height="0" Content="Click me" FontSize="22">
</Button>
在應用模板時,按鈕的Background(Beige)與橢圓的Fill屬性相對系結,Content(Click me)與ContentPresenter的Content屬性相對系結,依賴值生效并給出以下輸出,
輸出

2.4 PreviousData
這是相對使用最少的方式,當資料被分析時,這就出現了,我們需要表示值相對于以前資料的變化,
讓我們舉個例子來更詳細地理解它,
步驟
- 創建一個類Data并實作INotifyPropertyChanged介面,如下所示
public class Data: INotifyPropertyChanged
{
public int DataValue
{
get;
set;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string PropertyName)
{
if (null != PropertyChanged)
{
PropertyChanged(this,
new PropertyChangedEventArgs(PropertyName));
}
}
}
- 創建一個Data型別的串列并將其指定為DataContext,
public RelativeSourcePreviousData()
{
InitializeComponent();
List < Data > data = https://www.cnblogs.com/Dotnet9-com/p/new List < Data > ();
data.Add(new Data()
{
DataValue = 60
});
data.Add(new Data()
{
DataValue = 100
});
data.Add(new Data()
{
DataValue = 120
});
this.DataContext = data;
}
- 在XAML檔案中添加ItemsControl,
<ItemsControl ItemsSource="{Binding}"></ItemsControl>
- 為其創建ItemsPanel模板,如下,
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
- 現在,為了正確地表示資料,創建DataTemplate,如下所示,
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Grid Margin="30,20,0,0">
<Rectangle Width="80" Height="{Binding DataValue}" Fill="Blue" />
<TextBlock Foreground="White" Margin="35,0,0,0" Text="{Binding DataValue}"></TextBlock>
</Grid>
<TextBlock Margin="30,20,0,0" Text="Previous Data:"></TextBlock>
<TextBlock VerticalAlignment="Center" Margin="5,20,0,0" Text="{Binding
RelativeSource={RelativeSource PreviousData}, Path=DataValue}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
輸出

藍色框的高度是串列中專案的值,舊資料顯示在右側,該項的第一個值為“60”,因此,第一項沒有舊值,
3、ItemSource系結
在處理集合時使用,使用這個系結運算式,您可以非常容易地讀取SelectedItem的屬性,斜杠是一種特殊運算子,用于處理集合中的當前項,
下面給出了三種運算式,
- {Binding / }
- {Binding Collection / }
- {Binding Collection / Property}
3.1 {Binding / }
此運算式用于系結DataContext中的當前項,
讓我們采取一個示例:
在下面給出的示例中,DataContext是字串型別的國家/地區的集合,并且與Listbox系結在一起,
步驟
- 創建一個Countries類并添加一個GetCountriesName()方法,該方法回傳string資料型別的國家的集合,如下所示,
public class Countries
{
public static List <string> GetCountriesName()
{
List <string> countries = new List <string> ();
foreach(CultureInfo culture in CultureInfo.GetCultures(CultureTypes.SpecificCultures))
{
RegionInfo country = new RegionInfo(culture.LCID);
if (!countries.Contains(country.EnglishName))
countries.Add(country.EnglishName);
}
countries.Sort();
return countries;
}
}
- 添加一個XAMl檔案,一個ListBox和TextBlock,如下所示,
<DockPanel Name="Collection">
<ListBox ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True">
</ListBox>
<TextBlock DockPanel.Dock="Top" />
</DockPanel>
- 創建類Countries的實體并將Countries集合指定為DataContext,
public CurrentItemCollection()
{
InitializeComponent();
Countries countries = new Countries();
this.DataContext = countries.GetCountriesName()
}
- 系結TextBlock的Text屬性以將其系結到集合的當前選定項,如下所示,
<TextBlock DockPanel.Dock="Top" Text="{Binding /}" />
輸出

一旦串列項被選中,它將在右側顯示所選國家/地區,
3.2 {Binding Collection /}
此運算式用于系結DataContext中集合屬性的當前項,
例如,
DataContext是Countries類
Collection屬性是CounriesList,它與ListBox系結,
步驟
- 使用上面創建的類似的國家類,只是略有不同,創建回傳型別為RegionInfo的方法,
public static List <RegionInfo> GetCountries()
{
List <RegionInfo> countries = new List <RegionInfo> ();
foreach(CultureInfo culture in CultureInfo.GetCultures(CultureTypes.SpecificCultures))
{
RegionInfo country = new RegionInfo(culture.LCID);
if (countries.Where(p => p.Name == country.Name).Count() == 0)
countries.Add(country);
}
return countries.OrderBy(p => p.EnglishName).ToList();
}
- 添加RegionInfo型別的CountriesList屬性,
private List <RegionInfo> countries = null;
public List <RegionInfo> CountriesList
{
get
{
if (countries == null)
countries = GetCountries();
return countries;
}
}
下面是CountriesList集合中的值的截圖,

- 將類Countries指定為DataContext,并將Listbox與DataContext的CountriesList屬性系結,
<Window.Resources>
<vm:Countries x:Key="Countries"></vm:Countries>
</Window.Resources>
<Grid>
<DockPanel Name="Collection" DataContext="{StaticResource Countries}">
<ListBox ItemsSource="{Binding CountriesList}" IsSynchronizedWithCurrentItem="True">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding EnglishName}"></TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
</Grid>
- 要計算CountriesList屬性的當前項,請系結TextBlock的Text屬性,如下所示,
<TextBlock DockPanel.Dock="Top" Text="{Binding CountriesList/}" HorizontalAlignment="Center" FontSize="16" VerticalAlignment="Center" />
輸出

右側顯示DataContext(CountriesList)中集合的當前項(CountriesList),
3.3 {Binding Collection / Property}
此運算式用于系結DataContext中集合的當前項的屬性,
例如,如果必須計算CountriesList集合的當前項的特定屬性,
在這個例子中,我想顯示屬性“EnglishName”的值,

為此,系結TextBlock的Text屬性,如下所示,
<TextBlock DockPanel.Dock="Top" Text="{Binding CountriesList/EnglishName}" />
輸出

現在,當串列中的項被選中時,它顯示屬性“EnglishName”的值,
結論
我已經詳細介紹了所有的資料系結運算式,我希望這有助于您理解系結的概念和WPF提供的運算式,
時間如流水,只能流去不流回,
- 作者:Swati Gupta
- 原文標題:DataBinding Expressions In WPF
- 原文鏈接:https://www.c-sharpcorner.com/article/data-binding-expression-in-wpf/
- 翻譯、編輯:沙漠盡頭的狼
- 公眾號:Dotnet9
- 日期:2021-05-04
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/285409.html
標籤:WPF
