前言
隨著 DEV24.1.3 的釋出,XAF Blazor 中的屬性編輯器(PropertyEditor)也進行了很大的改動,在使用體驗上也更接近 WinForm 了,由於進行了大量的封裝,理解上沒有 WinForm 直觀,所以本文透過對屬性編輯器的原理進行解析,並對比新舊版本中的變化,使大家能夠對屬性編輯器有一個更全面的認識。
原理
XAF 可以建立與平臺無關的業務程式碼,而 PropertyEditor 就是它們與各個平臺之間的一個橋樑,也就是說 PropertyEditor 每個平臺都有各自的實現。從表面上看 PropertyEditor 的原理並不複雜,BO 中屬性的更改會觸發 PropertyEditor 中值的更改,而 PropertyEditor 值的更改又會更新具體平臺元件的值,反過來也是一樣,元件值的更改會觸發 PropertyEditor 值的更改,而PropertyEditor 值的更改又會更新 BO 中的屬性值。
如果我們使用的是XPO,PersistentBase 是全部 BO 的基類,它實施一個 INotifyPropertyChanged 介面,我們可以透過 INotifyPropertyChanged 介面來監聽每一個屬性的變化,這樣我們就可以在屬性值發生變化時通知 PropertyEditor,而這個監聽工作是在 DetailView 中完成的。DetailView 在監聽到 BO 中屬性值發生更改時,會查詢到具體的 PropertyEditor,並呼叫它的 Refresh 方法。
PropertyEditor 的屬性(如:Caption,DisplayFormat)大部分來自 XAF 的 Model,也就是在 PropertyEditor 初始化時會傳遞一個 IModelMemberViewItem 物件,它裡面包含了我們在模型編輯器中設定的值或一些預設值。
PropertyEditor 中有兩個比較重要的方法 ReadValue 與 WriteValue,我一開始看到這個兩方法時也產生了混淆,ReadValue 與 WriteValue 它們針對的物件是 BO 中的屬性,而不是 PropertyEditor,也就是說 ReadValue 是讀取 BO 中屬性的值,而 WriteValue 是將值寫入到屬性中。
PropertyEditor 中還有一個 Control 屬性,它的型別是 Object,不同的平臺它返回的型別是不一樣的,這個也是各個平臺的元件,WinForm 返回的是 Control 型別,Blazor 返回的是 ComponentAdapter 或 ComponentModel (24.1.3之後 XAF 自帶編輯器預設返回型別)。由於不同的平臺渲染方式不同,對於 WinForm 來說,它返回的是 Control 型別,可以直接將其放置到父元件的 Controls 中,而對於 Blazor 就不行,Blazor 元件就不能像 WinForm 控制元件那樣操作,它是透過 RenderFragment 進行組合在一起的,所以 Blazor 中的 PropertyEditor 同時還實施了一個 IComponentContentHolder 介面,透過它可以返回一個 RenderFragment。
在對外暴露元件時,Blazor 比 WinForm 要多一步,Blazor 中的 RenderFragment 是用於渲染元件的,不能透過 Control 屬性返回,同時我們也無法直接操作 RenderFragment,我們是透過 ComponentModel 間接操作 Blazor 元件渲染的,所以當我們在外部想自定義 PropertyEditor 中的元件時,WinForm 是直接操作 Control,而 Blazor 是透過 ComponentModel 來完成。
PropertyEditor 的大部分子類都是圍繞上面提到的屬性或方法進行封裝,以適應不同的平臺。對於 WinForm 來說相對比較簡單,就是直接操作 Control,而對於 Blazor 來說就要繁瑣一些,就因為這樣 XAF 針對 Blazor PropertyEditor 建立,做了大量的封裝,特別是在最新的24.1.3中丟棄了 ComponentAdapter,使 PropertyEditor 的建立更加簡單,甚至比 WinForm 還要簡潔一些。下面主要以 Blazor 為主介紹 PropertyEditor 的相關技術點。
由於本文講的是 PropertyEditor 的原理,預設讀者是熟悉 PropertyEditor 的建立,所以不會再去講解 PropertyEditor 的建立過程,而是隻講解它的技術點,如果對 PropertyEditor 的建立不熟悉的小夥伴,可以檢視 XAF 的官方文件。
在 XAF 新版本中 ComponentAdapter 被廢棄了,那它被引入的原因及被廢棄的原因是什麼呢?在 XAF Blazor 建立之初 ComponentAdapter 就已存在,透過它的名字我們知道它是一個代理層,負責 PropertyEditor 與 ComponentModel 之間的通訊。那為什麼要加入這個代理層呢,而不是 PropertyEditor 直接操作 ComponentModel 呢。這裡主要考慮是 PropertyEditor 的封裝,由於 PropertyEditor 的操作基本是固定的(如,讀值、寫值及一些常規屬性的設定),而 ComponentModel 是針對不同的元件的,不同的元件會有不同的屬性,比如類似值屬性,文字框是Text,日期框是Date,數值框是Value等,透過 ComponentAdapter 來適配不同的 ComponentModel(如:GetValue,SetValue 等),PropertyEditor 再去操作 ComponentAdapter ,這樣更利於 PropertyEditor 的封裝。
那在新版本中為什麼 ComponentAdapter 又被廢棄了呢,透過 XAF 的部落格可以瞭解到,由於增加了 ComponentAdapter,使建立自定義 PropertyEditor 變的比較繁瑣,移除 ComponentAdapter 後,可以使 PropertyEditor 的建立更接近於 WinForm。在沒有了 ComponentAdapter 後,是不是 PropertyEditor 直接操作 ComponentModel 了呢,如果是那樣的話,就會出現前面所講到的,這會增加自定義 PropertyEditor 的複雜度,那新版是如何實現的呢。新版中是透過介面的方式來替代 ComponentAdapter,如新版中增加了一個 IHandleValueComponentModel 介面,透過這個介面 PropertyEditor 就可以獲取、更新或監聽元件值的變化,同時又增加了 DxComponentModelBase 這樣的一個基類,它包含了一些常用的屬性。也就是說新版是透過增加不同的介面及擴充套件基類的方式來替代 ComponentAdapter,這樣在簡化自定義 PropertyEditor 的同時,也保持了 PropertyEditor 的靈活性。
ComponentModel 在新版中還增加了一個 ComponentType 屬性,XAF 透過 ComponentType 屬性,可以自動完成 Renderer 的建立及屬性的賦值。在之前的版本中我們都是在 Renderer 中建立一個 Create 靜態方法,將 ComponentModel 傳遞進去,Renderer 需要將 ComponentModel 中的屬性一一賦值給元件,這個過程非常繁瑣,特別是 XAF 自身的 Renderer,程式碼量非常的大。新版中透過 ComponentType 的元件型別實現元件自動建立的同時,還將 ComponentModel 的屬性自動傳遞給元件(提示:ComponentModel 的屬性要與元件的屬性保持一致)。由於 XAF 中的 PropertyEditor 都是對 Blazor 元件的封裝,在 XAF 中直接省掉了 Renderer,也就是在 XAF 中沒有 Renderer 的相關程式碼了。
在新版中建立一個 Blazor PropertyEditor 則相當簡單,首先建立一個繼承自 DxComponentModelBase 的 ComponentModel,在 ComponentModel 中增加元件所需要的屬性,如果是自定義元件需要建立一個 Renderer,如果是 DEV 自身的 Blazor 元件,則直接將元件賦值給 ComponentType,同時基於 BlazorPropertyEditorBase 再建立一個 PropertyEditor,重寫 CreateComponentModel 方法,並返回 ComponentModel 例項,整個 PropertyEditor 建立過程就結束了,相對來說要比 WinForm 還要簡潔一些。
總結
在日常的 XAF 開發中,不管是增強或是個性化定製,都離不開 PropertyEditor,簡化 PropertyEditor 的建立過程,在降低建立 PropertyEditor 難度的同時,也能大幅提高自定義 PropertyEditor 在專案中的佔比,提高使用者操作體驗。
https://www.cnblogs.com/haoxj/p/18255657