WPF入門系列教程(二) 深入剖析WPF Binding的使用方法
同一個物件(特指System.Windows.DependencyObject的子類)的同一種屬性(特指DependencyProperty)只能擁有一個binding。
這一點可以通過設定binding物件的方法名得知:
public static BindingExpressionBase SetBinding(
DependencyObject target,
DependencyProperty dp,
BindingBase binding
)
方法名是SetBinding而不是AddBinding。如果想要驗證一下,也可以在listView1_SelectionChanged
當多次在同一個物件上設定Binding時,其實並不會增加多餘的binding,而是將原來的binding替換掉了,這裡以textBox_ContactID為例如下圖:
另一個相對的概念:同一個Binding可以同時與多個物件的多個屬性(或同一物件的多個屬性)形成Binding。這一點就不做實踐驗證了,在MSDN上《Data Binding Overview》中有一段“Binding and BindingExpression”就是講這個道理的。
也許很多讀者已經知道或潛意識裡有了這種概念,這裡只是做一些強調。
改善優化WPF Binding
下面的內容在修正錯誤的同時,還將對原有的Binding做一些小小的改進。
情況如下:在將資料庫中的DataTable物件的DefaultView通過XAML語言Binding到listView1物件的ItemsSource上時總是失敗。
答案那就是使用DataContext,這裡先來梳理一下Binding的一些基本概念及使用方法。
l典型的Binding具有四個重要組成部分:Binding目標物件(binding target object)、目標物件屬性(target property)、Binding資料來源(binding source)、Path(用於指明要從資料來源中取得的值,就是我們通常寫的屬性名稱)。例如:想要把一個員工(Employee)的姓名(EmpName)Binding顯示到一個TextBox的Text屬性上。
分析一下其中的4個組成部分:Binding目標物件是TextBox(因為它要最終使用這些資料);目標物件屬性是TextProperty(這裡要注意,不是Text屬性,而是與Text屬性相對應的Dependency
Property,通常命名是屬性名+Property);
資料來源是員工(Employee);Path是EmpName。我的經驗是:剛開始使用Binding的時候由於還不熟悉,建議在做之前先在心理把這4個部分對號入座一下,等日後就可以熟悉成自然了。
l目標物件屬性一定要是Dependency Property。由於只有DependencyObject類及其子類能夠定義Dependency Property,因此,Binding目標物件就必須是DependencyObject類及其子類了。另一方面反過來說,UIElement(DependencyObject的子類)的大多數屬性都有與其對應的Dependency Property,因此,大多數的UIElement屬性我們都可以利用其Dependency Property作為目標物件屬性從而與資料來源建立Binding。
l再有一點,建立Binding的資料來源不一定要是一個CLR物件,也可以是XML資料、ADO.NET物件及DependencyObject。
關於為什麼binding的物件一定要是DependencyObject,為什麼binding物件的屬性一定要是dependency property,這個我就不知道了。
在我第一次看到在XAML中設定Binding的語法時,簡直是滿頭霧水。這東西到底怎麼回事,簡直就沒有章法可循,一會兒ElementName,一會兒Source,還有時什麼都沒有,就一個{Binding },再有就是Mode,UpdateSourceTrigger等,簡直沒有頭緒。
不過只要開啟MSDN,看一下System.Windows.Data.Binding物件,似乎開始有點感覺了,檢視Binding物件的屬性欄(Properties),你會發現,原來我們在XAML中設定Binding時使用的東西和Binding物件的屬性好像都是一一對應的。
確實如此,在XAML中設定Binding就是在這隻System.Windows.Data.Binding物件,這裡先提醒一下大家。
WPF可不是就這麼一個Binding噢,實時上,從BindingBase繼承過來的有三個兄弟,Binding只是最常用而已。
找到了binding的老家,相信大家對{Binding ElementName=ElemName, Path=PropertyName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, …}這樣的語法應該清楚了吧。
那麼{Binding } 及{Binding XXXX}這樣的語法呢?其實可以就是等於new Binding()及new Binding(XXXX),其實可以考慮成是分別使用了Binding的兩種構造方法,而XXXX的作用自然也就清晰了。
其實和{Binding Path=XXXX}是等價的,都是設定Path屬性的。至於這裡的Path屬性在我們平常使用時其實是用來設定屬性名的地方,為什麼不直接叫做是PropertyName之類的名字呢?這個又要留著以後思考了。
當一個數據源與一個UIElement形成Binding後,資料來源的改變,如果傳遞給UIElement的指定屬性?
同樣,當UIElement中指定屬性值改變了,如何才能夠通知到資料來源?要討論這個問題,需要按資料來源來分類進行討論。這個分類並不是互斥的之前存在一些交集。
因此,同一種Binding場景可能適用於多種情況。
下面的內容是來自MSDN中《Binding Sources Overview》中的內容,由於怕翻譯的不好而誤導大家,所以分類的名稱還是使用英文吧。
Using a CLR Class as the Binding Source Object
在典型的Binding情景中,一個CLR 物件作為資料來源,將其中一個屬性值Binding到某個UIElement的屬性上(以上面顯示員工姓名的情景為例。
用XAML實現<TextBox Text=”{Binding Source=Employee, Path=EmpName}” />)。當資料來源屬性值變化了,相應讓UIElement中對應屬性變化。
需要實現一箇中通知機制,最簡單的方法是通過實現INotifyPropertyChanged介面完成此操作。具體實現,可以參考MSDN上的文章《How to: Implement Property Change Notification》。
其實就是實現了”public event PropertyChangedEventHandler PropertyChanged;”事件,通常的做法是在上面加封裝一個”protected void OnPropertyChanged(string name)”函式。在屬性的set方法結尾處,呼叫此函式實現通知的作用。
如果不實行此介面,則必須自己實現一套通知系統。為你的類中的每一個想要實現通知機制的屬性建立一個PropertyChanged模板。
即為你的每一個屬性建立一個名為”PropertyNameChanged”事件,其中PropertyName是與這個事件對應的屬性名稱。然後在屬性發生變化是觸發這個事件。(聽起來就麻煩,我也懶得去實踐驗證了)
如果以上兩種方法都無法實現的話,那麼就只好在你認為需要的時候顯示的呼叫UpdateTarget方法來手動更新了。
(這種方法沒用過,感覺已經失去了Binding的意義了)
Entire Objects Used as a Binding Source
可以理解為將整個物件作為資料來源,與上一個題目的區別很小。
基本上可以認為是原來Binding實現的是資料來源屬性與UIElement屬性之間的Binding,而這次是直接將一個Object物件與一個UIElement的屬性進行Binding了。
這種情況可以使用Binding的Source屬性指明資料來源(假設Employee物件與一個ListBox實現banding,用XAML實現<ListBox ItemsSource=”{Binding Employee}” />),或者還可以將DataContext屬性設定為Employee(DataContext屬性只有UIElement的一個子類System.Windows.FrameworkElement及其子類才會有)。
這樣,在設定ItemsSource時就可以直接使用”{Binding }”這樣的語法了。當然,DataContext屬性設定成Employee還是需要使用DataContext=”{Binding Employee}”語句的。
有人在這裡會嘲笑這一舉動實在是囉嗦,事實上,DataContext屬性是非常有用的。之所以Binding物件失敗而最終不得不使用C#語句進行Binding就是因為沒有設定DataContext。
因為,當我們使用{Binding Source= Employee, Path=”EmpName”}設定屬性後,WPF查詢Source物件是根據Element Tree逐級向上自己的Parent Element查詢的,查詢每一Element上的DataContext所指定的內容是否包含這一Source內容,並以第一個匹配到的結果作為最終物件。
也就是說,DataContext是按照Element Tree向下繼承的,並且決定這WPF在執行時能否找到我們所制定的Source物件。即使我們使用{Binding Path=”EmpName”}的語句也是如此。
比較常見的做法是,當我們有大量的Element需要與一個數據源中的眾多屬性實現Binding時,我們可以直接在一個公共Parent Element的DataContext上設定這一物件(甚至可以是整個window或page上),這將會極大的加少我們重複的程式碼量,同時也會是程式更加可讀。
當我們需要使用的資料來源是一個集合時,如果想要實現改變通知的機制。
同樣需要實現一個介面INotifyCollectionChanged用來在集合發生改變時發起,用法單個物件是實現INotifyPropertyChanged介面類似。
首先是將上以前做的不是很好的那一段程式碼改成XAML語言實現。
將TextBox標籤中的Text屬性與上面listView1中被選定的記錄(listView1.SelectedItem)中對應屬性(例如:ContactID)做Binding。
實現後如圖所示:
<TextBox Text="{Binding ElementName=listView1, Path=SelectedItem.ContactID}" MinWidth="100" />
看以來程式碼有點長,而且,下面的幾個TextBox也都存在大量的重複內容,這裡就可以考慮使用上面所說的DataContext了。
相關推薦
WPF入門系列教程(二) 深入剖析WPF Binding的使用方法
同一個物件(特指System.Windows.DependencyObject的子類)的同一種屬性(特指DependencyProperty)只能擁有一個binding。 這一點可以通過設定binding物件的方法名得知: public static Binding
WPF入門教程系列(二) 深入剖析WPF Binding的使用方法
同一個物件(特指System.Windows.DependencyObject的子類)的同一種屬性(特指DependencyProperty)只能擁有一個binding。 這一點可以通過設定binding物件的方法名得知: public static Bindin
Python入門系列教程(二)
字符 小寫 無符號 bsp div width raw_input abc body 字符串 1.字符串輸出 name = ‘xiaoming‘ print("姓名:%s"%name) 2.字符串輸入 userName = raw_input(‘請輸
Angular2入門系列教程6-路由(二)-使用多層級路由並在在路由中傳遞複雜引數
之前介紹了簡單的路由以及傳參,這篇文章我們將要學習複雜一些的路由以及傳遞其他附加引數。一個好的路由系統可以使我們的程式更好的工作。 假設你已經跟上了我們的進度。 我們來為我們的文章明細新增一個評論框;當我們在明細中點選評論的時候,在我們的明細頁面顯示評論,這裡
資料探勘入門系列教程(二)之分類問題OneR演算法
資料探勘入門系列教程(二)之分類問題OneR演算法 資料探勘入門系列部落格:https://www.cnblogs.com/xiaohuiduan/category/1661541.html 專案地址:GitHub 在上一篇部落格中,我們通過分析親和性來尋找資料集中資料與資料之間的相關關係。這篇部落
Python入門系列教程(五)函數
st3 python入門 test print 缺省 .com 教程 技術 log 全局變量 修改全局變量 a=100 def test(): global a a=200 print a 多個返回值 缺省參數 d
Java多線程(二) —— 深入剖析ThreadLocal
tst isp get方法 con 需要 detail 什麽 RM 深入 對Java多線程中的ThreadLocal類還不是很了解,所以在此總結一下。 主要參考了http://www.cnblogs.com/dolphin0520/p/3920407.html 中的文章。
MySQL系列教程二---觸發器
1. 觸發器簡介 觸發器是一個特殊的儲存過程,執行儲存過程需要使用CALL語句來呼叫,但是觸發器的執行不需要用CALL語句呼叫,也不需要手工啟動,只要的當一個預定義的事件發生時,就會被MYSQL自動呼叫。比如當對fruits表進行INSERT,DELETE或UPDATE操作時就會啟用它。
大資料入門環境搭建整理、大資料入門系列教程合集、大資料生態圈技術整理彙總、大資料常見錯誤合集、大資料的離線和實時資料處理流程分析
本篇文章主要整理了筆者學習大資料時整理的一些文章,文章是從環境搭建到整個大資料生態圈的常用技術整理,環境希望可以幫助到剛學習大資料到童鞋,大家在學習過程中有問題可以隨時評論回覆! 大資料生態圈涉及技術: Hadoop、MapReduce、HDFS、Hive、Hbase、Spark、Scala
Flutter入門系列(二)---Flutter的原理及美團的實踐
轉載自:美團技術團隊 導讀 Flutter是Google開發的一套全新的跨平臺、開源UI框架,支援iOS、Android系統開發,並且是未來新作業系統Fuchsia的預設開發套件。自從2017年5月釋出第一個版本以來,目前Flutter已經發布了近60個版本,並且在2018年5月釋出了第一個
深度學習入門系列教程
這是一個優秀的零基礎入門深度學習教程! 零基礎入門深度學習(1) - 感知器 零基礎入門深度學習(2) - 線性單元和梯度下降 零基礎入門深度學習(3) - 神經網路和反向傳播演算法 零基礎入門深度學習(4) - 卷積神經網路 零基礎入門深度學習(5) - 迴圈神經網路 零基礎入門深度學習(6)
Docker入門系列之二:使用dockerfile製作包含指定web應用的映象
實現題目描述的這個需求有很多種辦法,作為入門,讓我們從最簡單的辦法開始。 首先使用命令docker ps確保當前沒有正在執行的Docker例項。 執行命令docker run -it nginx: 然後我們另外開一個終端,用docker ps命令檢視這個執行起來的容器例項,Status的Up 54 s
spark入門系列教程三——spark sql(一)
Spark SQL是用於結構化資料處理的Spark模組,可以通過sql、dataset、dataframe與spark sql進行互動。更多理論性知識請移步官網http://spark.apache.org/docs/2.3.1/sql-programming-guide.html 在spark 2.0以前
WordPress系列教程(二)----WordPress基本使用和常用設定
接著上文,上篇文章已經把網站搭建好了,這篇主要講WordPress基本使用與常用設定。 一、前後臺頁面 預設前臺頁面是這個樣子: 後臺是這個樣子: 後臺包含以下這些模組: 下面我們就來講講上面的功能。 二、文章 文章欄目下包括,寫文章,分類目
爬蟲入門系列(二):優雅的HTTP庫requests
爬蟲入門系列目錄: urllib、urllib2、urllib3、httplib、httplib2 都是和 HTTP 相關的 Python 模組,看名字就覺得很反人類,更糟糕的是這些模組在 Python2 與 Python3 中有很大的差異,如果業務程式碼要同時相容 2 和 3,寫起來
內網***系列教程二(ping和tracertroute命令的使用)
一、ICMP協議ICMP是在RFC792定義的網際網路協議,通常用於返回錯誤資訊和分析路由。為了有效的提好轉發IP資料報和提高交付的機會。ICMP是IP層協議,ICMP的報文包含在IP資料包中,作為其中的資料部分。當其加上了IP資料包的首部(20位元組)便組成了IP資料報傳送出去。他通常不由網路程式直接使用,
sencha touch 入門系列 (二)sencha touch 開發準備
這是本人第一次寫部落格教程,沒什麼經驗,文筆也不是很好,寫這教程一方面為了鞏固自己這段時間的學習成果,一方面幫助大家解決問題,歡迎大家多提建議,指出問題。接下來我們就開始我們的sencha touch開發之旅了。 首先,我們開始搭建sencha touch的開發環境
以太坊構建DApps系列教程(二):構建TNS代幣
在本系列關於使用以太坊構建DApps教程的第1部分中,我們引導大家做了兩個版本的本地區塊鏈進行開發:一個Ganache版本和一個完整的私有PoA版本。 在這一部分中,我們將深入研究並構建我們的TNS代幣:使用者將使用代幣對Story DAO中的提案進行投票。 先決條件 按照上一部
ASP.NET Identity入門系列教程(一) 初識Identity
摘要 通過本文你將瞭解ASP.NET身份驗證機制,表單認證的基本流程,ASP.NET Membership的一些弊端以及ASP.NET Identity的主要優勢。 目錄 身份驗證(Authentication)和授權(Authorization) 我們先來思考一個問題:如何構建安全的WEB應
MongoDB入門系列(二):Insert、Update、Delete、Drop
概述 本章節介紹Insert、Update、Delete、Drop操作基本語法。 環境: Version:3.4 insert insert()基本語法如下: db.collection.insert( <document or array of documents&