程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> WPF中的數據綁定

WPF中的數據綁定

編輯:關於.NET

目錄

數據綁定細節

創建簡單的綁定

綁定模式

綁定的 時間

綁定到 XML

對象綁定和 DataTemplates

對數據進行排 序

歡迎試用和反饋

到目前為止,很多人都知道使用 Windows® Presentation Foundation (WPF) 可以輕松地設計強大的用戶界面。但是您可能 並不知道它還提供了強大的數據綁定功能。使用 WPF,可以通過利用 Microsoft® .NET Framework 代碼、XAML 或兩者的組合進行數據操作。您可 以綁定控件、公共屬性、XML 或對象,從而使數據綁定比以前更快捷、靈活和簡 單。所以,讓我們來看一下如何開始將控件綁定到您所選的數據源中。

數 據綁定細節

要使用 WPF 數據綁定功能,您必須始終要有目標和源。綁定 的目標可以是從 DependencyProperty 派生而來的任何可訪問屬性或元素,例如 TextBox 控件的 Text 屬性。綁定的源可以是任何公共屬性,包括其他控件、公 共語言運行庫 (CLR) 對象、XAML 元素、ADO.NET Dataset、XML 片段等的屬性。 為了幫助您正確實現綁定,WPF 包含了兩個特殊的提供程序:XmlDataProvider 和 ObjectDataProvider。

現在讓我們看一下 WPF 數據綁定技術的工作原 理,我將列舉一些實用的示例來說明它們的用法。

創建簡單的綁定

首先,我們來看一個簡單的示例,該示例說明了如何將 TextBlock 的 Text 屬性綁定到 ListBox 的選定項。圖 1 中的代碼顯示的是聲明了六個 ListBoxItem 的 ListBox。該代碼示例中的第二個 TextBlock 具有名為 Text( 使用 XML 子元素 <TextBlock.Text> 在 XAML 屬性元素語法中指定)的屬 性,它將包含 TextBlock 的文本。Text 屬性聲明了通過 <Binding> 標記 與 ListBox 選定項的綁定。Binding 標記的 ElementName 屬性指示 TextBlock 的 Text 屬性要與其綁定的控件的名稱。Path 屬性指示我們將綁定到的元素(在 本例中是 ListBox)的屬性。此代碼產生的結果是,如果從 ListBox 選擇了一種 顏色,該顏色的名稱則會在 TextBlock 中顯示。

Figure1基本但詳細的控件綁定

<StackPanel>
   <TextBlock Width="248" Height="24" Text="Colors:"
     TextWrapping="Wrap"/>
  <ListBox x:Name="lbColor" Width="248" Height="56">
    <ListBoxItem Content="Blue"/>
    <ListBoxItem Content="Green"/>
    <ListBoxItem Content="Yellow"/>
    <ListBoxItem Content="Red"/>
    <ListBoxItem Content="Purple"/>
    <ListBoxItem Content="Orange"/>
  </ListBox>
   <TextBlock Width="248" Height="24" Text="You selected color:" />
  <TextBlock Width="248" Height="24">
     <TextBlock.Text>
      <Binding ElementName="lbColor" Path="SelectedItem.Content"/>
     </TextBlock.Text>
  </TextBlock>
</StackPanel>

為了使用簡單的語法來進行數據綁定,可 以對圖 1 中列出的代碼稍加修改。例如,我們用下列代碼段替代 TextBlock 的 <Binding> 標記:

<TextBlock Width="248" Height="24"
  Text="{Binding ElementName=lbColor,
  Path=SelectedItem.Content}" />

這種語法稱為屬性語法,它壓縮了 TextBlock 的 Text 屬 性內部的數據綁定代碼。基本上,Binding 標記會連同它的屬性一起被歸入大括 號內。

綁定模式

我可以繼續以上述示例為例,將 TextBlock 的背 景色綁定到在 ListBox 中選擇的顏色。以下代碼可將 Background 屬性添加到 TextBlock 中,並使用該屬性的綁定語法將其綁定到 ListBox 中選定項的值:

<TextBlock Width="248" Height="24"
Text="{Binding ElementName=lbColor, Path=SelectedItem.Content,
  Mode=OneWay}"
