Click here to Skip to main content
15,881,803 members
Articles / Desktop Programming / WPF

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

Rate me:
Please Sign up or sign in to vote.
4.50/5 (2 votes)
21 Feb 2013CPOL3 min read 18.3K   348   11   2
A simple class to control open windows.

Introduction

Image 1

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.

Image 2

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:

C#
/// <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:

C#
/// <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.

Image 3

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.

C#
/// <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:

C#
/// <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:

C#
/// <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:

C#
/// <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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Avalon Development
United States United States
Jose A. Garcia Guirado, Electronic Engineer, graduated in Havana/Cuba 1982, MCTS, MCSD.NET, MCAD.NET, MCSE. Worked in the Institute for Cybernetics and Mathematics of Academy of Science of Cuba for 8 years; since 1995 working as free software architect, developer and adviser, first in Argentina and from 2003 to 2010, in Germany as External consultant in DWS Luxembourg, AIXTRON AG and Shell Deutschland GmbH and from 2010 to 2012 in Mexico working for Twenty Century Fox, and Mexico Stock Exchange (BMV). From 2013 to now in USA, Florida, First in FAME Inc. and now as Senior Software Engineer in Spirit Airlines.

Comments and Discussions

 
QuestionA possibly different way for one part of your issue Pin
Sacha Barber7-Feb-13 22:54
Sacha Barber7-Feb-13 22:54 
AnswerRe: A possibly different way for one part of your issue Pin
freedeveloper8-Feb-13 16:55
professionalfreedeveloper8-Feb-13 16:55 
Thanks very much for your analysis,

Yes it is possible to control the windows using the Application.Windows property and it is a very simple mode to close the windows when the application close.

I made a false emphasys in the control of open windows when the application is closing.
This work came for the necesity in several application to control open windows in SDI applications that have several levels of child windows. And as you correctly said that is the best point of the class. The class also gives a common base to support the windows and give a very easy point of communication between ViewModels and controls how many instance of the windows the user can create.

Again thanks for your reply.

Freedeveloper

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.