Activate the output window using WindowManager

I am using WPF with the latest and most recent version of Caliburn.Micro (1.4.1). I am using IWindowManager.ShowWindow (...) to open a new model window:

private void OpenOrReactivateInfoView()
{
    if(this.infoViewModel == null)
    {
        this.infoViewModel = new InfoViewModel();
    }

    this.windowManager.ShowWindow(this.infoViewModel);
}

      

Instead of opening a new window every time it is called OpenOrReactivateInfoView()

, I would like to check if the entire window is open, and if so, the existing window should just restore focus.

That we will be a good Calibrun.Micro-way to solve this problem? I really would like to avoid referencing the window (or any UIElement for that matter) myself in the viewmodel. Also note that this is a common behavior for a lot of modeless dialogs, so it is recommended that you use this in a generic reusable mode.

Does Caliburn.Micro already have the means to do this built in?

+3


source to share


3 answers


A pretty easy way to keep track of your windows without implementing IViewAware would be to maintain a dictionary of weak references to your ViewModels and the views that go along, and then check if you already have an existing window or not. Can be implemented as either a decorator, WindowManager, subclass, or extension.

Something as simple as the following should do the trick of assuming that you're not actually planning on opening enough windows that even dead WeakReferences will affect performance. If it is going to be long, it shouldn't be that difficult to implement some kind of cleaning.



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

      

+4


source


The WindowManager source code always creates a new window, so you really need to use the WindowManager.ShowWindow method if you are actually going to create a new window.

The first thing you want to do is keep a permalink to your view model like this:

private readonly InfoViewModel infoViewModel = new InfoViewModel();
private void OpenOrReactivateInfoView()
{
    this.windowManager.ShowWindow(this.infoViewModel);
}

      

Then, in your viewmodel, create a Focus method or whatever you want:



public void Focus()
{
    var window = GetView() as Window;
    if (window != null) window.Activate();
}

      

Then go into the OpenOrReactivateInfoView () method, making a small adjustment like this:

private void OpenOrReactivateInfoView()
{
    if (!this.infoViewModel.IsActive)
        this.windowManager.ShowWindow(this.infoViewModel);
    else
        this.infoViewModel.Focus();
}

      

This method worked for me.

+2


source


I came up with this extension method. It works, but I'm not very happy with it, it's still somewhat hacky.

Obviously this extension has to make so many assumptions about the model (do you see these nasty exceptions too?).

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

      

+1


source







All Articles