我有一个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,但它似乎更适合我
一个想法可能是将view.Show()移到导体中——这样您就可以取消激活,而无需向管理器公开详细信息。两者都是相互依赖的所以对我来说都是一样的