WPF9-數據綁定進階

目錄

  • 1. 定義
  • 2. 背景
  • 3. Binding源
    • 3.1. 使用Data Context作為Binding的源
    • 3.2. 使用LINQ檢索結果作為Binding的源
  • 4. Binding對數據的轉換和校驗
    • 4.1. 需求
    • 4.2. 實現步驟
    • 4.3. 值轉換和校驗的好處
      • 4.3.1. 數據轉換的好處
    • 4.4. 數據校驗的好處
    • 4.5. 原理
      • 4.5.1. 值轉換器原理
      • 4.5.2. 數據校驗原理

前面的博文我們討論了Binding的Path知道了如何在一個對象身上尋找數據。本篇繼續看如何為Binding指定Source。

1. 定義

Binding,出于方便業界一直使用Binding一詞的音譯,即“綁定”。我理解Binding更注重表達它是一種像橋梁一樣的關聯關系。WPF中,正是在這段橋梁上我們有機會為往來流通的數據做很多事情。

Binding在源與目標之間架起了溝通的橋梁,默認情況下數據既能夠通過Binding送達目標,也能夠從目標返回源(收集用戶對數據的修改)。

有時候數據只需要展示給用戶、不允許用戶修改,這時候可以把Binding模式更改為從源向目標的單向溝通。Binding還支持從目標向源的單向溝通以及只在Binding關系確立時讀取一次數據,這需要我們根據實際情況去選擇。

控制Binding數據流向的屬性是Mode, 它的類型是BindingMode枚舉。

BindingMode可取值為:

TwoWay

OneWay

OnTime

OneWayToSource

Default

這里的Default值是指Binding的模式會根據目標的實際情況來確定,比如若是可編輯的(如TextBox.Text屬性),Default就采用雙向模式;

若是只讀的(如TextBlock.Text)則采用單向模式。

2. 背景

讓我們回歸程序的本質。程序的本質是數據加算法,用戶給進一個輸入,經過算法的處理程序會反饋一個輸出。

這里,數據處于程序的核心地位。反過頭來再看“UI驅動程序”,數據處于被動地位,總是在等待程序接收來自UI的消息/事件后被處理或者算法完成處理后被顯示。

如何在GUI編程時把數據的地位由被動變主動、讓數據回歸程序的核心呢?這就是WPF中的Data Binding的背景。

WPF具有這種能力的關鍵是它引入了Data Binding概念以及與之配套的Dependency Property系統和DataTemplate

