Mike Taulty's Blog
Bits and Bytes from Microsoft UK
Silverlight 5 Beta Rough Notes–Multiple Window Support

Blogs

Mike Taulty's Blog

Elsewhere

Archives

Note: these are early notes based on some initial experiments with the Silverlight 5 beta, apply a pinch of salt to what you read.

Back in the old days I think we used to called this Multiple Document Interface or MDI and the need for business applications for multiple top level windows is a very strong one - the Office applications do it and I’ve certainly encountered many developers who find that their users want their Silverlight applications to be able to open up in multiple windows.

This is present in the Silverlight 5 beta but only for elevated trust applications which, largely, means “out of browser” applications although I’ll come back and talk about why I say “largely” in a follow on blog post about elevated in-browser applications which are also new in Silverlight 5.

The first thing that you’ll notice is that your Application instance has a new property hanging off it called Windows which is of type WindowsCollection. You will not be able to get to this property unless you are out-of-browser (much like Application.MainWindow which was there in Silverlight 4).

But, if you are out-of-browser then you can get to the Windows of the application and enumerate them as;

      foreach (Window window in Application.Current.Windows)
      {
        
      }
as you’ll notice – this is returning Window instances to me and Window is a class that already exists in Silverlight 4 but I think it gets some new properties here like;
  • Title (note – in the current bits, this seems to throw a NotImplementedException from its getter so you can set it up but you don’t seem to be able to query it at a later point)
  • Content (of type FrameworkElement)
  • IsVisible/Visibility
  • WindowStyle (None, SingleBorder, BorderlessRoundCornersWindow although I found that the last one seems to throw an exception in the beta)

and there’s an additional new static method called GetWindow which takes a DependencyObject and returns the Window that represents the parent of that object which will probably facilitate some scenarios where you have some general purpose routine and needs to figure out where some “UI element” lives in terms of the multiple windows in the application.

As an aside, I’ve seen a number of scenarios in Silverlight where developers use Application.Current.RootVisual as a way of finding the root of the visual tree – this kind of code might need revisiting in a Silverlight application that has content in multiple windows because the RootVisual does not necessarily provide a “root” for content living in a separate window.

When you want to create a Window, you new one up and that’s pretty much it. I wrote a simple app that displays multiple windows;

image

where there are 3 child windows displaying images that can then be opened in their own separate windows ensuring that only one child window is created for a particular image and that the state of those images is preserved across invocations of the app.

I wrote a little “service” to manage that activity;

namespace SilverlightApplication7
{
  using System;
  using System.Collections.Generic;
  using System.IO.IsolatedStorage;
  using System.Linq;
  using System.Windows;

  public static class WindowManagementService
  {
    public static event EventHandler WindowCreated;
    public static event EventHandler WindowClosed;

    static Dictionary<string, Window> windows;

    static WindowManagementService()
    {
      windows = new Dictionary<string, Window>();
    }
    public static int ChildWindowCount
    {
      get
      {
        return (windows.Count);  
      }
    }
    public static void CreateOrActivateWindow(string key,
  Func<string, FrameworkElement> contentFactory)
    {
      Window window = FindWindowByKey(key);

      if (window == null)
      {
        window = CreateWindow(key, contentFactory);
      }
      else
      {
        window.Visibility = Visibility.Visible;
        window.WindowState = WindowState.Normal;
        window.Activate();
      }
    }
    public static void MinimiseAll()
    {
      foreach (var item in windows.Values)
      {
        item.WindowState = WindowState.Minimized;
      }
    }
    public static void RestoreAll()
    {
      foreach (var item in windows.Values)
      {
        item.WindowState = WindowState.Normal;
      }
    }
    public static void SaveMainWindowInfo(string persistenceKey)
    {
      IsolatedStorageSettings.ApplicationSettings[persistenceKey] =
        WindowPersistInfo.FromWindow(Application.Current.MainWindow);
      IsolatedStorageSettings.ApplicationSettings.Save();
    }
    public static void LoadMainWindowInfo(string persistenceKey)
    {
      try
      {
        WindowPersistInfo info = 
          (WindowPersistInfo)IsolatedStorageSettings.ApplicationSettings[persistenceKey];

        info.ApplyToWindow(Application.Current.MainWindow);
      }
      catch (KeyNotFoundException)
      {
      }
    }
    public static void LoadChildWindowInfo(string persistenceKey,
      Func<string, FrameworkElement> contentFactory)
    {
      try
      {
        List<WindowPersistInfo> storedInfo =
          (List<WindowPersistInfo>)IsolatedStorageSettings.ApplicationSettings[
            persistenceKey];

        foreach (var item in storedInfo)
        {
          CreateWindow(item, contentFactory);
        }
      }
      catch (KeyNotFoundException)
      {
      }
    }
    public static void SaveChildWindowInfo(string persistenceKey)
    {
      var info = windows.Select(
        entry => WindowPersistInfo.FromWindow(entry.Key, entry.Value)).ToList();

      IsolatedStorageSettings.ApplicationSettings[persistenceKey] = info;
      IsolatedStorageSettings.ApplicationSettings.Save();
    }
    static Window FindWindowByKey(string uri)
    {
      Window window;

      windows.TryGetValue(uri, out window);

      return (window);
    }

