使用WindowManager重新激活退出窗口

时间:2013-01-29 12:18:55

标签: wpf caliburn.micro window-management

我正在使用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是否已经有内置的手段?

3 个答案:

答案 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);
    }
}