我有一个静态WindowService
类,它可以帮助我创建新窗口和模态对话框。
到目前为止,我所拥有的是:
/// <summary>
/// Opens a new window of type <paramref name="newWindowType"/> and closes the <paramref name="oldWindow"/>
/// </summary>
/// <param name="oldWindow">The window which should be closed (Usually the current open window)</param>
/// <param name="newWindowType">The type of the new window to open</param>
public static void ShowNewWindow(Window oldWindow, Type newWindowType)
{
((Window)Activator.CreateInstance(newWindowType)).Show();
oldWindow.Close();
}
我的viewmodel引发了一个事件,并且视图已订阅它。在视图中的事件处理程序中,它调用WindowService.ShowNewWindow(this,The type here)
。这很好。
我的模态对话框创建方法也将以类似的方式工作。唯一的区别是信息将返回到视图(在事件处理程序中),因此视图必须显式地将该信息传递给视图模型。这违反了mvvm模式,我不知道如何使视图模型在事件被提升后等待视图返回值。
有没有更好的方法呢?
答案 0 :(得分:3)
很多有关如何实现这一目标的不同变体,但这是我的两分钱。
此处的主要观点是确保您的View
和View Model
彼此不了解,因此您的View
不应订阅View Model
中的活动,您的View Model
不应直接致电您的服务并提供观看Type
。
我的建议是使用ICommand
实现而不是依赖静态服务类,因为您的类始终具有对此服务的依赖性,并且只要您发送视图{{ 1}}对于这个服务,那么MVVM模式就会丢失。
所以,首先,我们需要某种命令来打开给定Type
的窗口,这是我提出的:
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
这里要注意的一点是我使用<Button Content="Open Window"
Command="{Binding OpenWindowCommand}"
CommandParameter="{x:Type local:MyWindow}"/>
作为x:Type
,这是执行此命令时将创建的CommandParameter
。
我们上面所取得的只是一半的要求,我们现在需要显示对话框的内容并将结果输出到我们的Window
,这个并不是那么棘手,因为我们已经拥有了现有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
,以便创建这个新命令:
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
类充当ICommand
和View
之间的控制器,以确保它们彼此不了解,并保持强制执行MVVM模式。
就像我在这个答案的开头所说的那样,有很多方法可以实现这一目标,但我希望你现在有点开明。