    static Window CreateWindow(WindowPersistInfo persistInfo,
      Func<string, FrameworkElement> contentFactory)
    {
      Window window = new Window()
      {
        Title = persistInfo.Key,
        WindowStyle = WindowStyle.SingleBorderWindow,
        Width = persistInfo.Width,
        Height = persistInfo.Height,
        Top = persistInfo.Top,
        Left = persistInfo.Left,
        Visibility = Visibility.Visible,
        Content = contentFactory(persistInfo.Key)
      };     
      window.Closing += (s, e) =>
      {
        windows.Remove(persistInfo.Key);

        if (WindowClosed != null)
        {
          WindowClosed(null, EventArgs.Empty);
        }
      };
      windows[persistInfo.Key] = window;

      if (WindowCreated != null)
      {
        WindowCreated(null, EventArgs.Empty);
      }
      return window;
    }
    static Window CreateWindow(string key,
      Func<string, FrameworkElement> contentFactory)
    {
      return (CreateWindow(
        new WindowPersistInfo()
        {
          Key = key,
          Width = 3 * 96,
          Height = 2 * 96,
          Left = 96,
          Top = 96
        },
        contentFactory));
    }
  }
}

this just manages a list of child windows and associates a key (of type string) with each one so that they can be identified again at a later point. Perhaps the only slightly obscure thing is methods like LoadChildWindowInfo which does something like;

  1. Reach into isolated storage to try and find a persisted List<WindowPersistInfo>
  2. Re-create each window that it finds in that list
  3. Invoke a lambda passed as a parameter to determine what content to put into that window as it’s created

where the WindowPersistInfo is just a simple class to help out with storing the details about each window;

namespace SilverlightApplication7
{
  using System.Runtime.Serialization;
  using System.Windows;

  [DataContract]
  public class WindowPersistInfo
  {
    [DataMember]
    public string Key { get; set; }
    [DataMember]
    public double Left { get; set; }
    [DataMember]
    public double Top { get; set; }
    [DataMember]
    public double Width { get; set; }
    [DataMember]
    public double Height { get; set; }

    public void ApplyToWindow(Window window)
    {
      window.Top = this.Top;
      window.Left = this.Left;
      window.Width = this.Width;
      window.Height = this.Height;
    }
    public static WindowPersistInfo FromWindow(Window window)
    {
      return (FromWindow(null, window));
    }
    public static WindowPersistInfo FromWindow(string key, Window window)
    {
      return (new WindowPersistInfo()
      {
        Key = key,
        Top = window.Top,
        Left = window.Left,
        Width = window.Width,
        Height = window.Height
      });
    }
  }
}
and I wrote a couple of simple views and view models around some of this and built it up into a simple app – you can download the sourcecode from here.

One other thing that I did notice was that my child windows seemed to resize their content ok but with an added black border around the right and bottom of the content – not sure if that’s a bug in the beta.


Posted Mon, Apr 18 2011 9:05 AM by mtaulty
Filed under: , ,

Comments

Matt Davey wrote re: Silverlight 5 Beta Rough Notes–Multiple Window Support
on Mon, Apr 18 2011 12:25 PM

Nice, but I want it from non OOB as well

Tom wrote re: Silverlight 5 Beta Rough Notes–Multiple Window Support
on Mon, Apr 18 2011 2:25 PM

MDI is actually multiple child windows inside one top level window.  It's largely considered obsolete and Office has tried to stamp it out, for the most part.

en.wikipedia.org/.../Multiple_document_interface

Anonymous wrote re: Silverlight 5 Beta Rough Notes–Multiple Window Support
on Mon, Apr 18 2011 4:08 PM

Hi Mike

Any idea about how to drag an element off the main app into a new window?

Thanks !!

mtaulty wrote re: Silverlight 5 Beta Rough Notes–Multiple Window Support
on Tue, Apr 19 2011 2:02 PM

Tom,

Yes, you're right about MDI :-)

On the drag-drop question - Silverlight isn't a drag source if I remember correctly so you won't be able to really do this via the OS unless you can come up with a COM (or in the future PInvoke) route to doing it.

Perhaps another metaphor would work better?

Mike.

Steve Ruben wrote re: Silverlight 5 Beta Rough Notes–Multiple Window Support
on Fri, Apr 22 2011 2:06 AM

Let us all know if you find out about that black border around the window it really makes things ugly.

Anonymous wrote re: Silverlight 5 Beta Rough Notes–Multiple Window Support
on Tue, May 3 2011 2:17 PM

Hi Mike

Back to the drag-drop question, wasn't this something that was shown in the Firestarter demo (almost exactly 1hr in from keynote)?

Thanks !!

online mafia game wrote online mafia game
on Wed, Oct 8 2014 3:46 PM

Silverlight 5 Beta Rough Notes–Multiple Window Support - Mike Taulty's Blog - Mike Taulty's Blog

gates repair Alameda wrote gates repair Alameda
on Tue, Nov 18 2014 2:40 PM

Silverlight 5 Beta Rough Notes–Multiple Window Support - Mike Taulty's Blog - Mike Taulty's Blog