1. 项目概述
在DevExpress XAF框架中为ASP.NET Core Blazor应用添加一个简单的操作(Simple Action)是每个XAF开发者都会遇到的基础需求。这个看似简单的功能实际上涉及到XAF框架中控制器、视图、动作系统的核心设计理念。我最近在升级一个老项目到v25.2.5版本时,就遇到了几个关于Simple Action的典型问题,今天就把这些实战经验系统梳理一下。
Simple Action本质上是一个预定义的命令模式实现,它允许开发者在不直接修改视图控制器的情况下,向UI层添加可交互元素。在Blazor前端中,这些动作会渲染为按钮或菜单项,触发后执行对应的业务逻辑。与传统的ASP.NET Core控制器动作不同,XAF的Action系统提供了声明式的配置方式和内置的权限集成。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心概念解析
2.1 XAF中的Action系统架构
XAF的Action系统建立在几个关键组件之上:
- ActionBase:所有动作的基类,定义了Name、Caption、ImageName等通用属性
- SimpleAction:最常用的具体实现,处理简单的命令执行
- PopupWindowShowAction:显示弹出窗口的特殊动作
- ActionContainer:管理动作分组和布局的容器
在Blazor前端,这些动作会被渲染为:
- 工具栏按钮(当放置在WindowTemplateController中)
- 列表视图的上下文菜单项(通过ListViewCommandAction)
- 详情视图的顶部命令区域(DetailViewCommandAction)
2.2 动作的生命周期
一个典型的SimpleAction执行流程:
- 框架初始化时扫描所有控制器,实例化声明的动作
- 根据当前视图类型和权限过滤可用动作
- 渲染为对应的UI元素(Blazor中通常是
<DxButton>) - 用户交互触发Execute事件
- 框架处理完前置条件后调用动作的ExecuteCore方法
- 执行开发者定义的回调逻辑
3. 实现步骤详解
3.1 基础实现方案
以下是为订单实体添加审核动作的完整示例:
csharp复制public class ApproveOrderController : ObjectViewController<DetailView, Order> {
public ApproveOrderController() {
// 初始化动作
ApproveAction = new SimpleAction(this, "ApproveOrder", PredefinedCategory.Edit) {
Caption = "审核通过",
ImageName = "Action_Approve",
ConfirmationMessage = "确定要审核通过此订单吗?",
ToolTip = "将订单状态变更为已审核"
};
// 设置执行条件
ApproveAction.SelectionDependencyType = SelectionDependencyType.RequireSingleObject;
ApproveAction.Execute += (s, e) => {
var order = View.CurrentObject as Order;
if (order != null) {
order.Status = OrderStatus.Approved;
order.ApprovedBy = SecuritySystem.CurrentUserName;
order.ApprovedDate = DateTime.Now;
View.ObjectSpace.CommitChanges();
}
};
}
public SimpleAction ApproveAction { get; }
}
关键参数说明:
PredefinedCategory.Edit:指定动作显示在编辑类操作区域SelectionDependencyType:控制动作可用状态与选择项的关联方式ConfirmationMessage:执行前的二次确认提示(Blazor中表现为模态对话框)
3.2 高级配置技巧
3.2.1 动态可用状态控制
通过重写OnActivated方法实现复杂条件判断:
csharp复制protected override void OnActivated() {
base.OnActivated();
ApproveAction.Enabled.SetItemValue(
"BusinessRule",
View.CurrentObject is Order o && o.Status == OrderStatus.Pending
);
}
3.2.2 异步动作实现
在v25.2+版本中推荐使用异步模式:
csharp复制ApproveAction.Execute += async (s, e) => {
await Task.Run(() => {
var order = (Order)View.CurrentObject;
// 模拟耗时操作
Thread.Sleep(1000);
order.Status = OrderStatus.Approved;
});
View.ObjectSpace.CommitChanges();
};
3.2.3 跨平台样式适配
在Blazor中自定义动作按钮样式:
csharp复制ApproveAction.CustomizeControl += (s, e) => {
if (e.Control is DxButton button) {
button.CssClass = "btn-success";
button.RenderStyle = ButtonRenderStyle.Primary;
}
};
4. 实战问题排查
4.1 动作不显示的常见原因
-
控制器未激活:
- 检查是否继承了正确的控制器基类
- 确认
TargetViewType和TargetObjectType是否匹配
-
权限问题:
- 在Model.DesignedDiffs.xafml中检查动作的Visibility属性
- 确认当前用户有对应业务对象的写权限
-
生命周期问题:
- 动态条件应在OnActivated中设置而非构造函数
- Blazor中注意状态保持与服务器端同步
4.2 性能优化建议
-
避免频繁启用/禁用切换:
csharp复制// 错误做法:每次属性变更都触发重绘 protected override void OnViewControlsCreated() { base.OnViewControlsCreated(); ApproveAction.Active["Rule"] = CheckCondition(); } // 正确做法:仅在条件实质变化时更新 private OrderStatus _lastStatus; protected override void OnActivated() { var order = (Order)View.CurrentObject; if (_lastStatus != order.Status) { ApproveAction.Active["Rule"] = order.Status == OrderStatus.Pending; _lastStatus = order.Status; } } -
批量操作优化:
对于列表视图中的动作,使用MyAction.SelectionDependencyType = SelectionDependencyType.RequireMultipleObjects配合并行处理:csharp复制Execute += (s, e) => { var orders = e.SelectedObjects.Cast<Order>(); Parallel.ForEach(orders, o => { o.Status = OrderStatus.Approved; }); };
5. 设计模式进阶
5.1 动作组合模式
实现复合动作的典型方案:
csharp复制public class CompoundActionController : ViewController {
public SimpleAction MainAction { get; }
public PopupWindowShowAction SubAction { get; }
public CompoundActionController() {
MainAction = new SimpleAction(/*...*/);
SubAction = new PopupWindowShowAction(/*...*/);
MainAction.Execute += (s, e) => {
if (SubAction.Enabled.ResultValue) {
SubAction.DoExecute(e.ShowViewParameters);
}
};
}
}
5.2 动态动作工厂
基于策略模式的动态动作生成:
csharp复制public interface IActionStrategy {
bool CanHandle(Type objectType);
SimpleAction CreateAction(Controller controller);
}
public class DynamicActionController : ViewController {
private readonly IEnumerable<IActionStrategy> _strategies;
public DynamicActionController(IEnumerable<IActionStrategy> strategies) {
_strategies = strategies;
}
protected override void OnActivated() {
foreach (var strategy in _strategies.Where(s => s.CanHandle(View.ObjectTypeInfo.Type))) {
var action = strategy.CreateAction(this);
Actions.Add(action);
}
}
}
6. 版本适配指南
6.1 v25.2.5新特性
-
Blazor性能增强:
- 动作渲染现在使用增量DOM更新
- 支持动作状态的服务器端推送通知
-
新的预定义动作:
csharp复制// 新增的图表相关动作 new SimpleAction { ImageName = "Actions_Chart", PaintStyle = ActionItemPaintStyle.Image }; -
无障碍支持:
csharp复制ApproveAction.AccessibilitySettings.Label = "审核订单"; ApproveAction.AccessibilitySettings.Hint = "将选定订单状态变更为已审核";
6.2 迁移注意事项
从旧版本升级时特别注意:
- 动作的ID生成规则变化(现在要求全局唯一)
- Blazor中动作的CSS类命名规范更新
- 异步执行上下文处理更加严格
我最近在将一个v23项目升级到v25.2.5时,发现原先直接修改DOM的动作自定义方式需要调整为使用新的Render API:
csharp复制// 旧方式(不再推荐)
action.CustomizeControl += (s, e) => {
if (e.Control is HtmlElement el) {
el.Style["color"] = "red";
}
};
// 新方式
action.CustomizeControl += (s, e) => {
if (e.Control is DxButton btn) {
btn.RenderStyle = ButtonRenderStyle.Danger;
}
};