x:Name="tbSelectedColor"
Background="{Binding ElementName=lbColor, Path=SelectedItem.Content,
  Mode=OneWay} "/>

如果用戶在 ListBox 中選擇了一種顏色,那麼該顏 色的名稱就會出現在 TextBlock 中,並且 TextBlock 的背景色會變為選定的顏 色(請參見圖 2)。

圖 2將一個源綁定到兩個目標

請注意前一個示例中將 Mode 屬性設為 OneWay 的語句。Mode 屬性用於定義綁定模式,它將決定數據如何在源和目標之間流動。 除 OneWay 之外,還有另外三種綁定模式:OneTime、OneWayToSource 和 TwoWay 。

正如前面的代碼段中所示,使用 OneWay 綁定時,每當源發生變化,數 據就會從源流向目標。盡管我在示例中顯式指定了此綁定模式,但其實 OneWay 綁定是 TextBlock 的 Text 屬性的默認綁定模式,無需對其指定。和 OneWay 綁 定一樣,OneTime 綁定也會將數據從源發送到目標;但是,僅當啟動了應用程序 或 DataContext 發生更改時才會如此操作,因此,它不會偵聽源中的更改通知。 與 OneWay 和 OneTime 綁定不同,OneWayToSource 綁定會將數據從目標發送到 源。最後,TwoWay 綁定會將源數據發送到目標,但如果目標屬性的值發生變化, 則會將它們發回給源。

在上述示例中,我使用了 OneWay 綁定,因為我希 望只要 ListBox 選擇發生變化,就將源(選定的 ListBoxItem)發送到 TextBlock。我不希望 TextBlock 的更改再回到 ListBox。當然,用戶無法編輯 TextBlock。如果我想使用 TwoWay 綁定,可以將 TextBox 添加到此代碼中,將 其文本和背景色綁定到 ListBox,並將 Mode 屬性設為 TwoWay。用戶在 ListBox 中選擇一種顏色後,該顏色就會顯示在 TextBox 中,並且其背景色會相應變化。 如果該用戶在 TextBox 中鍵入了一種顏色(例如藍綠色),ListBox 中的顏色名 稱就會更新(從目標到源),反過來,因為 ListBox 已經更新,所以此新值就會 被發送到綁定到 ListBox 的 SelectedItem 屬性的所有元素。這意味著 TextBlock 也會更新其顏色,並且將其文本值設置為該新的顏色(請參見圖 3) 。

圖 3運行中的 TwoWay 綁定

下面是我剛才用來將 TextBlock (OneWay) 和 TextBox (TwoWay) 綁定到 ListBox 的代碼:

<TextBlock Width="248" Height="24"
  Text=" {Binding ElementName=lbColor, Path=SelectedItem.Content,
   Mode=OneWay}" x:Name="tbSelectedColor"
   Background="{Binding ElementName=lbColor, Path=SelectedItem.Content,
  Mode=OneWay}"/>
<TextBox Width="248" Height="24"
   Text="{Binding ElementName=lbColor, Path=SelectedItem.Content,
  Mode=TwoWay}" x:Name="txtSelectedColor"
   Background="{Binding ElementName=lbColor, Path=SelectedItem.Content,
  Mode=OneWay} "/>

如果我將 TwoWay 模式改回到 OneWay,用戶則可以 編輯 TextBox 中的顏色,且不會導致更改過的值被發回給 ListBox。

選 擇合適的綁定模式非常重要。當我想向用戶顯示只讀數據時,我通常會采用 OneWay 模式。當我希望用戶可以更改控件中的數據,並且讓該變化能在數據源( DataSet、對象、XML 或其他綁定控件)中體現出來時,我會使用 TwoWay 綁定。 如果想讓用戶在數據源不將其數據綁定到目標的情況下更改數據源,我發現 OneWayToSource 是個不錯的選擇。我曾經接到一個任務,要求在只讀控件中顯示 與加載屏幕時一樣的數據狀態。在這個任務中,我使用了 OneTime 綁定。通過使 用 OneTime 綁定,一系列只讀控件均被綁定到了數據,並且當用戶與表單交互且 數據源的值發生更改時,綁定控件仍保持不變。這為用戶提供了一種比較所發生 更改的方法。此外,當源沒有實現 INotifyPropertyChanged 時,OneTime 綁定 也是一個不錯的選擇。

