Click here to Skip to main content
Click here to Skip to main content

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

, 21 Feb 2013 CPOL
Rate this:
Please Sign up or sign in to vote.
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

freedeveloper
Software Developer (Senior) G-SoftSolutions
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 advisor, first in Argentina and from 2003 to 2010, in Germany as External consultant in DWS Luxemburg, AIXTRON AG and Shell Deutschland GmbH and from 2010 to 2012 in in Mexico in Twenty Century Fox, and Mexico Stock Exchange (BMV). Actually as freelance in Florida, USA.
Follow on   Google+

Comments and Discussions

 
QuestionA possibly different way for one part of your issue [modified] PinmvpSacha Barber7-Feb-13 23:54 
Mmmmm I would have just gone for using Application.Windows property for the problem of making sure all windows are closed, which is part of framework since 3.0 also works in SL, and is very easy to use. You just iterate through the Windows in the Applictation.Windows property and close them down if that is your issue you are trying to solve (which is seems to be)
 

http://msdn.microsoft.com/en-us/library/system.windows.application.windows.aspx
 

 
For the other part where you manage child windows, seems like neat enough idea, though I would like to see it more as a UI service which could be abstracted
 

Still cool. 4 from me
Sacha Barber
  • Microsoft Visual C# MVP 2008-2012
  • Codeproject MVP 2008-2012
Open Source Projects
Cinch SL/WPF MVVM

Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net


modified 8-Feb-13 5:16am.

AnswerRe: A possibly different way for one part of your issue Pinmemberfreedeveloper8-Feb-13 17:55 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141216.1 | Last Updated 21 Feb 2013
Article Copyright 2013 by freedeveloper
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid