2013-04-21 54 views
0

我有一個加載模式對話框的ShellViewModel。 Dialog的ViewModel具有OnActivate()覆蓋,它將收集要顯示在對話框上的數據。我想知道我們如何要求WindowManager根據支持該對話框的ViewModel的OnActivate中的條件取消它的ShowDialog。如何從ViewModel取消windowManager.ShowDialog()

例如,讓我們說,我有以下的ShellViewModel代碼試圖加載基於StationOpenViewModel

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

一個模態對話框,在這裏是的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覆蓋的代碼

因此,在上面的代碼中,如果我在OnActivate覆蓋中得到異常,我沒有任何站數據顯示,我想取消StationOpenViewModel的showDialog()。我嘗試使用TryClose(),但如果我這樣做,WindowManager.ShowDialog()拋出異常,說該操作無效。總之,如果我爲某個ViewModel支持的對話框調用WindowManager.ShowDialog(),那麼在該ViewModel中,我該如何取消ShowDialog()操作。

回答

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

完整的源代碼在這裏:

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

它看起來並不像有一個與默認實現做到這一點的好辦法。你或許應該實現自己的WindowManager,並繼承原執行

在上面的代碼文件的WindowConductor負責窗口的生命週期,因此和額外的接口,你的虛擬機可以實現將工作做好:

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添加停用和GuardClose掛鉤,即使他們不應該被使用,這可能導致一些不良的行爲(不知道基準保持或者 - 可能確定與此一次,因爲沒有任何持有裁判指揮/虛擬機)

2:它似乎像WindowConductor激活虛擬機應負責處理取消激活 - 確定這意味着WindowManager需要知道是否以顯示虛擬機或不虛擬,但它似乎更適合我自己

編輯2:

一個想法可能是移動view.Show()到導體 - 這樣你可以取消激活而無需暴露細節的經理。兩者都相互依賴,所以它對我來說都是一樣的

+0

看起來很合理,因爲我有很多Windows需要處理。在你的答案中實現你提供的WindowManager和Conductor似乎是很好的。但我仍然有疑問。爲什麼我們需要在這裏帶上導體?我的意思是爲什麼MyWindowManager不能直接從rootModel查詢ICancelActivate,並且自己取消創建對話框。我的意思是爲什麼我們需要通過售票員? – Jatin 2013-04-22 11:21:00

+0

我這樣做的原因是,指揮有管理窗口生命週期(佈線事件等)的工作,所以你將VM的激活傳遞給另一個組件。虛擬機進入這個黑匣子,你無法控制是否實際添加了deactivate和guardclose鉤子(儘管它永遠不會顯示)。它可能會在您的代碼中引入一個不明確的錯誤 - (不知道是什麼,但最好是安全的!) – Charleh 2013-04-22 11:27:23

+0

我想另一種方法是將view.Show()放入指示器,可能會稍微整理一下實現...它似乎是指揮應該負責顯示窗口 - 這是一個混合..經理是嚴重依賴於指揮 – Charleh 2013-04-22 11:39:06

相關問題