备份一下,下次懒得写了,MVVM模式,顺带一个按钮激活的使用例子。有更好的建议欢迎提出。
1.CommandBase.cs
cs
/// <summary>
/// 命令鸡肋(执行状态控制)
/// 空参 方法
/// </summary>
public class CommandBase : ICommand
{
// 命令状态变更事件
public event EventHandler CanExecuteChanged;
// 存储命令执行逻辑(有参版本)
private readonly Action<object> _executeWithParam;
// 存储执行条件判断逻辑
private readonly Func<object, bool> _canExecute;
/// <summary>
/// 构造函数:接收执行逻辑和执行条件
/// </summary>
public CommandBase(Action<object> execute, Func<object, bool> canExecute = null)
{
_executeWithParam = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
/// <summary>
/// 构造函数:接收无参执行逻辑和执行条件
/// </summary>
public CommandBase(Action execute, Func<object, bool> canExecute = null)
{
_executeWithParam = _ => execute();
_canExecute = canExecute;
if (execute == null)
throw new ArgumentNullException(nameof(execute));
}
/// <summary>
/// 判断命令是否可执行
/// </summary>
public bool CanExecute(object parameter)
{
// 如果有执行条件则使用条件判断,否则默认可执行
return _canExecute == null ? true : _canExecute(parameter);
}
/// <summary>
/// 执行命令逻辑
/// </summary>
public void Execute(object parameter)
{
if (CanExecute(parameter))
{
_executeWithParam(parameter);
}
}
/// <summary>
/// 手动触发CanExecuteChanged事件,强制 UI 更新状态。
/// </summary>
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
2.NotifyBase.cs
cs
public class NotifyBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// 属性改变通知方法
/// </summary>
/// <param name="propName"></param>
public void NotifyChanged([CallerMemberName] string propName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
3.按钮激活示例
在ViewModel中
cs
//数据表格的选中项SelectedItem数据源
private ConfProduct _selectedConfProduct;
public ConfProduct SelectedConfProduct
{
get => _selectedConfProduct;
set
{
if (value == null)
{
_selectedConfProduct = null;
NotifyChanged();
//激按钮状态控制
UpdateBtnCommand.RaiseCanExecuteChanged();
DeleteBtnCommand.RaiseCanExecuteChanged();
//Dgv2数据初始化
ResetDgv2ToInitialState();
return;
}
_selectedConfProduct = value;
NotifyChanged();
UpdateBtnCommand.RaiseCanExecuteChanged();
DeleteBtnCommand.RaiseCanExecuteChanged();
ShowExistingProductDetails(); // Dgv2加载选中项
}
}
#region 2. 命令声明
public CommandBase UpdateBtnCommand { get; }
public CommandBase DeleteBtnCommand { get; }
#endregion
#region 4. 构造函数(初始化命令,数据)
public ProductSettingViewModel()
{
AddBtnCommmand = new CommandBase(DoAdd);
UpdateBtnCommand = new CommandBase(DoUpdate, CanUpdateOrDelete);
DeleteBtnCommand = new CommandBase(DoDelete, CanUpdateOrDelete);
// 异步加载数据,避免UI阻塞
_ = LoadDataAsync();
}
#endregion
#region 按钮能否执行判断
// 按钮是否可执行(选中产品才可用)
private bool CanUpdateOrDelete(object parameter) => SelectedConfProduct != null; //选中项不为空,返回true
#endregion
激活和不激活的实例图片
未选中一行

选中一行

PS:添加我没有使用这套逻辑,默认一直处于能激活的状态。
4.解析
出自Ai的总结
结合CommandBase
命令基类和SelectedConfProduct
选中项属性,按钮激活 / 取消激活的逻辑形成了 **"条件绑定→状态判断→事件触发→UI 响应"** 的完整闭环,每个环节都有明确的代码支撑,具体拆解如下:
一、第一步:命令初始化 ------ 绑定 "执行条件" 到命令
在 ViewModel 的构造函数中,UpdateBtnCommand
和DeleteBtnCommand
初始化时,会将 "按钮可执行条件"(CanUpdateOrDelete
)通过CommandBase
的构造函数传入,完成 "条件与命令" 的绑定:
csharp
// ViewModel中命令初始化
UpdateBtnCommand = new CommandBase(DoUpdate, CanUpdateOrDelete);
DeleteBtnCommand = new CommandBase(DoDelete, CanUpdateOrDelete);
关键关联:CommandBase
构造函数的作用
CommandBase
的第二个构造函数(接收无参执行逻辑 + 执行条件)会将传入的CanUpdateOrDelete
(Func<object, bool>
类型)赋值给私有字段_canExecute
,为后续的 "可执行判断" 埋下依据:
csharp
// CommandBase构造函数(接收无参执行逻辑+执行条件)
public CommandBase(Action execute, Func<object, bool> canExecute = null)
{
_executeWithParam = _ => execute(); // 包装无参执行逻辑(如DoUpdate、DoDelete)
_canExecute = canExecute; // 保存执行条件(即ViewModel中的CanUpdateOrDelete)
if (execute == null)
throw new ArgumentNullException(nameof(execute));
}
二、第二步:可执行判断 ------CanExecute
方法决定按钮状态
Avalonia 的按钮在绑定命令后,会自动调用命令的CanExecute
方法 判断自身是否激活(可点击),这个判断逻辑由CommandBase
的CanExecute
方法实现,且直接依赖第一步绑定的_canExecute
(即CanUpdateOrDelete
):
1. CommandBase.CanExecute
的核心逻辑
csharp
public bool CanExecute(object parameter)
{
// 如果有执行条件(_canExecute不为null),则执行条件判断;否则默认"可执行"
return _canExecute == null ? true : _canExecute(parameter);
}
2. 最终的判断依据:CanUpdateOrDelete
ViewModel 中的CanUpdateOrDelete
是实际的 "条件判断函数",它直接关联SelectedConfProduct
的状态 ------只有选中项不为 null 时,才返回 true(按钮激活):
csharp
private bool CanUpdateOrDelete(object parameter) => SelectedConfProduct != null;
场景举例
- 当
SelectedConfProduct = null
(未选中 DGV1 行):CanUpdateOrDelete
返回false
→CommandBase.CanExecute
返回false
→按钮禁用(灰色,不可点击)。 - 当
SelectedConfProduct = 某产品实体
(选中 DGV1 行):CanUpdateOrDelete
返回true
→CommandBase.CanExecute
返回true
→按钮激活(正常颜色,可点击)。
三、第三步:状态更新 ------SelectedConfProduct
变化时触发事件
CanExecute
方法只会在按钮初始化或 "命令状态变更事件" 触发时重新执行。因此,当SelectedConfProduct
变化(选中 / 取消选中)时,必须通过CommandBase.RaiseCanExecuteChanged
手动触发事件,强制 UI 重新检查CanExecute
,更新按钮状态:
1. SelectedConfProduct
变化时的触发逻辑
在SelectedConfProduct
的set
访问器中,无论值是 "从 null 变为实体" 还是 "从实体变为 null",都会调用RaiseCanExecuteChanged
:
csharp
public ConfProduct SelectedConfProduct
{
get => _selectedConfProduct;
set
{
if (value == null)
{
_selectedConfProduct = null;
NotifyChanged();
// 关键:触发命令状态更新
UpdateBtnCommand.RaiseCanExecuteChanged();
DeleteBtnCommand.RaiseCanExecuteChanged();
ResetDgv2ToInitialState();
return;
}
_selectedConfProduct = value;
NotifyChanged();
// 关键:触发命令状态更新
UpdateBtnCommand.RaiseCanExecuteChanged();
DeleteBtnCommand.RaiseCanExecuteChanged();
ShowExistingProductDetails();
}
}
2. CommandBase.RaiseCanExecuteChanged
的作用
该方法会主动触发CommandBase
的CanExecuteChanged
事件(ICommand
接口的核心事件),而 Avalonia 的按钮会监听这个事件 ------ 事件触发时,按钮会立即重新调用CanExecute
方法,更新自身的激活状态:
csharp
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty); // 触发状态变更事件
}
四、第四步:UI 响应 ------ 按钮根据状态更新显示
Avalonia 的Button
控件在绑定Command
后,会自动完成以下逻辑,无需额外代码:
- 初始化时,调用
Command.CanExecute(null)
获取初始状态,设置按钮是否激活。 - 当
Command.CanExecuteChanged
事件触发时,再次调用Command.CanExecute(null)
,重新获取状态,并更新按钮的IsEnabled
属性(激活 / 禁用)。
总结:完整逻辑链路(以 "删除按钮" 为例)
操作场景 | 代码执行流程 | 按钮状态结果 |
---|---|---|
程序启动,未选中任何行 | 1. SelectedConfProduct = null →CanUpdateOrDelete 返回false ;2. 按钮初始化调用DeleteBtnCommand.CanExecute(null) →返回false ;3. 按钮禁用。 |
禁用 |
用户点击 DGV1 某行 | 1. SelectedConfProduct = 选中的产品实体 ;2. 调用DeleteBtnCommand.RaiseCanExecuteChanged() →触发CanExecuteChanged 事件;3. 按钮监听事件,调用CanExecute(null) →CanUpdateOrDelete 返回true ;4. 按钮激活。 |
激活 |
用户点击 DGV1 空白区域 | 1. SelectedConfProduct = null ;2. 调用DeleteBtnCommand.RaiseCanExecuteChanged() →触发事件;3. 按钮调用CanExecute(null) →返回false ;4. 按钮禁用。 |
禁用 |
整个逻辑完全遵循 MVVM 模式:数据(SelectedConfProduct)的变化驱动命令状态,命令状态驱动 UI(按钮)的显示 ,无需在代码后置中直接操作按钮的IsEnabled
属性,实现了 "数据与 UI 的解耦"。