深度解析XAF Blazor中的属性编辑器改动

随着 DEV24.1.3的发布,XAF Blazor中的属性编辑器(PropertyEditor)也经历了巨大的改变,使得使用体验更接近WinForm。本文将通过对属性编辑器的原理进行解析,并对比新旧版本中的变化,以帮助读者更全面地了解属性编辑器。

原理

XAF可以创建与平台无关的业务代码,而属性编辑器(PropertyEditor)则是它们与各个平台之间的桥梁,每个平台都有各自的实现。表面上看,属性编辑器的原理并不复杂:BO中属性的更改会触发属性编辑器中值的更改,而属性编辑器值的更改又会更新具体平台组件的值,反之亦然。

如果我们使用的是XPO,PersistentBase是全部BO的基类,它实现了INotifyPropertyChanged接口,通过该接口可以监听每个属性的变化,从而在属性值发生变化时通知属性编辑器。这个监听工作是在DetailView中完成的。DetailView在监听到BO中属性值发生更改时,会查找到具体的属性编辑器,并调用它的Refresh方法。

属性编辑器的属性(如:Caption,DisplayFormat)大部分来自XAF的Model,在属性编辑器初始化时会传递一个IModelMemberViewItem对象,它里面包含了我们在模型编辑器中设置的值或一些默认值。

属性编辑器中有两个重要的方法ReadValue和WriteValue,这两个方法针对的对象是BO中的属性,而不是属性编辑器。ReadValue用于读取BO中属性的值,而WriteValue用于将值写入到属性中。

属性编辑器中还有一个Control属性,它的类型是Object,不同的平台返回的类型也不一样。这个属性是各个平台的组件。WinForm返回的是Control类型,而Blazor返回的是ComponentAdapter或ComponentModel(24.1.3之后XAF自带编辑器默认返回类型)。由于不同的平台渲染方式不同,对于WinForm来说,它返回的是Control类型,可以直接将其放置到父组件的Controls中,而对于Blazor则不行,Blazor组件是通过RenderFragment进行组合的。因此,Blazor中的属性编辑器同时实施了一个IComponentContentHolder接口,通过它可以返回一个RenderFragment。

在对外暴露组件时,Blazor比WinForm要多一步。Blazor中的RenderFragment用于渲染组件,不能通过Control属性返回。同时,我们也无法直接操作RenderFragment,而是通过ComponentModel间接操作Blazor组件的渲染。因此,当我们在外部想自定义属性编辑器中的组件时,WinForm是直接操作Control,而Blazor是通过ComponentModel来完成。

属性编辑器的大部分子类都是围绕上面提到的属性或方法进行封装,以适应不同的平台。对于WinForm来说比较简单,直接操作Control,而对于Blazor来说要繁琐一些。因此,XAF针对Blazor属性编辑器的创建,做了大量的封装,特别是在最新的24.1.3中丢弃了ComponentAdapter,使属性编辑器的创建更加简单,甚至比WinForm还要简洁一些。下面主要以Blazor为主介绍属性编辑器的相关技术点。

由于本文讲的是属性编辑器的原理,默认读者是熟悉属性编辑器的创建,因此不会再去讲解属性编辑器的创建过程,而是只讲解它的技术点。如果对属性编辑器的创建不熟悉的读者,可以查看XAF的官方文档。

在XAF新版本中,ComponentAdapter被废弃了。那它被引入的原因及被废弃的原因是什么呢?在XAF Blazor创建之初,ComponentAdapter就已存在。通过它的名字我们知道它是一个代理层,负责属性编辑器与ComponentModel之间的通讯。那为什么要加入这个代理层呢,而不是属性编辑器直接操作ComponentModel呢?主要考虑是属性编辑器的封装。由于属性编辑器的操作基本是固定的(如,读值、写值及一些常规属性的设置),而ComponentModel是针对不同的组件的,不同的组件会有不同的属性。因此,通过ComponentAdapter来适配不同的ComponentModel,这样更利于属性编辑器的封装。

在新版本中,为什么ComponentAdapter又被废弃了呢?通过XAF的博客可以了解到,由于增加了ComponentAdapter,使得创建自定义属性编辑器变得比较繁琐。移除ComponentAdapter后,可以使属性编辑器的创建更接近于WinForm。在没有了ComponentAdapter后,是不是属性编辑器直接操作ComponentModel了呢?如果是那样的话,就会出现前面所讲到的情况,这会增加自定义属性编辑器的复杂度。那新版是如何实现的呢?新版中是通过接口的方式来替代ComponentAdapter,如新版中增加了一个IHandleValueComponentModel接口,通过这个接口属性编辑器就可以获取、更新或监听组件值的变化,同时又增加了DxComponentModelBase这样的一个基类,它包含了一些常用的属性。也就是说新版是通过增加不同的接口及扩展基类的方式来替代ComponentAdapter,这样在简化自定义属性编辑器的同时,也保持了属性编辑器的灵活性。

在新版中,ComponentModel还增加了一个ComponentType属性,XAF通过ComponentType属性,可以自动完成Renderer的创建及属性的赋值。在之前的版本中,我们都是在Renderer中创建一个Create静态方法,将ComponentModel传递进去,Renderer需要将ComponentModel中的属性一一赋值给组件,这个过程非常繁琐,特别是XAF自身的Renderer,代码量非常的大。新版中通过ComponentType的组件类型实现组件自动创建的同时,还将ComponentModel的属性自动传递给组件(提示:ComponentModel的属性要与组件的属性保持一致)。由于XAF中的属性编辑器都是对Blazor组件的封装,因此在XAF中直接省掉了Renderer,也就是在XAF中没有Renderer的相关代码了。

在新版中创建一个Blazor属性编辑器则相当简单。首先创建一个继承自DxComponentModelBase的ComponentModel,在ComponentModel中增加组件所需要的属性。如果是自定义组件需要创建一个Renderer,如果是DEV自身的Blazor组件,则直接将组件赋值给ComponentType。同时基于BlazorPropertyEditorBase再创建一个属性编辑器,重写CreateComponentModel方法,并返回ComponentModel实例,整个属性编辑器创建过程就结束了,相对来说要比WinForm还要简洁一些。

总结

在日常的XAF开发中,不管是增强或是个性化定制,都离不开属性编辑器。简化属性编辑器的创建过程,在降低创建属性编辑器难度的同时,也能大幅提高自定义属性编辑器在项目中的占比,提高用户操作体验。

原文链接: https://www.cnblogs.com/haoxj/p/18255657

标签:游戏攻略