我正在使用WPF和当前最新版本的Caliburn.Micro(1.4.1)。我使用IWindowManager.ShowWindow(...)打开一个新的无模式窗口:
private void OpenOrReactivateInfoView()
{
if(this.infoViewModel == null)
{
this.infoViewModel = new InfoViewModel();
}
this.windowManager.ShowWindow(this.infoViewModel);
}
每次调用OpenOrReactivateInfoView()
时,我都不想打开一个新窗口,而是想检查窗口是否仍然打开,如果是,现有窗口应该重新获得焦点。
我们将成为一名优秀的Calibrun.Micro方式来解决这个问题?我确实希望避免在viewmodel中保留对窗口(或任何UIElement)的引用。另请注意,这是许多无模式对话框的常见行为,因此最好以通用的可重用方式解决此问题。
Caliburn.Micro是否已经有内置的手段?
答案 0 :(得分:4)
一种非常直接的方法来实际跟踪您的窗口 必须实现IViewAware将保留弱引用的字典 到您的ViewModel和Views一起检查,然后检查您是否已经 是否有现有的窗口。可以作为装饰器来实现 WindowManager,子类或扩展。
如果你不这样做,那么下面这么简单应该做的伎俩 实际上计划打开足够的窗户甚至死亡的WeakReferences 会影响表现。如果它将长期运行它不应该 很难实现某种清理。
public class MyFancyWindowManager : WindowManager
{
IDictionary<WeakReference, WeakReference> windows = new Dictionary<WeakReference, WeakReference>();
public override void ShowWindow(object rootModel, object context = null, IDictionary<string, object> settings = null)
{
NavigationWindow navWindow = null;
if (Application.Current != null && Application.Current.MainWindow != null)
{
navWindow = Application.Current.MainWindow as NavigationWindow;
}
if (navWindow != null)
{
var window = CreatePage(rootModel, context, settings);
navWindow.Navigate(window);
}
else
{
var window = GetExistingWindow(rootModel);
if (window == null)
{
window = CreateWindow(rootModel, false, context, settings);
windows.Add(new WeakReference(rootModel), new WeakReference(window));
window.Show();
}
else
{
window.Focus();
}
}
}
protected virtual Window GetExistingWindow(object model)
{
if(!windows.Any(d => d.Key.IsAlive && d.Key.Target == model))
return null;
var existingWindow = windows.Single(d => d.Key.Target == model).Value;
return existingWindow.IsAlive ? existingWindow.Target as Window : null;
}
}
答案 1 :(得分:3)
WindowManager源代码总是创建一个新窗口,所以你真正想做的只是使用WindowManager.ShowWindow方法,如果你真的打算创建一个新窗口。
您要做的第一件事是对视图模型持久引用,如下所示:
private readonly InfoViewModel infoViewModel = new InfoViewModel();
private void OpenOrReactivateInfoView()
{
this.windowManager.ShowWindow(this.infoViewModel);
}
然后,在您的视图模型中,创建一个名为Focus的方法或任何您想要的方法:
public void Focus()
{
var window = GetView() as Window;
if (window != null) window.Activate();
}
然后重新访问你的OpenOrReactivateInfoView()方法做一个小小的调整:
private void OpenOrReactivateInfoView()
{
if (!this.infoViewModel.IsActive)
this.windowManager.ShowWindow(this.infoViewModel);
else
this.infoViewModel.Focus();
}
这种方法适合我。
答案 2 :(得分:1)
我想出了这种扩展方法。它有效,但我并不特别满意,它仍然有点hackish。
这显然是一个设计,这个扩展必须对模型做出如此多的假设(你还看到那些讨厌的例外吗?)。
using System;
using System.Collections.Generic;
using Caliburn.Micro;
public static class WindowManagerExtensions
{
/// <summary>
/// Shows a non-modal window for the specified model or refocuses the exsiting window.
/// </summary>
/// <remarks>
/// If the model is already associated with a view and the view is a window that window will just be refocused
/// and the parameter <paramref name="settings"/> is ignored.
/// </remarks>
public static void FocusOrShowWindow(this IWindowManager windowManager,
object model,
object context = null,
IDictionary<string, object> settings = null)
{
var activate = model as IActivate;
if (activate == null)
{
throw new ArgumentException(
string.Format("An instance of type {0} is required", typeof (IActivate)), "model");
}
var viewAware = model as IViewAware;
if (viewAware == null)
{
throw new ArgumentException(
string.Format("An instance of type {0} is required", typeof (IViewAware)), "model");
}
if (!activate.IsActive)
{
windowManager.ShowWindow(model, context, settings);
return;
}
var view = viewAware.GetView(context);
if (view == null)
{
throw new InvalidOperationException("View aware that is active must have an attached view.");
}
var focus = view.GetType().GetMethod("Focus");
if (focus == null)
{
throw new InvalidOperationException("Attached view requires to have a Focus method");
}
focus.Invoke(view, null);
}
}