5

我正在将 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 方法来解决这个问题?我当然想避免在视图模型中保留对窗口(或任何 UIElement)本身的引用。另请注意,这是许多无模式对话框的常见行为,因此最好以通用的可重用方式解决此问题。

Caliburn.Micro 是否已经内置了这个功能?

4

3 回答 3

5

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

这种方法对我有用。

于 2013-01-29T13:49:02.657 回答
4

无需实际实现 IViewAware 即可跟踪您的窗口的一种相当简单的方法是保留对您的 ViewModel 和 View 的弱引用的字典,然后检查您是否已经有一个现有的 Window。可以作为 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;
    }
}
于 2013-02-08T04:45:31.277 回答
1

我想出了这种扩展方法。它有效,但我对它并不特别满意,它仍然有些骇人听闻。

很明显,这个扩展必须对模型做出如此多的假设(你是否也看到了那些讨厌的例外?)。

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);
    }
}
于 2013-01-29T15:40:25.313 回答