綁定的時間

在上述示例中,TextBox 允許 TwoWay 綁定到在 ListBox 中選定的 ListBoxItem。這會使數據在 TextBox 失去 焦點時從 TextBox 流回 ListBox。為了改變導致將數據發送回源的這種情況,可 以為 UpdateSourceTrigger 指定值,它是用於定義何時更新源的綁定屬性。可以 為 UpdateSourceTrigger 設置三個值:Explicit、LostFocus 和 PropertyChanged。

如果將 UpdateSourceTrigger 設置為 Explicit,則 不會更新源,除非從代碼調用 BindingExpression.UpdateSource 方法。 LostFocus 設置(TextBox 控件的默認值)指示源在目標控件失去焦點時才會更 新。PropertyChanged 值指示目標會在目標控件的綁定屬性每次發生更改時更新 源。如果您想指示綁定的時間,該設置非常有用。

綁定到 XML

綁 定到數據源(例如 XML)和對象同樣也非常方便。圖 4 顯示了 XmlDataProvider 的示例,其中包含將用作數據源的顏色的嵌入式列表。XmlDataProvider 可用來 綁定到 XML 文檔或片斷,該文檔或片段既可以嵌入在 XmlDataProvider 標記中 ,也可以位於外部位置引用的文件中。

Figure4XmlDataProvider

<StackPanel>
<StackPanel.Resources>
<XmlDataProvider x:Key="MoreColors" XPath="/colors">
   <x:XData>
    <colors >
      <color name="pink"/>
      <color name="white"/>
      <color name="black"/>
      <color name="cyan"/>
      <color name="gray"/>
      <color name="magenta"/>
    </colors>
   </x:XData>
</XmlDataProvider>

嵌入式 XML 內容必須置於 XmlDataProvider 內部的 <x:XData> 標記中,如圖 4 所示 。必須為 XmlDataProvider 提供 x:Key 值,以便數據綁定目標可對其進行引用 。請注意,XPath 屬性設置為“/colors”。此屬性定義了將用作數據 源的 XML 內容的級別。當綁定到可能包含在文件或數據庫中的較大 XML 結構, 且想要綁定的數據不是根元素時,這一屬性會變得非常有用。

XmlDataProvider 是可放置到特定上下文資源內部的一種資源。如圖 4 所示,已將 XmlDataProvider 定義為 StackPanel 上下文中的資源。這意味著 XmlDataProvider 將可用於該 StackPanel 內部的所有內容。設置資源的上下文 有助於限制數據源向合適的區域公開。這使您可以在頁面內分別為控件和支持資 源創建定義明確的獨立區域,從而提高可讀性。

綁定到資源的語法與綁定 到元素的語法略有不同。綁定到控件時,可以設置綁定的 ElementName 和 Path 屬性。但是綁定到資源時,需要設置 Source 屬性,由於我們是綁定到 XmlDataProvider,所以還要設置綁定的 XPath 屬性。例如,下列代碼可將 ListBox 的項綁定到 MoreColors 資源。將 Source 屬性設置為資源,並將其指 定為名為 MoreColors 的 StaticResource。XPath 屬性指示項會綁定到 XML 數 據源中 <color> 元素的名稱屬性:

<ListBox x:Name="lbColor" Width="248" Height="56"
   IsSynchronizedWithCurrentItem="True"
   ItemsSource="{Binding Source={StaticResource MoreColors},
   XPath=color/@name}">
</ListBox>

我在 本例中指定了 StaticResource,因為 XML 不會發生更改。如果數據源發生了更 改,則不會將這些更改發送到目標。DynamicResource 設置則表示相反的情況, 即會將數據源的更改發送到目標。當引用系統主題、全球化語言或字體時,該設 置非常有用。DynamicResource 將允許這些類型的設置被傳送到與其動態綁定的 所有 UI 元素中。