在從傳統的Windows Form遷移到WPF之后,對于一個三層程序而言,數據存儲層由數據庫和文件系統來構建,數據傳輸和處理仍然使用.NET FramworkADO.NET等基本類(與Windows Form等開發一樣),展示層則使用WPF類庫來實現,而展示層與邏輯層的溝通就使用Data Binding`來實現。

可見Data Binding在WPF系統中起到的是數據高速公路的作用。有了這條高速公路,加工好的數據會自動送達用戶界面加以顯示,被用戶修改過的數據也會自動傳回邏輯層, 一旦數據被加工好又會被送達用戶界面……程序的邏輯層就像一個強有力的引擎不停運轉,用加工好的數據 驅動程序的用戶界面以文字、圖形、動畫等形式把數據顯示出來——這就是“數據驅動UI”。

3. Binding源

Binding的源是數據的來源,所以,只要一個對象包含數據并能通過屬性把數據暴露出來,它就能當作Binding的源來使用。包含數據的對象比比皆是,但必須為Binding的Source指定合適的對象Binding才能正確工作,常見的辦法有:

  1. 把普通CLR類型單個對象指定為Source:包括.NETFramework自帶類型的對象和用戶自定義類型的對象。如果類型實現了INotifyPropertyChanged接口,則可通過在屬性的set語句里激發PropertyChanged事件來通知Binding數據已被更新。
  2. 把普通CLR集合類型對象指定為Source:包括數組、List、ObservableCollection等集合類型。實際工作中,我們經常需要把一個集合作為ItemsControl派生類的數據源來使用,一般是把控件的ItemsSource屬性使用Binding關聯到一個集合對象上。
  3. 把ADO.NET數據對象指定為Source:包括DataTable和DataView等對象。
  4. 使用XmlDataProvider把XML數據指定為Source:XML作為標準的數據存儲和傳輸格
    式幾乎無處不在,我們可以用它表示單個數據對象或者集合;一些WPF控件是級聯式的

(如TreeView和Menu),我們可以把樹狀結構的XML數據作為源指定給與之關聯的Binding。

  1. 把依賴對象(DependencyObject)指定為Source:依賴對象不僅可以作為Binding的目標,同時也可以作為Binding的源。這樣就有可能形成Binding鏈。依賴對象中的依賴屬性可以作為Binding的Path。
  2. 把容器的DataContext指定為Source(WPFDataBinding的默認行為):有時候我們會遇到這樣的情況——我們明確知道將從哪個屬性獲取數據,但具體把哪個對象作為Binding源還不能確定。這時候,我們只能先建立一個Binding、只給它設置Path而不設置Source,讓這個Binding自己去尋找Source。這時候,Binding會自動把控件的DataContext當作自己的Source(它會沿著控件樹一層一層向外找,直到找到帶有Path指定屬性的對象為止)。
  3. 通過ElementName指定Source:在C#代碼里可以直接把對象作為Source賦值給Binding,但XAML無法訪問對象,所以只能使用對象的Name屬性來找到對象。
  4. 通過Binding的RelativeSource屬性相對地指定Source:當控件需要關注自己的、自己容器的或者自己內部元素的某個值就需要使用這種辦法。
  5. 把ObjectDataProvider對象指定為Source:當數據源的數據不是通過屬性而是通過方法暴露給外界的時候,我們可以使用這兩種對象來包裝數據源再把它們指定為Source。
  6. 把使用LINQ檢索得到的數據對象作為Binding的源。

3.1. 使用Data Context作為Binding的源

前面的例子都是把單個CLR類型對象指定為Binding的Source,方法有兩種——把對象賦值給Binding.Source屬性或把對象的Name賦值給Binding.ElementName。

DataContext屬性被定義在FrameworkElement類里,這個類是WPF控件的基類,這意味著所有WPF控件(包括容器控件)都具備這個屬性。

如前所述,WPF的UI布局是樹形結構,這棵樹的每個結點都是控件,由此我們推出另一個結論——在UI元素樹的每個結點都有DataContext。

這一點非常重要,因為當一個Binding只知道自己的Path而不知道自己的Soruce時,它會沿著UI元素樹一路向樹的根部找過去,

每路過一個結點就要看看這個結點的DataContext是否具有Path所指定的屬性。如果有,那就把這個對象作為自己的Source;如果沒有,那就繼續找下去;

如果到了樹的根部還沒有找到,那這個Binding就沒有Source,因而也不會得到數據。

先創建一個名為Student的類,它具有Id、Name、Age三個屬性:

public class Student{public int Id { get; set; }public string Name { get; set; }public int Age { get; set; }}

<Window x:Class="WpfApp1.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="clr-namespace:WpfApp1"Title="WPF 綁定進階" Height="450" Width="800"><StackPanel Background="LightBlue" Margin="10"><!-- DataContext 設置為 Student 對象 --><StackPanel.DataContext><local:Student Id="6" Name="Tim" Age="29" /></StackPanel.DataContext><!-- 綁定到 Student 的屬性 --><TextBox Text="{Binding Path=Id, Mode=TwoWay}" Margin="5" /><TextBox Text="{Binding Path=Name, Mode=TwoWay}" Margin="5" /><TextBox Text="{Binding Path=Age, Mode=TwoWay}" Margin="5" /></StackPanel>
</Window>

這個UI布局可以下面樹狀圖來表示:

文章配圖

在實際工作中DataContext的用法是非常靈活的。比如:

(1)當UI上的多個控件都使用Binding關注同一個對象時,不妨使用DataContext。

(2)當作為Source的對象不能被直接訪問的時候——比如B窗體內的控件想把A窗體內的控件當作自己的Binding源時,

但A窗體內的控件是private訪問級別,這時候就可以把這個控件(或者控件的值)作為窗體A的DataContext(這個屬性是public訪問級別的)從而暴露數據。

形象地說,這時候外層容器的DataContext就相當于一個數據的“制高點”,只要把數據放上去,別的元素就都能看見。

另外,DataContext本身也是一個依賴屬性,我們可以使用Binding把它關聯到一個數據源上。

3.2. 使用LINQ檢索結果作為Binding的源

自3.0版開始,.NET Framework開始支持LINQ(Language-IntegratedQuery,語言集成查詢),

使用LINQ,我們可以方便地操作集合對象、DataTable對象和XML對象而不必動輒就把好幾層foreach循環嵌套在一起卻只是為了完成一個很簡單的任務。

LINQ查詢的結果是一個IEnumerable類型對象,而IEnumerable又派生自IEnumerable,所以它可以作為列表控件的ItemsSource來使用。

文章配圖

我們先來看查詢集合對象。要從一個已經填充好的 List對象中檢索出所有名字以字母T 開頭的學生,代碼如下:

<StackPanel Background="LightBlue" Margin="10"><ListView x:Name="listViewStudents" Height="143" Margin="5"><ListView.View><GridView><GridViewColumn Header="Id" Width="60" DisplayMemberBinding="{Binding Id}" /><GridViewColumn Header="Name" Width="100" DisplayMemberBinding="{Binding Name}" /><GridViewColumn Header="Age" Width="80" DisplayMemberBinding="{Binding Age}" /></GridView></ListView.View></ListView><Button Content="Load" Height="25" Margin="5" Click="Button_Click" /></StackPanel>
public partial class MainWindow : Window{public MainWindow(){InitializeComponent();}private void Button_Click(object sender, RoutedEventArgs e){List<Student> stuList = new List<Student>{new Student { Id = 0, Name = "Tim", Age = 29 },new Student { Id = 1, Name = "Tom", Age = 28 },new Student { Id = 2, Name = "Kyle", Age = 27 },new Student { Id = 3, Name = "Tony", Age = 26 },new Student { Id = 4, Name = "Vina", Age = 25 },new Student { Id = 5, Name = "Mike", Age = 24 },};this.listViewStudents.ItemsSource = from stu in stuList where stu.Name.StartsWith("T") select stu;}}

4. Binding對數據的轉換和校驗

前面我們已經知道,Binding的作用就是架在Source與Target之間的橋梁,數據可以在這座橋梁的幫助下來流通。

就像現實世界中的橋梁會設置一些關卡進行安檢一樣,Binding這座橋上也可以設置關卡對數據的有效性進行檢驗,不僅如此,當Binding兩端要求使用不同的數據類型時,我們還可以為數據設置轉換器。

WPF中通過**值轉換器(IValueConverter)和數據校驗(IDataErrorInfo或INotifyDataErrorInfo)**來實現數據的轉換和校驗。

這種機制使得數據在顯示和更新時更加靈活和安全。

4.1. 需求

假設我們有一個Person類,包含以下屬性:

Age:用戶的年齡(整數)。

IsAdult:一個布爾值,表示用戶是否成年(年齡是否大于等于18)。

我們需要實現以下功能:

數據轉換:將Age屬性的值轉換為布爾值IsAdult,并在UI中顯示。

數據校驗:確保用戶輸入的年齡是有效的(例如,年齡必須大于0)。

4.2. 實現步驟

(1) 創建Person類

Person類實現INotifyPropertyChanged接口,用于支持數據綁定的更新,并實現IDataErrorInfo接口用于數據校驗。

文件:Person.cs


using System.ComponentModel;namespace WpfDataBindingExample{public class Person : INotifyPropertyChanged, IDataErrorInfo{private int age;public int Age{get => age;set{if (age != value){age = value;OnPropertyChanged(nameof(Age));OnPropertyChanged(nameof(IsAdult)); // 通知依賴屬性更新}}}public bool IsAdult => Age >= 18;// 實現 INotifyPropertyChanged 接口public event PropertyChangedEventHandler PropertyChanged;protected virtual void OnPropertyChanged(string propertyName){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));}// 實現 IDataErrorInfo 接口public string Error => null; // 不需要整體校驗public string this[string columnName]{get{switch (columnName){case nameof(Age):if (Age <= 0)return "年齡必須大于0";break;}return null; // 無錯誤}}}
}

(2) 創建值轉換器

創建一個值轉換器,將Age轉換為布爾值IsAdult。

文件:AgeToAdultConverter.cs


using System;using System.Globalization;using System.Windows.Data;namespace WpfDataBindingExample{public class AgeToAdultConverter : IValueConverter{public object Convert(object value, Type targetType, object parameter, CultureInfo culture){if (value is int age){return age >= 18;}return false;}public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture){throw new NotImplementedException(); // 不需要反向轉換}}
}

(3) XAML文件

在XAML中,使用Binding將Person對象的屬性綁定到UI,并使用值轉換器和數據校驗。

文件:MainWindow.xaml


<Window x:Class="WpfDataBindingExample.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="clr-namespace:WpfDataBindingExample"Title="Data Binding with Conversion and Validation" Height="200" Width="400"><Window.Resources><local:AgeToAdultConverter x:Key="AgeToAdultConverter" /></Window.Resources><StackPanel Margin="10"><TextBox x:Name="AgeTextBox" Width="100" Margin="5" VerticalContentAlignment="Center"Text="{Binding Age, Mode=TwoWay, ValidatesOnDataErrors=True}" /><TextBlock Text="是否成年:" Margin="5" /><TextBox IsReadOnly="True" Background="LightGray" Width="100" Margin="5" VerticalContentAlignment="Center"Text="{Binding IsAdult, Converter={StaticResource AgeToAdultConverter}}" /></StackPanel>
</Window>

(4) 代碼后臺

在代碼后臺中,初始化Person對象并設置為DataContext。

文件:MainWindow.xaml.cs


using System.Windows;namespace WpfDataBindingExample{public partial class MainWindow : Window{public MainWindow(){InitializeComponent();DataContext = new Person { Age = 20 }; // 初始化數據}}
}

4.3. 值轉換和校驗的好處

4.3.1. 數據轉換的好處

  1. 靈活性:值轉換器允許在綁定過程中對數據進行任意轉換,例如格式化、邏輯判斷等。
  2. 解耦:將數據轉換邏輯從UI代碼中分離出來,便于維護和復用。

4.4. 數據校驗的好處

  1. 用戶體驗:在用戶輸入無效數據時,及時給出反饋,提升用戶體驗。
  2. 數據完整性:確保綁定到模型的數據始終符合業務規則,避免無效數據進入后端邏輯。
  3. 安全性:防止用戶輸入非法數據,減少潛在的安全風險。

4.5. 原理

4.5.1. 值轉換器原理

IValueConverter接口定義了Convert和ConvertBack方法。

Convert方法用于將源數據轉換為目標數據。

ConvertBack方法用于將目標數據轉換回源數據(可選實現)。

在XAML中通過Binding的Converter屬性指定值轉換器。

4.5.2. 數據校驗原理

IDataErrorInfo接口允許在數據綁定時對屬性進行校驗。

如果校驗失敗,Binding會自動將錯誤信息顯示在UI上(例如,輸入框顯示紅色邊框)。

校驗邏輯在this[string columnName]屬性中實現,返回錯誤信息或null。

通過上述實現,我們可以在WPF中靈活地處理數據綁定的轉換和校驗,提升應用程序的用戶體驗和數據安全性。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/895855.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/895855.shtml
英文地址,請注明出處:http://en.pswp.cn/news/895855.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

大數據治理:數字時代的關鍵密碼

大數據治理&#xff1a;數字時代的關鍵密碼 在信息技術飛速發展的今天&#xff0c;數字化浪潮席卷全球&#xff0c;深刻地改變著我們的生活和工作方式。數據&#xff0c;作為數字化時代的核心資產&#xff0c;正以前所未有的速度增長和積累。據國際數據公司&#xff08;IDC&am…

LeetCode 1299.將每個元素替換為右側最大元素:倒序遍歷,維護最大值,原地修改

【LetMeFly】1299.將每個元素替換為右側最大元素&#xff1a;倒序遍歷&#xff0c;維護最大值&#xff0c;原地修改 力扣題目鏈接&#xff1a;https://leetcode.cn/problems/replace-elements-with-greatest-element-on-right-side/ 給你一個數組 arr &#xff0c;請你將每個…

機器學習面試題匯總

1. 基礎知識 什么是監督學習和無監督學習? 監督學習是基于已標注的訓練數據來學習預測模型;無監督學習則是在沒有標簽的數據上進行學習,尋找數據的結構或模式。什么是過擬合和欠擬合? 過擬合是指模型在訓練數據上表現很好,但在測試數據上表現差。欠擬合是指模型在訓練數據…

【SQL教程|07】sql中條件查詢where用法示例

SQL WHERE 條件查詢教程 在SQL中&#xff0c;WHERE 條件用于在 SELECT 語句后過濾結果集&#xff0c;只返回符合條件的記錄。它幫助我們從大量數據中提取所需的信息。以下是使用 WHERE 條件的逐步指南。 1. 基本語法 SELECT [字段] FROM [表] WHERE [條件];SELECT&#xff1a…

力扣 跳躍游戲 II

貪心算法&#xff0c;存下每一步的最遠&#xff0c;去達到全局的最小跳躍次數。 題目 從題中要達到最少次數&#xff0c;肯定是每一步盡可能走遠一點。但注意j被限制了范圍&#xff0c;這種不用想每一步遍歷時肯定選最大的num[i]&#xff0c;但要注意&#xff0c;題中是可以到…

如何查看 Linux 服務器的 MAC 地址:深入解析與實踐指南

&#x1f49d;&#x1f49d;&#x1f49d;歡迎來到我的博客&#xff0c;很高興能夠在這里和您見面&#xff01;希望您在這里可以感受到一份輕松愉快的氛圍&#xff0c;不僅可以獲得有趣的內容和知識&#xff0c;也可以暢所欲言、分享您的想法和見解。 推薦:kwan 的首頁,持續學…

Linux驅動學習(二)--字符設備

設備分類 字符設備塊設備網絡設備 內核結構圖&#xff1a; 字符設備號 字符設備號是32位的無符號整型值 高12位&#xff1a;主設備號低20位&#xff1a;次設備號 查看設備號 cat /proc/devices 設備號構造 直接使用宏MKDEV #define MKDEV(ma,mi) (((ma) << MINORBITS…

開發小技巧分享 02:xml解析工具

1.百度詞條 可擴展標記語言 (Extensible Markup Language, XML) &#xff0c;標準通用標記語言的子集&#xff0c;可以用來標記數據、定義數據類型&#xff0c;是一種允許用戶對自己的標記語言進行定義的源語言。 XML是標準通用標記語言 可擴展性良好,內容與形式分離,遵循嚴格的…

ffmpeg configure 研究1-命令行參數的分析

author: hjjdebug date: 2025年 02月 14日 星期五 17:16:12 CST description: ffmpeg configure 研究1 ./configure 命令行參數的分析 文章目錄 1 configure 對命令行參數的分析,在4019行1.1 函數名稱: is_in1.2. 函數名稱: enable1.3. 函數名稱: set_all 2 執行退出判斷的關鍵…

Linux操作系統:從分布式計算到容器化的實踐

Linux集群與高可用性技術&#xff1a;從分布式計算到容器化的實踐 摘要 隨著云計算和大數據技術的飛速發展&#xff0c;Linux集群和高可用性技術已成為現代IT架構的核心組成部分。本文以幽默風趣的方式&#xff0c;深入探討了Linux集群技術&#xff08;如Hadoop、Spark等分布…

python和pycharm 和Anaconda的關系

好的&#xff0c;下面我會詳細說明 Python、PyCharm 和 Anaconda 三者的關系&#xff0c;并逐一解釋它們的功能和作用。 1. Python&#xff08;編程語言&#xff09; 定義&#xff1a;Python 是一種高級編程語言&#xff0c;設計簡潔&#xff0c;易于學習&#xff0c;且功能強…

STM32 外部中斷和NVIC嵌套中斷向量控制器

目錄 背景 外部中斷/事件控制器(EXTI) 主要特性 功能說明 外部中斷線 嵌套向量中斷控制器 特性 ?中斷線&#xff08;Interrupt Line&#xff09; 中斷線的定義和作用 STM32中斷線的分類和數量 優先級分組 搶占優先級&#xff08;Preemption Priority&#xff09; …

代碼隨想錄算法【Day49】

Day49 42. 接雨水 思路 這道題利用單調棧進行橫向求解。對于每一個元素&#xff0c;找到它右邊第一個比它大的元素和左邊第一個比它大&#xff08;或者與它相等的元素&#xff0c;當然這種情況可以忽略&#xff09;&#xff0c;最后計算雨水的存儲量&#xff1a;&#xff08…

PHP 網絡編程介紹

PHP 學習資料 PHP 學習資料 PHP 學習資料 在當今數字化時代&#xff0c;網絡編程是開發各類應用必不可少的技能。PHP 作為一門廣泛應用于 Web 開發的編程語言&#xff0c;同樣具備強大的網絡編程能力。接下來&#xff0c;我們將深入探討 PHP 中網絡連接的建立、Socket 編程、…

《深度學習》——ResNet網絡

文章目錄 ResNet網絡ResNet網絡實例導入所需庫下載訓練數據和測試數據設置每個批次的樣本個數判斷是否使用GPU定義殘差模塊定義ResNet網絡模型導入GPU定義訓練函數定義測試函數創建損失函數和優化器訓練測試數據結果 ResNet網絡 ResNet&#xff08;Residual Network&#xff0…

為什么要學習AI、掌握AI技能有什么用?

隨著人工智能的迅速的發展&#xff0c;DeepSeek的爆火&#xff0c;加之目前就業環境的走向&#xff0c;越來越多的職場朋友開始關注到AI的發展&#xff0c;重視AI技能的掌握。不少同學都會問&#xff1a;“職場人為什么要學習AI、掌握AI技能&#xff1f;” 為什么要學AI 現…

AIP-146 泛化域

編號146原文鏈接AIP-146: Generic fields狀態批準創建日期2019-05-28更新日期2019-05-28 API中的大多數域&#xff0c;無論是在請求、資源還是自定義應答中&#xff0c;都有具體的類型或模式。這個模式是約定的一部分&#xff0c;開發者依此約定進行編碼。 然而&#xff0c;偶…

vue3和vue2的組件開發有什么區別

Vue3和Vue2在組件開發上存在不少差異&#xff0c;下面從多個方面詳細介紹&#xff1a; 響應式原理 Vue2&#xff1a;用Object.defineProperty()方法來實現響應式。打個比方&#xff0c;它就像給對象的每個屬性都安排了一個“小管家”&#xff0c;屬性被訪問或修改時&#xff0…

【NLP 25、模型訓練方式】

目錄 一、按學習范式分類 1. 監督學習&#xff08;Supervised Learning&#xff09; 2. 無監督學習&#xff08;Unsupervised Learning&#xff09; 3. 半監督學習&#xff08;Semi-supervised Learning&#xff09; 4. 強化學習&#xff08;Reinforcement Learning, RL&#x…

1-知識圖譜-概述和介紹

知識圖譜&#xff1a;浙江大學教授 陳華軍 知識圖譜 1課時 http://openkg.cn/datasets-type/ 知識圖譜的價值 知識圖譜是有什么用&#xff1f; 語義搜索 問答系統 QA問答對知識圖譜&#xff1a;結構化圖 輔助推薦系統 大數據分析系統 自然語言理解 輔助視覺理解 例…