【翻譯】WPF中的資料繫結表示式

Dotnet9個人部落格發表於2021-05-08

有很多文章討論繫結的概念,並講解如何使用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提供的表示式。


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

相關文章