XmlDataProvider 也可以指向 XML 內容的外部源。例 如,我有一個名為 colors.xml 的文件,其中包含我希望 ListBox 綁定到的顏色 列表。我只需要將第二個 XmlDataProvider 資源添加到 StackPanel,並將其引 向 XML 文件即可。請注意,我將 Source 屬性設置為了 XML 文件的名稱,並將 x:Key 設置為 Colors:

<XmlDataProvider x:Key="Colors" Source="Colors.xml" XPath="/colors"/>

兩個 XmlDataProvider 在同一 個 StackPanel 中都作為資源而存在。我可以使 ListBox 將其本身綁定到這個新 的資源,方法是更改 StaticResource 設置的名稱:

<ListBox x:Name="lbColor" Width="248" Height="56"
   IsSynchronizedWithCurrentItem="True"
   ItemsSource="{Binding Source={StaticResource Colors},
   XPath=color/@name}">
</ListBox>

對象綁 定和 DataTemplates

雖然 XmlDataProvider 對 XML 非常有用,但是當您 想綁定到對象或對象列表時,可以創建 ObjectDataProvider 作為資源。 ObjectDataProvider 的 ObjectType 指定將提供數據綁定源的對象,而 MethodName 則指示為獲得數據而需調用的方法。例如,假設我有一個名為 PersonService 的類,該類使用一種名為 GetPersonList 的方法來返回列表 <Person>,那麼 ObjectDataProvider 可能會如下所示:

<StackPanel.Resources>
<ObjectDataProvider x:Key="persons"
ObjectType="{x:Type svc:PersonService}"
MethodName="GetPersonList"></ObjectDataProvider></StackPanel.Resources>

如果您想進行更全面的了解 ,本欄隨附的代碼中還包含了 PersonService 和 Person 類以及其他示例代碼。

ObjectDataProvider 還可以使用許多其他屬性。 ConstructionParameters 屬性允許您將參數傳遞給要調用的類的構造函數。此外 ,可以使用 MethodParameters 屬性來指定參數,同時還可以使用 ObjectInstance 屬性來指定現有的對象實例作為源。

如果希望異步檢索 數據,可以將 ObjectDataProvider 的 IsAsynchronous 屬性設為 true。這樣, 用戶將可以在等待數據填充綁定到 ObjectDataProvider 的源的目標控件時與屏 幕進行交互。

在添加 ObjectDataProvider 時,必須限定數據源類的命名 空間。在本例中,我必須將 xmlns 屬性添加到 <Window> 標記中,以便 svc 快捷方式符合要求,並指示正確的命名空間:

xmlns:svc="clr- namespace:DataBindingWPF"

既然數據源已通過 ObjectDataProvider 定義,接著我想將 ListBox 控件中的項綁定到此數據。我 想在每個 ListBoxItem 中顯示兩行文本。第一行將以粗體顯示 Person 實例的 FullName 屬性,第二行將顯示該實例的 Title 和 City。在 XAML 中,通過使用 DataTemplate,這是很容易實現的,DataTemplate 允許您定義可重用的數據可視 化策略。

圖 5 顯示了完整的 XAML,其中將 DataTemplate 定義為在我指 定的布局中顯示 Person 信息。我設置了 DataTemplate 的 DataType 屬性,以 指示 DataTemplate 將會引用 Person 類類型。我沒有在 DataTemplate 中指定 真正的綁定,因為我會在 ListBox 控件中指定。通過省略綁定源,可對作用域內 的當前 DataContext 執行綁定。

在圖 5 中,我將 ListBox 的 ItemsSource 屬性設置為綁定到人員資源,以便我可以將數據綁定到 ListBox, 而不用對它進行格式化。通過將 ItemTemplate 屬性設置為 PersonLayout 資源 (即 DataTemplate 的鍵名),可以正確顯示數據。最終結果的屏幕如圖 6 所示 。

Figure5對象綁定

