我有一个ShellViewModel,可以加载模态对话框。 Dialog的ViewModel具有OnActivate()覆盖,它收集要在Dialog上显示的数据。我想知道如何根据支持对话框的ViewModel的OnActivate中的条件要求WindowManager取消其ShowDialog。
例如,假设我在ShellViewModel中有以下代码,它试图加载基于StationOpenViewModel的模式对话框
public class ShellViewModel : Conductor<object>, IShell, IHandle<ConnectionChangedEvent> {
public void ShowOpenStationPage() {
StationOpenViewModel viewModel = container.GetExportedValue<StationOpenViewModel>();
windowManager.ShowDialog(viewModel);
}
...
}
以下是OnOctivate覆盖StationOpenViewModel
的代码public class StationOpenViewModel : Screen {
...
protected override void OnActivate() {
try {
using (StationRepository stationRepository = new StationRepository()) {
//code to get Station Data
}
catch (Exception ex) {
//Here I have no data, so there is no point in showing the window.
//How to cancel showDialog() for this viewModel
}
...
}
所以在上面的代码中,如果我在OnActivate覆盖中得到Exception,我没有要显示的任何Station数据,我想取消StationOpenViewModel的showDialog()。我尝试使用TryClose(),但如果我这样做,WindowManager.ShowDialog()会抛出异常,说操作无效。
总之,如果我为一些ViewModel支持的对话框调用WindowManager.ShowDialog(),那么在该ViewModel中如何取消ShowDialog()操作。
答案 0 :(得分:1)
CM源中的ShowDialog()
实现是:
public virtual void ShowDialog(object rootModel, object context = null, IDictionary<string, object> settings = null)
{
var view = EnsureWindow(rootModel, ViewLocator.LocateForModel(rootModel, null, context));
ViewModelBinder.Bind(rootModel, view, context);
var haveDisplayName = rootModel as IHaveDisplayName;
if(haveDisplayName != null && !ConventionManager.HasBinding(view, ChildWindow.TitleProperty)) {
var binding = new Binding("DisplayName") { Mode = BindingMode.TwoWay };
view.SetBinding(ChildWindow.TitleProperty, binding);
}
ApplySettings(view, settings);
new WindowConductor(rootModel, view);
view.Show();
}
完整来源:
使用默认实现看起来不是很好的方法。您应该实现自己的WindowManager
并继承原始实现
上面代码文件中的WindowConductor
负责窗口的生命周期,因此VM可以实现的其他接口也能正常运行:
public interface ICancelActivate
{
public bool ActivationCancelled { get };
}
然后将您的MyWindowConductor
实施更改为:
public MyWindowConductor(object model, ChildWindow view)
{
// Added this field so the window manager can query the state of activation (or use a prop if you like)
public bool ActivationCancelled;
this.model = model;
this.view = view;
var activatable = model as IActivate;
if (activatable != null)
{
activatable.Activate();
}
// Added code here, check to see if the activation was cancelled:
var cancelActivate = model as ICancelActivate;
if(cancelActivate != null)
{
ActivationCancelled = cancelActivate.ActivationCancelled;
if(ActivationCancelled) return; // Don't bother handling the rest of activation logic if cancelled
}
var deactivatable = model as IDeactivate;
if (deactivatable != null) {
view.Closed += Closed;
deactivatable.Deactivated += Deactivated;
}
var guard = model as IGuardClose;
if (guard != null) {
view.Closing += Closing;
}
}
然后停止显示视图:
// This is in 'ShowDialog' - you can override the default impl. as the method is marked virtual
ApplySettings(view, settings);
// Get a ref to the conductor so you can check if activation was cancelled
var conductor = new MyWindowConductor(rootModel, view);
// Check and don't show if we don't need to
if(!conductor.ActivationCancelled)
view.Show();
显然我只是把它扔在一起所以它可能不是最好的方式,我会仔细看看这会留下你的申请状态
您的虚拟机只是实现了这个:
public class StationOpenViewModel : Screen, ICancelActivation {
private bool _activationCancelled;
public bool ActivationCancelled { get { return _activationCancelled; } }
...
protected override void OnActivate() {
try {
using (StationRepository stationRepository = new StationRepository()) {
//code to get Station Data
}
catch (Exception ex) {
_activationCancelled = true;
}
...
}
...当然,你可能有更好的方法来检查你是否需要首先打开一个虚拟机 - 我不确定它们会是什么,但仍然值得考虑
修改:
我之所以不在WindowManager
...
new WindowConductor(rootModel, view);
var cancel = rootModel as ICancelActivation;
if(cancel == null || !cancel.ActivationCancelled) // fixed the bug here!
view.Show();
是双重的 - 1:您仍然允许WindowConductor
添加Deactivate和GuardClose挂钩,即使它们永远不会被使用,可能会导致某些不良行为(不确定参考持有其中任何一个 - 这可能是好的,因为没有任何东西对导体/ VM有任何反对意见
2:似乎激活VM的WindowConductor应该负责处理激活的取消 - 好吧它确实意味着WindowManager
需要知道是否显示VM,但它似乎是更自然适合我
编辑2:
一个想法可能是将view.Show()移动到指挥中 - 这样您就可以取消激活而无需向管理器公开详细信息。两者都相互依赖,但这对我来说都是一样的