【翻譯】WPF中的數據綁定表達式

有很多文章討論綁定的概念,並講解如何使用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" />   

輸出

  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有四種類型,如下所示。

  1. Self
  2. FindAncestor
  3. TemplatedParent
  4. 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的控件的屬性。

讓我們舉個例子來更詳細地理解它

步驟

  1. 為按鈕創建一個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屬性依賴於將應用此模板的控件的屬性值。

  1. 添加一個按鈕並對其應用模板。
<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

這是相對使用最少的方式。當數據被分析時,這就出現了,我們需要表示值相對於以前數據的變化。

讓我們舉個例子來更詳細地理解它。

步驟

  1. 創建一個類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));  
        }  
    }  
}   
  1. 創建一個Data類型的列表並將其指定為DataContext。
public RelativeSourcePreviousData() 
{  
    InitializeComponent();  
    List < Data > data = new List < Data > ();  
    data.Add(new Data() 
    {  
        DataValue = 60  
    });  
    data.Add(new Data() 
    {  
        DataValue = 100  
    });  
    data.Add(new Data() 
    {  
        DataValue = 120  
    });  
    this.DataContext = data;  
}   
  1. 在XAML文件中添加ItemsControl。
<ItemsControl ItemsSource="{Binding}"></ItemsControl>  
  1. 為其創建ItemsPanel模板,如下。
<ItemsControl ItemsSource="{Binding}">  
    <ItemsControl.ItemsPanel>  
        <ItemsPanelTemplate>  
            <StackPanel Orientation="Vertical" />  
        </ItemsPanelTemplate>  
    </ItemsControl.ItemsPanel>  
</ItemsControl>  
  1. 現在,為了正確地表示數據,創建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的屬性。斜杠是一種特殊運算符,用於處理集合中的當前項。

下面給出了三種表達式。

  1. {Binding / }
  2. {Binding Collection / }
  3. {Binding Collection / Property}

3.1 {Binding / }

此表達式用於綁定DataContext中的當前項。

讓我們採取一個示例:

在下面給出的示例中,DataContext是字符串類型的國家/地區的集合,並且與Listbox綁定在一起。

步驟

  1. 創建一個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;  
    }  
}  
  1. 添加一個XAMl文件,一個ListBox和TextBlock,如下所示。
<DockPanel Name="Collection">  
    <ListBox ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True">  
    </ListBox>  
    <TextBlock DockPanel.Dock="Top" />  
</DockPanel>  
  1. 創建類Countries的實例並將Countries集合指定為DataContext。
public CurrentItemCollection() 
{  
    InitializeComponent();  
    Countries countries = new Countries();  
    this.DataContext = countries.GetCountriesName()  
} 
  1. 綁定TextBlock的Text屬性以將其綁定到集合的當前選定項,如下所示。
<TextBlock DockPanel.Dock="Top" Text="{Binding /}" />  

輸出

一旦列表項被選中,它將在右側顯示所選國家/地區。

3.2 {Binding Collection /}

此表達式用於綁定DataContext中集合屬性的當前項。

例如,

DataContext是Countries類

Collection屬性是CounriesList,它與ListBox綁定。

步驟

  1. 使用上面創建的類似的國家類,只是略有不同。創建返回類型為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();  
}
  1. 添加RegionInfo類型的CountriesList屬性。
private List <RegionInfo> countries = null;  
public List <RegionInfo> CountriesList 
{  
    get 
    {  
        if (countries == null)  
            countries = GetCountries();  
        return countries;  
    }  
}  

下面是CountriesList集合中的值的截圖。

  1. 將類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>  
  1. 要計算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提供的表達式。


時間如流水,只能流去不流回。

Tags: