【问题标题】:reactivate exiting window using WindowManager使用 WindowManager 重新激活退出窗口
【发布时间】:2013-01-29 12:18:54
【问题描述】:

我正在将 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 是否已经内置了这个功能?

【问题讨论】:

  • IsActive 属性不够用?
  • IsActive 不会在关联窗口失去焦点或被最小化并且Screen.Activate() 不会重新聚焦关联窗口时变为false

标签: wpf caliburn.micro window-management


【解决方案1】:

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

这个方法对我有用。

【讨论】:

  • 这也是我现在想出的。我不太喜欢它的是我的 ViewModel 必须是 IViewAware 并且需要持有对视图的引用。我也想让这个功能通用且可重用。
  • 我完全同意。当然,这不是最优雅的解决方案。我认为问题的症结在于 Caliburn.Micro 的 WindowManager 类。这里最优雅和可重用的解决方案是创建自己的 WindowManager 类来替换 Caliburn.Micro 的默认实现。
  • 或者适配/拦截WindowManager。
【解决方案2】:

一种相当简单的方法来跟踪您的窗口,而无需实际操作 必须实现 IViewAware 将保留弱引用字典 到你的 ViewModels 和 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;
    }
}

【讨论】:

    【解决方案3】:

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

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

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

    【讨论】:

      猜你喜欢
      • 2018-04-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-23
      • 1970-01-01
      相关资源
      最近更新 更多