【发布时间】:2025-11-23 04:25:02
【问题描述】:
当我使用 ICommand 时,我正在寻找一种模式以在我的应用程序中保留 SOLID 原则。基本上我的问题是命令执行与视图模型有依赖关系,但同时视图模型与命令有依赖关系(我通过构造函数注入它们)。我想只保留带有属性的视图模型,所以这是我当前实现的一个示例:
public class MyViewModel : INotifyPropertyChanged
{
public ICommand MyCommand { get; private set; }
public string Message { get; set; } // PropertyChanged ommited
public MyViewModel()
{
}
public void SetCommand(ICommand myCommand)
{
this.MyCommand = myCommand;
}
....
}
internal interface IMyViewModelCommandManager
{
void ExectueMyCommand();
}
internal class MyViewModelCommandManager : IMyViewModelCommandManager
{
private readOnly MyViewModel myViewModel;
public MyViewModelCommandManager(MyViewModel myViewModel)
{
this.myViewModel = myViewModel;
}
public ExectueMyCommand()
{
MessageBox.Show(this.myViewModel.Message);
}
}
internal class MyViewModelFactory: IMyViewModelFactory
{
private readonly IContainerWrapper container;
public MyViewModelFactory(IContainerWrapper container)
{
this.container = container;
}
public MyViewModel Create()
{
MyViewModel viewModel = new MyViewModel();
IMyViewmodelCommandManager manager = this.container.Resolve<IMyViewmodelCommandManager>(new ResolverOverride[] { new ParameterOverride("viewModel", viewModel) });
ICommand myCommand = new DelegateCommand(manager.ExecuteMyCommand);
viewModel.SetCommand(myCommand);
return viewModel;
}
}
所以,要避免使用 SetCommand 方法。我想到了两种解决方案,但我不知道它们是否优雅。
第一种是将viewmodel依赖从构造函数移到更新代码的方法中,这样:
public class MyViewModel : INotifyPropertyChanged
{
public ICommand MyCommand { get; private set; }
public string Message { get; set; } // PropertyChanged ommited
public MyViewModel(ICommand myCommand)
{
this.MyCommand = myCommand;
}
....
}
internal interface IMyViewModelCommandManager
{
void ExectueMyCommand(MyViewModel viewModel);
}
internal class MyViewModelCommandManager : IMyViewModelCommandManager
{
public MyViewModelCommandManager()
{
....
}
public ExectueMyCommand(MyViewModel viewModel)
{
MessageBox.Show(myViewModel.Message);
}
}
internal class MyViewModelFactory: IMyViewModelFactory
{
private readonly IContainerWrapper container;
public MyViewModelFactory(IContainerWrapper container)
{
this.container = container;
}
public MyViewModel Create()
{
IMyViewmodelCommandManager manager = this.container.Resolve<IMyViewmodelCommandManager>(..);
ICommand myCommand = new DelegateCommand<MyViewModel>(manager.ExecuteMyCommand);
MyViewModel viewModel = new MyViewModel(myCommand);
return viewModel;
}
}
当然,xaml 代码会使用 CommandParameter:
<Button Content="Show Message" Command="{Binding MyCommand}" CommandParameter="{Binding .}" />
我想到的其他解决方案是使用一种技巧来创建 viewModel 的 Wrapper,并且 commandManager 依赖于 Wrapper 而不是 viewModel:
internal class MyViewModelCommandContext
{
public MyViewModel ViewModel { get; set; }
}
public class MyViewModel : INotifyPropertyChanged
{
public ICommand MyCommand { get; private set; }
public string Message { get; set; } // PropertyChanged ommited
public MyViewModel(ICommand myCommand)
{
this.MyCommand = myCommand;
}
....
}
internal interface IMyViewModelCommandManager
{
void ExectueMyCommand();
}
internal class MyViewModelCommandManager : IMyViewModelCommandManager
{
private readonly MyViewModelCommandContext context;
public MyViewModelCommandManager(MyViewModelCommandContext context)
{
this.context = context;
....
}
public ExectueMyCommand()
{
MessageBox.Show(this.context.myViewModel.Message);
}
}
internal class MyViewModelFactory: IMyViewModelFactory
{
private readonly IContainerWrapper container;
public MyViewModelFactory(IContainerWrapper container)
{
this.container = container;
}
public MyViewModel Create()
{
MyViewModelCommandContext context = new MyViewModelCommandContext();
IMyViewmodelCommandManager manager = this.container.Resolve<IMyViewmodelCommandManager>(new ResolverOverride[] { new ParameterOverride("context", context) });
ICommand myCommand = new DelegateCommand(manager.ExecuteMyCommand);
MyViewModel viewModel = new MyViewModel(myCommand);
context.ViewModel = viewModel;
return viewModel;
}
}
在我看来,第一个是这个问题的最佳解决方案,你认为最好的解决方案是什么。你会应用其他解决方案吗?
【问题讨论】:
-
将 command 属性留给
private set然后添加一个允许任何人设置属性的方法真的没有意义。您不妨将命令属性的set保留为public,这没有什么问题。话虽如此,您的ICommand非常通用,有人很容易在其位置注入一个完全出乎意料的命令。您最好使用RelayCommand(请参阅here)并将您的命令逻辑包含在视图模型中。 -
我有私有集,因为我的意图是通过构造函数注入命令,因为我理解命令是视图模型的依赖,视图模型不能知道命令逻辑。如果我将设置更改为公共,任何人也可以注入意外命令。在我看来不是视图模型的责任来保持逻辑。