Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

WPF, SDI linked window base class or don't miss your windows

0.00/5 (No votes)
21 Feb 2013 1  
A simple class to control open windows.

Introduction

A common behavior when you work with WPF, MVVM, and SDI windows is that you can easily lose control of your windows when you close the main windows. That is, child windows remain open when you close the parent windows, and you need to close them individually. To control this, you need to create a custom logic to close the child windows at the right moment. This article describes a method to do this, and offers a base class that implements control windows logic for you.

Background

The logic used here to control the windows is based on two properties in the base view model class. One property retains references to the parent window's view and view model, and the view and view model of the actual view model. This property is of the type ILinked. See image.

The other it is a list of ILinked objects with the same information, but from the child windows created for the actual view model.

The stored information in those properties gives you a chance to close all child windows when the parent window is closed and also to notify the parent windows when a child window is closed.

The two described properties are implemented in the LinkedViewModel class, you should implement that your view model inherited from LinkedViewModel. This class has all you need to automatically manage the child window states and to report to an existing parent about its self status.

Each time that a window is created, a list of ILinked information about the possible children is created and LinkedValues is populated with the necessary information about the window's parent and the information about the windows. If you create a new window the new window creates it's proper LinkedValues property and adds it to the child list of the parent.

All the logic of the control is called in the constructors, and occurs in the following procedure:

/// <summary>
/// Internal refactoring constructor code.
/// </summary>
/// <param name="view" >
/// The view for this view model.
/// </param>
/// <param name="parentView" />
/// The parent view.
/// </param>
/// <param name="parentViewModel" >
/// The parent view model.
/// </param>
private void SetParentValue(ContentControl view, ContentControl parentView, 
        ILinkedViewModel parentViewModel)
{
    this.ChildControlList = new List<ilinked>();
    ILinked ntc = Linked.Factory();
    ntc.ViewModel = this;
    ntc.ViewModelParent = parentViewModel;
    ntc.ViewParent = parentView;
    ntc.View = view;
    this.LinkedValues = ntc;

    // Set the child list of the parent, if it is not the root windows.
    if (parentViewModel != null)
    {
        if (parentViewModel.ChildControlList != null)
        {
            parentViewModel.ChildControlList.Add(ntc);
        }
    }

    // Set to get the closed envent.
    if (view is Window)
    {
        ((Window)view).Closed += this.TracingViewModelClosed;
    }
}

Observe that it also creates an event for the Closed windows event. This event is used to remove the Linked instance from the parent list of children, as you can see in the following code:

/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, 
/// or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
   var copyList = new List<ilinked>();
   copyList.AddRange(this.ChildControlList);

   foreach (ILinked tc in copyList)
   {
       var view = tc.View;
       if (view is Window)
       {
            ((Window)view).Close();
       }
   }

   // Disconnect from the parent
   if (this.LinkedValues != null && this.LinkedValues.ViewModelParent != null)
   {
       var vmp = this.LinkedValues.ViewModelParent;
       vmp.ChildControlList.Remove(this.LinkedValues);
   }
}

If a window is closed, automatically close all child windows that exist in ChildControlList and also removes from the parent window, information about it from the parent ChildControlList.

Then the control of the window is manipulated automatically by the base class and you don’t need to create code to do this. The presented LinkedViewModel also implements INotifyPropertyChanges to support the MVVM support for properties.

Using the code

To use these classes simply inherit your View Models from LinkedViewModel as shown in the following code.

/// <summary>
/// Base class for ViewModels.
/// </summary>
public class UserRootViewModel : LinkedViewModel
{
 ...
}

To create your root view model class and windows, simply declare it in the App.axml.cs of your project and create with the following code:

/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App
{
    /// <summary>
    /// Main Windows.
    /// </summary>
    private readonly Window main;

    /// <summary>
    /// ViewModel for tracing Windows.
    /// </summary>
    private readonly LinkedViewModel vm;

    /// <summary>
    /// Initializes a new instance of the <see cref="App"> class.
    /// </summary>
    public App()
    {
        this.main = new UserRootView();
        this.vm = new UserRootViewModel(this.main, null, null);
        this.main.DataContext = this.vm;
        this.main.Show();
    }
}

In this case we set the parent view and view model to null because they do not exist.

To create a window child, simply declare the View Model of the new window as the root class and create your view. Then create in the same form, but in this case fill the parent parameters. See the following example code:

/// <summary>
/// Command to create multiple windows.
/// </summary>
private void CreateMultipleWindow()
{
    var view = new WindowsChild();
    var viewModel = new WindowsChildViewModel(view, this.LinkedValues.View, this);
    view.DataContext = viewModel;
    view.Show();
}

You can also use the control of instance of the LinkedViewModel to see if more instances of the windows that you want to create exist. This auxiliary procedure helps you to limit the number of windows that needs to be created at the same time. To use this functionality you only need to call the static method TestMultiplicyOverrun. See the following example:

/// <summary>
/// Create a single windows.
/// </summary>
private void CreateSingleWindow()
{
 var view = new WindowsChild();
 if (LinkedViewModel.TestMultiplicyOverrun(view, this, 1))
 {
    view.Close();
    return;
 }

  var viewModel = new WindowsChildViewModel(view, this.LinkedValues.View, this);
  view.DataContext = viewModel;
  view.Show();
}

The routine simply scans the list of children in the view model and then returns true if the number of active windows of the same type is greater than the number of windows allowed (the last parameter of the method).

See the code for a complete example. The example also supports MVVM to illustrate a complete application with the pattern.

Points of interest

The article offers a simple method to control child windows in an SDI application using WPF and MVVM.

History

  • First version.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here