You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF遵循DRY原则优化RelayCommand重复代码及实现NavigateToCommand的方法

优化实现方案

下面提供两种可落地的优化方式,都可以完全消除重复的RelayCommand定义逻辑。

方案1:使用带参数的通用导航命令(改动最小,无额外依赖)

第一步:修改MainViewModel代码

仅保留1个通用导航命令,通过命令参数区分要跳转的目标视图模型:

public class MainViewModel : BaseViewModel
{
    // 仅需定义一个通用导航命令
    public RelayCommand NavigateCommand { get; set; }

    private BaseViewModel _currentViewModel;
    public BaseViewModel CurrentViewModel
    {
        get => _currentViewModel;
        set
        {
            _currentViewModel = value;
            OnPropertyChanged(nameof(CurrentViewModel)); // 替换硬编码字符串,避免拼写错误
        }
    }
    public MainViewModel()
    {
        CurrentViewModel = new HomeViewModel();
        // 统一导航逻辑:根据传入参数创建对应ViewModel
        NavigateCommand = new RelayCommand(o =>
        {
            CurrentViewModel = o switch
            {
                "Home" => new HomeViewModel(),
                "Custody" => new CustodyViewModel(),
                "Operations" => new OperationsViewModel(),
                _ => CurrentViewModel
            };
        });
    }
}

第二步:修改XAML绑定,新增CommandParameter

每个按钮指定对应参数即可复用同一个命令:

<Button Content="Home"
        Command="{Binding NavigateCommand}"
        CommandParameter="Home"/>
<Button Content="Custody"
        Command="{Binding NavigateCommand}"
        CommandParameter="Custody"/>
<Button Content="Operations"
        Command="{Binding NavigateCommand}"
        CommandParameter="Operations"/>

该方案适合中小项目快速优化,完全消除了重复的Command定义和初始化逻辑。

方案2:进阶版本(结合类型参数+ViewModel工厂,扩展性更强)

如果后续视图模型数量较多,用字符串匹配容易出错,可以进一步优化为按类型导航,配合工厂模式完全符合开闭原则:

第一步:实现ViewModel工厂

public static class ViewModelFactory
{
    public static BaseViewModel CreateViewModel(Type vmType)
    {
        return Activator.CreateInstance(vmType) as BaseViewModel;
    }
    // 如果使用依赖注入容器,可修改为从容器获取实例,避免每次新建对象:
    // public static BaseViewModel CreateViewModel(Type vmType) => App.ServiceProvider.GetService(vmType) as BaseViewModel;
}

第二步:修改MainViewModel导航逻辑

public class MainViewModel : BaseViewModel
{
    public RelayCommand NavigateCommand { get; set; }
    private BaseViewModel _currentViewModel;
    public BaseViewModel CurrentViewModel
    {
        get => _currentViewModel;
        set { _currentViewModel = value; OnPropertyChanged(nameof(CurrentViewModel)); }
    }
    public MainViewModel()
    {
        CurrentViewModel = ViewModelFactory.CreateViewModel(typeof(HomeViewModel));
        NavigateCommand = new RelayCommand(o =>
        {
            if (o is Type vmType && typeof(BaseViewModel).IsAssignableFrom(vmType))
            {
                CurrentViewModel = ViewModelFactory.CreateViewModel(vmType);
            }
        });
    }
}

第三步:修改XAML,传类型作为参数

<!-- 先在XAML头部引入ViewModel所在的命名空间,示例如下 -->
<Window xmlns:vm="clr-namespace:你的项目名.ViewModels">
    <Button Content="Home"
            Command="{Binding NavigateCommand}"
            CommandParameter="{x:Type vm:HomeViewModel}"/>
    <Button Content="Custody"
            Command="{Binding NavigateCommand}"
            CommandParameter="{x:Type vm:CustodyViewModel}"/>
    <Button Content="Operations"
            Command="{Binding NavigateCommand}"
            CommandParameter="{x:Type vm:OperationsViewModel}"/>
</Window>

该方案后续新增视图时只需新增对应按钮,无需修改ViewModel中的导航逻辑,维护成本更低。

内容的提问来源于stack exchange,提问作者WindCheck

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 17:57:05