<Window x:Class="DataBindingWPF.ObjectBinding"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation "
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:svc="clr-namespace:DataBindingWPF"
Title="DataBindingWPF" Height="300" Width="300">
<StackPanel>
   <StackPanel.Resources>
    <ObjectDataProvider x:Key="persons"
     ObjectType="{x:Type svc:PersonService}"
      MethodName="GetPersonList" ></ObjectDataProvider>
      <DataTemplate x:Key="personLayout" DataType="Person">
        <StackPanel Orientation="Vertical">
           <TextBlock Text="{Binding Path=FullName}"
               FontWeight="Bold" Foreground="Blue">
           </TextBlock>
        <StackPanel Orientation="Horizontal">
           <TextBlock Text="{Binding Path=Title} "></TextBlock>
          <TextBlock Text=", "></TextBlock>
           <TextBlock Text="{Binding Path=City} "></TextBlock>
         </StackPanel>
      </StackPanel>
     </DataTemplate>
  </StackPanel.Resources>
   <TextBlock></TextBlock>
  <ListBox x:Name="lbPersons"
      ItemsSource=" {Binding Source={StaticResource persons}}"
       ItemTemplate="{DynamicResource personLayout}"
       IsSynchronizedWithCurrentItem="True"/>
</StackPanel>
</Window>

圖 6使用 DataTemplate

對數據進行排序

如果想以特定的方式對數據 進行排序,可以綁定到 CollectionViewSource,而不是直接綁定到 ObjectDataProvider。CollectionViewSource 則會成為數據源,並充當截取 ObjectDataProvider 中的數據的媒介,並提供排序、分組和篩選功能,然後將它 傳送到目標。

接著顯示的 CollectionViewSource 將其 Source 屬性設置 為 ObjectDataProvider(人員)的資源名稱。然後我通過指示排序依據的屬性及 其方向定義了數據的排序順序:

<CollectionViewSource x:Key="personView"
  Source="{Binding Source= {StaticResource persons}}">
   <CollectionViewSource.SortDescriptions>
   <ComponentModel:SortDescription
   PropertyName="City"
  Direction="Ascending" />  
  <ComponentModel:SortDescription
   PropertyName="FullName"
   Direction="Descending" />
   </CollectionViewSource.SortDescriptions>
</CollectionViewSource>

DataContext 可用來將容器控 件內部的所有控件都綁定到數據源中。當您擁有多個控件,而且這些控件全部使 用同一個綁定源時,這非常有用。如果為每個控件都指定了綁定源,那麼代碼可 能會重復。相反,可以將容器控件的 DataContext 設置為綁定源,然後只需從所 包含的控件中省略 Source 屬性即可。例如,下面是一系列顯式綁定到同一個綁 定源的 TextBlock:

<StackPanel>
<TextBlock Text="{Binding Source={StaticResource personView},
   Path=FullName}"></TextBlock>
<TextBlock Text="{Binding Source={StaticResource personView},
   Path=Title}"></TextBlock>
<TextBlock Text=" {Binding Source={StaticResource personView},
  Path=City} "></TextBlock>
</StackPanel>

以下 是綁定到 DataContext 的三個相同的 TextBox,在此處,DataContext 反過來引 用了該控件的 StackPanel 容器:

<StackPanel DataContext="{Binding Source={StaticResource personView}}" >
<TextBlock Text="{Binding Path=FullName} "></TextBlock>
<TextBlock Text="{Binding Path=Title}"></TextBlock>
<TextBlock Text=" {Binding Path=City}"></TextBlock>
</StackPanel>

如果該容器沒有定義 DataContext,那麼 它會繼續查找下一個外部嵌套容器,直到它找到當前的 DataContext 為止。

歡迎試用和反饋

WPF 數據綁定具有很大的靈活性,並且能夠控制 可被綁定的數據類型以及它的控制和顯示方式。有了這麼多的功能和選擇,我相 信您一定躍躍欲試。

請將您想向 John 詢問的問題和提出的意見發送至 [email protected].

John Papa 是 ASPSOFT (aspsoft.com) 的一名高 級 .NET 顧問,也是一名棒球迷。夏季的夜晚,他大多時候都和家人及其忠實的 狗 Kadi 一起為洋基隊加油助威度過。John 是 C# 領域的一位 MVP,撰寫過多本 有關 ADO、XML 和 SQL Server 方面的書籍。他經常在行業會議(如 VSLive)上 發表演講,或者在 codebetter.com/blogs/john.papa 上撰寫博客文章。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved