WPF自定義集合控件概述與遇到的問題
WPF中涉及到控件,那么就不可能繞過Template。首先咱們來看一下WPF中基礎(chǔ)集合控件ItemsControl涉及到的幾個(gè)Template。
- ItemsControl自身的Template,類型為ControlTemplate,其內(nèi)部聲明了一個(gè)ItemsPresenter用以呈現(xiàn)下述第2個(gè)Template。當(dāng)我們?yōu)榧峡丶x予自定義模板時(shí),ItemsPresenter不可丟棄,否則就失去了集合控件的意義;
- ItemsPanel,類型為ItemsPanelTemplate,其內(nèi)容一般為可擁有多個(gè)子項(xiàng)的布局控件,如StackPanel、Grid等;
- ItemTemplate,類型為DataTemplate,呈現(xiàn)各數(shù)據(jù)項(xiàng)。
此時(shí)有人會(huì)問,數(shù)據(jù)項(xiàng)對(duì)應(yīng)的類型是ControlTemplate的Template跑哪里去了?說的是呀,話說只要是繼承自Control的控件都應(yīng)該有這個(gè)Template屬性,你看ItemsControl不就有嗎。那么與各子項(xiàng)對(duì)應(yīng)的這個(gè)屬性應(yīng)該怎么獲取和設(shè)置呢。ItemsControl并沒有提供給我們直接訪問子項(xiàng)Template屬性的方法,而是給了另一個(gè)屬性ItemContainerStyle。ItemContainerStyle對(duì)應(yīng)子項(xiàng)控件的Style,因此我們可以通過該Style設(shè)置子項(xiàng)控件的Template(上述第3條實(shí)際上對(duì)應(yīng)子項(xiàng)控件Template內(nèi)的ContentPresenter)。ItemContainerStyle對(duì)于ItemsControl是設(shè)置ContentPresenter的樣式,不過ContentPresenter并沒有Template屬性,但是對(duì)于ListBox來說,它設(shè)置的是ListBoxItem的Style。
關(guān)于為什么wpf不給我們直接設(shè)置子項(xiàng)控件Template的方法,我沒有特地研究過,不過我想微軟的開發(fā)人員是這么想的:也許有人希望在保持原先默認(rèn)的Template,對(duì)其它屬性做一些掌控,比如可見性,那么何不如直接給你們?cè)O(shè)置所有樣式的機(jī)會(huì)呢?(上帝說,要有光。于是就有了光。我等P民感恩戴德。)
WPF靈活地樣式自定義給了開發(fā)人員極大的便利,同時(shí)也帶來了可能破壞控件內(nèi)在邏輯的問題。如何做到設(shè)計(jì)和邏輯分離,周大牛在WPF中自定義控件(3) CustomControl說的很詳細(xì),不過由于該博文“年代久遠(yuǎn)”,因此不排除有過時(shí)的內(nèi)容。
原本我打算自定義一個(gè)名為UserAddDelItemsControl的集合控件,該控件繼承自ItemsControl,為用戶提供自增刪子項(xiàng)的功能,但進(jìn)行到一半的時(shí)候發(fā)現(xiàn)它的ItemsSource屬性給我造成了相當(dāng)大的麻煩。ItemsSource != null即有數(shù)據(jù)源綁定時(shí),我要去判斷可能的數(shù)據(jù)源類型,謝天謝地,大部分實(shí)體數(shù)據(jù)源都繼承自IList,該接口有我期望的Insert、Remove等方法。但是假設(shè)我Insert一個(gè)新數(shù)據(jù)項(xiàng)(注意此處是數(shù)據(jù)項(xiàng))的時(shí)候,我要不要去刷新界面,以及如何去刷新界面以反映后臺(tái)數(shù)據(jù)列表的更改呢,所以我又要去判斷數(shù)據(jù)源是否繼承自INotifyCollectionChanged。假如它沒有繼承自INotifyCollectionChanged,那么我該怎么辦?或許我該使用 ICollectionView.Refresh()方法?或許最終可以實(shí)現(xiàn),但這樣的控件并不能使我滿意。我意識(shí)到要實(shí)現(xiàn)一個(gè)優(yōu)良的可增刪項(xiàng)的集合控件的真正重點(diǎn)和難點(diǎn)是需要重寫ItemsSource屬性,甚至要為其自定義一套數(shù)據(jù)源處理邏輯。水太深鳥!時(shí)間不多,我只能暫時(shí)放棄。
不過該項(xiàng)工作并沒有白做,除自定義控件的知識(shí)外,重溫并加深了關(guān)于Command、VisualState(4.0,可以理解為Trigger的升級(jí)版)的知識(shí)。
- RoutedCommand,可理解為事件event,或者更本質(zhì)上,其實(shí)是一個(gè)信號(hào)。控件(如按鈕)觸發(fā)這個(gè)命令,有人(類或?qū)嵗┦盏竭@個(gè)命令,檢查自己能否處理(擁有的CommandBindingCollection是否包含與這個(gè)命令相關(guān)的CommandBinding(檢查其Command屬性)),若找到對(duì)應(yīng)的CommandBinding,那么就執(zhí)行CommandBinding的Executed邏輯。
- 按第1條理解,RoutedCommand定義在哪里并不要緊,一般將之定義為自定義控件的靜態(tài)字段。
- 事件和命令的區(qū)別,個(gè)人理解:事件的信號(hào)源是一個(gè),關(guān)注者收到信號(hào)后做自己計(jì)劃做的事,信號(hào)源并不care關(guān)注者做什么事;命令的信號(hào)源可以有多個(gè),當(dāng)然關(guān)注者也可以有多個(gè),一般來說,所有信號(hào)源都期望關(guān)注者執(zhí)行一個(gè)或一組邏輯,而關(guān)注者也遵循它們的意愿,這里的關(guān)注者可以理解為代理。
- 相同VisualStateGroup包含的所有VisualState互斥,使用VisualStateManager.GoToState來進(jìn)行狀態(tài)跳轉(zhuǎn)。
轉(zhuǎn)載請(qǐng)注明本文出處:http://www.rzrgm.cn/newton/archive/2012/12/28/2836672.html。

浙公網(wǎng)安備 33010602011771號(hào)