C# 对多个视图使用相同的命令
我有两个相同的视图C# 对多个视图使用相同的命令,c#,wpf,mvvm,C#,Wpf,Mvvm,我有两个相同的视图View1.xaml和View2.xaml,它们都有一个按钮button1和一个文本字段textfield1。这个想法是,当您按下按钮时,相应的文本字段将填充一些信息。两个视图都使用相同的方法填充文本字段(从这个意义上说,两个视图完全相同) 我的问题是:如何使用OOP原则编写通用代码而不破坏MVVM模式?我当前使用RelayCommand执行此操作的方式: ViewModel1和ViewModel2的代码相同: public RelayCommand ButtonCommand
View1.xaml
和View2.xaml
,它们都有一个按钮button1
和一个文本字段textfield1
。这个想法是,当您按下按钮时,相应的文本字段将填充一些信息。两个视图都使用相同的方法填充文本字段(从这个意义上说,两个视图完全相同)
我的问题是:如何使用OOP原则编写通用代码而不破坏MVVM模式?我当前使用RelayCommand执行此操作的方式:
ViewModel1
和ViewModel2
的代码相同:
public RelayCommand ButtonCommand { get; private set; }
#Constructor
ButtonCommand = new RelayCommand(ExecuteButtonCommand, CanExecuteButtonCommand);
#EndConstructor
private void ExecuteButtonCommand(object message)
{
//Some method to fill the corresponding textfield
}
private bool CanExecuteButtonCommand(object message)
{
return true;
}
为View1.xaml
和View2.xaml
中的按钮绑定:
<Button Command="{Binding Path=ButtonCommand, Mode=OneWay}" />
这很糟糕,因为我必须为两个ViewModel编写相同的代码。我试图创建一个继承自RelayCommand的类
ButtonCommand
,但由于并非每个视图都具有此功能,因此我无法使用此方法实现它。这可能是一种方法:
1-创建基本viewmodel类:
public class YourBaseViewModel
{
public Object YourBaseProperty{get; set;}
public RelayCommand ButtonCommand { get; private set; }
private void ExecuteButtonCommand(object message)
{
//Some method to fill the corresponding textfield
}
private bool CanExecuteButtonCommand(object message)
{
return true;
}
}
2-从基础viewmodel继承:
public class ViewModel1:YourBaseViewModel
{
// ....
}
public class ViewModel2:YourBaseViewModel
{
// ....
}
编辑:
如果您有另一个基类,则可以执行以下操作:
public class YourBaseViewModel:YourReallyBaseViewModel
{
// ....
}
public class ViewModel1:YourBaseViewModel
{
// ....
}
public class ViewModel2:YourBaseViewModel
{
// ....
}
与其有一个“基本”视图模型和两个派生视图模型,不如让您的两个视图模型都使用在别处定义的相同代码(理想情况下,都调用相同的接口,并注入依赖项注入)
这是组合优先于继承的原则
在编写测试时,请测试两个视图模型是否都调用该接口,并测试该接口的实现是否执行了一次应该执行的操作
这样,不仅可以避免编写代码两次,还可以避免测试代码两次,还可以遵循其他原则,如单一责任原则。这是一个XY问题。您正在寻求一种解决Y的方法(不是复制相同的
按钮命令,而是实际上),您的问题是X(您的代码中已经存在重复)
我有两个相同的视图View1.xaml和View2.xaml
我想补充一点,你也说过你并不是只有两个相同的视图,还有更多
解决此问题的最佳方法是使用可以构造子视图模型的父视图模型
因此,首先,我们需要一个用于子视图模型的接口
IMyViewModel
其次是实施
MyViewModel
最后是负责创建视图模型的ParentViewModel
。请注意,我没有告诉它什么时候创建ViewModels,我将留给您
父视图模型
公共类ParentViewModel:ViewModelBase
{
私人Func_myVmCreator;
公共ParentViewModel(Func myVmCreator)
{
_friendEditVmCreator=friendEditVmCreator;
}
公共ObservableCollection MyViewModels{get;private set;}
私有IMyViewModel CreateAndLoadMyViewModel()
{
var myVm=_myVmCreator();
添加(myVm);
myVm.Load();
返回myVm;
}
}
这将允许您创建任意数量的MyViewModels
,或任何其他类型的ViewModel,只要它实现了IMyViewModel
上述示例源自本课程:
我强烈推荐。将文本框的Text属性绑定到同一个ViewModel(您可以为具有此属性的ViewModels创建基类)属性。为什么不能对两个视图使用完全相同的视图模型类?不能对两个不同的视图使用相同的
ViewModel
,因为您破坏了模式,而且它具有“可读性”。如果你有MyView
,按照惯例,它的虚拟机是MyViewModel
而不是MyOtherViewModel
@Babbillumpa我已经有了ViewModelBase
,问题是不是每个ViewModel
都有这个功能,因此,我只能执行另一个基类,该基类将继承自ViewModelBase
,然后由这些ViewModels
@karolyz进行扩展,是什么阻止了您这样做?不幸的是,这不会起作用,因为如果我有view3.xaml
,那么,它具有与新的命令关联的按钮
和文本字段
,并在现有的视图2中添加相同的按钮
和文本字段
,然后视图2.xaml必须扩展两个基类,依此类推。换句话说,如果view2
具有view1
和view3
的功能,它将不起作用,但是view1
和view3
将完全不同。@karolyz和“new command”您的意思是相同的命令但不同的执行操作?Exmaple:view1
的按钮负责生成一个随机数,view3
的按钮负责生成一个随机字符串。现在,如果view2
有两个按钮,一个用于生成字符串,一个用于数字,这将不再起作用,因为view2现在必须有另一个类来扩展这两个功能。如果我理解正确,你是说有一个包含该方法的命令及其注入的接口?然后viewmodels会简单地调用接口?实际的命令是视图模型的属性,因此它们可以绑定到。为命令执行的代码只能调用另一个类上的方法。两个视图模型都可以使用该类而不共享基类。是的,这可能是在这种情况下的方法。回到这个答案,这可能是我读过的堆栈溢出中最重要的一个答案。谢谢,它改变了我看待软件开发的方式,让我学到了各种新概念。这无疑也是这个问题的最佳答案,这是我当时一直在寻找的。谢谢你这么广泛的回答,我很高兴
public interface IMyViewModel
{
void Load();
}
public class MyViewModel : ViewModelBase, IMyViewModel
{
public MainViewModel()
{
ButtonCommand = new RelayCommand(ExecuteButtonCommand, CanExecuteButtonCommand);
}
public RelayCommand ButtonCommand { get; private set; }
public void Load()
{
//Example load logic
InvalidateCommands();
}
private void InvalidateCommands()
{
ButtonCommand.RaiseCanExecuteChanged();
}
private void ExecuteButtonCommand(object message)
{
//Some method to fill the corresponding textfield
}
private bool CanExecuteButtonCommand(object message)
{
return true;
}
}
public class ParentViewModel : ViewModelBase
{
private Func<IMyViewModel> _myVmCreator;
public ParentViewModel(Func<IMyViewModel> myVmCreator)
{
_friendEditVmCreator = friendEditVmCreator;
}
public ObservableCollection<IMyViewModel> MyViewModels { get; private set; }
private IMyViewModel CreateAndLoadMyViewModel()
{
var myVm = _myVmCreator();
MyViewModels.Add(myVm);
myVm.Load();
return myVm;
}
}