如何从ViewModel中取消windowManager.ShowDialog()



我有一个ShellViewModel加载一个模态对话框。对话框的ViewModel有它的OnActivate()覆盖,它收集要显示在对话框上的数据。我想知道我们如何能要求WindowManager取消它的ShowDialog基于一个条件的OnActivate的ViewModel支持对话框。

例如,假设我在ShellViewModel中有以下代码,它试图加载基于StationOpenViewModel的模态对话框

public class ShellViewModel : Conductor<object>, IShell, IHandle<ConnectionChangedEvent> {
    public void ShowOpenStationPage() {
        StationOpenViewModel viewModel = container.GetExportedValue<StationOpenViewModel>();
        windowManager.ShowDialog(viewModel);
    }
    ...
}

,这里是OnActivate覆盖的代码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 override中得到Exception,我没有任何站点数据要显示,我想取消StationOpenViewModel的showDialog()。我尝试使用TryClose(),但如果我这样做,WindowManager.ShowDialog()抛出异常,说操作无效。

总之,如果我调用WindowManager.ShowDialog()的对话框支持一些ViewModel,那么在那个ViewModel我如何取消ShowDialog()操作

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();
}

完整的源代码在这里:

http://caliburnmicro.codeplex.com/SourceControl/changeset/view/ae25b519bf1e46a506c85395f04aaffb654c0a08 src/Caliburn.Micro.Silverlight WindowManager.cs

对于默认实现,似乎没有一个好的方法来做到这一点。你可能应该实现你自己的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;
        }
        ... 
 }

…当然,可能有更好的方法来检查你是否需要打开一个VM -我不确定他们会是什么,但仍然值得考虑

编辑:

我没有在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钩子,即使它们永远不应该被使用,可能会导致一些不希望的行为(不确定引用保持-这可能是ok的,因为没有任何东西保持对导体/VM的引用)

2:似乎激活VM的WindowConductor应该负责处理激活的取消-好吧,这确实意味着WindowManager需要知道是否显示VM,但它似乎更适合我

编辑2:

一个想法可能是将view.Show()移到导体中——这样您就可以取消激活,而无需向管理器公开详细信息。两者都是相互依赖的所以对我来说都是一样的

相关内容

  • 没有找到相关文章

最新更新