啊,这个老栗子。
有many关于如何实现这一目标有不同的变体,但是这是我的两分钱。
这里的主要想法是确保您的View
and View Model
彼此不了解,因此你们的View
不应订阅您的事件View Model
, 和你的View Model
不应直接调用您的服务并提供视图Type
.
不要使用事件,而使用命令
我的建议是使用ICommand
实现而不是依赖于静态服务类,因为您的类将始终依赖于该服务,并且一旦您发送视图Type
到此服务,那么 MVVM 模式就会丢失。
因此,首先,我们需要某种命令来打开给定的窗口Type
,这是我想出的:
public class OpenWindowCommand : ICommand
{
public bool CanExecute(object parameter)
{
TypeInfo p = (TypeInfo)parameter;
return p.BaseType == typeof(Window);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
if (parameter == null)
throw new ArgumentNullException("TargetWindowType");
//Get the type.
TypeInfo p = (TypeInfo)parameter;
Type t = p.BaseType;
if (p.BaseType != typeof(Window))
throw new InvalidOperationException("parameter is not a Window type");
//Create the window.
Window wnd = Activator.CreateInstance(t) as Window;
OpenWindow(wnd);
}
protected virtual void OpenWindow(Window wnd)
{
wnd.Show();
}
}
该类继承自ICommand
并指定接受的实现Type
,它代表期望的View
我们想要打开的。请注意,我已将方法标记为virtual
,我稍后会解释这部分。
下面是我们如何在我们的程序中使用这个命令View Model
:
public class MainWindowViewModel
{
public OpenWindowCommand OpenWindowCommand { get; private set; }
public MainWindowViewModel()
{
OpenWindowCommand = new OpenWindowCommand();
}
...
}
现在我们已经创建了命令,我们只需要绑定一个Button
to it:
<Button Content="Open Window"
Command="{Binding OpenWindowCommand}"
CommandParameter="{x:Type local:MyWindow}"/>
这里要注意的一件事是我正在使用x:Type
as the CommandParameter
,这是Window
执行此命令时将创建该文件。
但是对话框呢?
我们上面所取得的成果只是half根据要求,我们现在需要一些能够显示dialog并将结果输出到我们的View Model
,这并不那么棘手,因为我们现有的大部分内容已经有了OpenWindowCommand
.
首先,我们需要创建命令:
public class ShowDialogCommand : OpenWindowCommand
{
private Action _PreOpenDialogAction;
private Action<bool?> _PostOpenDialogAction;
public ShowDialogCommand(Action<bool?> postDialogAction)
{
if (postDialogAction == null)
throw new ArgumentNullException("postDialogAction");
_PostOpenDialogAction = postDialogAction;
}
public ShowDialogCommand(Action<bool?> postDialogAction, Action preDialogAction)
: this(postDialogAction)
{
if (preDialogAction == null)
throw new ArgumentNullException("preDialogAction");
_PreOpenDialogAction = preDialogAction;
}
protected override void OpenWindow(System.Windows.Window wnd)
{
//If there is a pre dialog action then invoke that.
if (_PreOpenDialogAction != null)
_PreOpenDialogAction();
//Show the dialog
bool? result = wnd.ShowDialog();
//Invoke the post open dialog action.
_PostOpenDialogAction(result);
}
}
我们正在利用我们的OpenWindowCommand
通过继承它并使用它的实现,而不必将其全部复制到我们的新类中。该命令需要一个Action
这是对你的方法的引用View Model
,您可以选择定义一个动作before or after(或两者)显示一个对话框。
下一步是改变我们的View Model
所以它创建了这个新命令:
public class MainWindowViewModel
{
public OpenWindowCommand OpenWindowCommand { get; private set; }
public ShowDialogCommand ShowDialogCommand { get; private set; }
public MainWindowViewModel()
{
OpenWindowCommand = new OpenWindowCommand();
ShowDialogCommand = new ShowDialogCommand(PostOpenDialog);
}
public void PreOpenDialog()
{
throw new NotImplementedException();
}
public void PostOpenDialog(bool? dialogResult)
{
throw new NotImplementedException();
}
}
该命令的用法实际上与以前相同,但它只是引用了不同的命令:
<Button Content="Open Window"
Command="{Binding ShowDialogCommand}"
CommandParameter="{x:Type local:MyWindow}"/>
就这样,一切都是松散耦合的,这里唯一真正的依赖关系是你的View Model
取决于你的ICommand
类。
最后几句话
The ICommand
我创建的类充当控制器之间的控制器View
和View Model
确保它们彼此不了解,并强制执行 MVVM 模式。
就像我在这个答案开始时所说的那样,有很多方法可以实现这一点,但是我希望您现在更加开明一点。