Listing the applications open forms in Winforms and WPF apps

Listing the applications open forms in Winforms and WPF apps
Listing the applications open forms in Winforms and WPF apps

Enumerating the applications open forms in C# & VB.NET

Yesterday, I a client told me to develop a specific little functionality for him – limiting the applications open forms of a Winforms app. Generally this is a pretty basic task, yet many devs in the learning process of .NET languages like C# and VB.NET asked me about it in the past as well. So in the end I was like: Why shouldn’t I write a little blog post on how to list the open forms of the application with a bit of extra things.

The easiest and quickest way of getting / listing the opened forms or windows in your .NET apps is by using the Application.OpenForms property for Winforms and the Application.Current.Windows property for WPF apps. These properties will provide iteratable objects to do your business upon.

infoIf you’re in a hurry, feel free to jump to the specific section of this blog post or use the hint from above here / the wrap up. I’d still encourage you to evaluate on the things potentially going on in the background though.

Before I literally shoot the „easy way“ at you, I think that it’s actually pretty neat to fully understand everything behind the scenes. This way you could create even more functionality like opening a form only if some user is authenticated / authorized and much more.

Building a centralized WindowManager class

A manager class for listing the applications open forms in Winforms and WPF apps
A manager class for listing the applications open forms in Winforms and WPF apps

Imagine a typical code to actually show a new form / window inside of your app. Usually this is pretty easily done by just creating a new instance of your forms class and calling like the „Show“ method on it. The problem is, that this „window / form“ is only like relevant in that little piece of code or that context. How would you then be able to keep track of „opened“ ones, if they are pretty much forgot after calling them? Let’s elaborate on that further down here!

Showing a simple form

Take a look at – for example – the following code, which could reside in a typical button click handler. Keep in mind, that I will not abuse that VB.NET specific „auto form instance“ stuff, as it’s dirty for professional developers. For C# it’s the normal and pretty much only way using a clean instance anyways:

private void btnShowNewForm_Click(object? sender, EventArgs e)
{
    Form2 form2 = new Form2();
    form2.Show();
}
Private Sub btnShowNewForm_Click(sender As Object, e As EventArgs) Handles btnShowNewForm.Click
    Dim form2 = New Form2()
    form2.Show()
End Sub

The „problem“ with the above code is, that you don’t actually have a nice way to keep track of the created instances. I mean you shouldn’t obviously do something like this, as it would grow with each instance you would be keeping track of. Using single fields like below or even using some sort of list doesn’t feel really good:

public class Form1
{

  Form2? _form2;

  // Form3? _form3;
  // Form4? _form4;
  // more...

  private void btnShowNewForm_Click(object? sender, EventArgs e)
  {
      if (_form2 == null)
        _form2 = new Form2();
      _form2.Show();
  }

}
Public Class Form1

    Private _form2 As Form2

    ' Private _form3 As Form3
    ' Private _form4 As Form4
    ' more...

    Private Sub btnShowNewForm_Click(sender As Object, e As EventArgs) Handles btnShowNewForm.Click
        If _form2 Is Nothing Then
            _form2 = New Form2()
        End If
        form2.Show()
    End Sub

End Class

Another problem could be, that you could potentially be tempted to repeat your code over and over again – like in the following image. Each button could do it’s own (sort of) checking routine resulting in repetitive code. I mean, for sure you could just create a function to group up those lines of code, but this still doesn’t feel right as this function would probably reside in your form – thus not being able to be easily / cleanly reused accross other forms. I won’t even start talking about things like dependency injection and inversion of control, etc. here.

Button and rendundant code checking if open forms or windows limit reached
Button and redundant code checking if open forms or windows limit reached

A „better“ approach of tracking open application forms & windows

After checking out the small code from above and noticing, that it doesn’t quite feel right solving it this way, let’s move on. We now take a look at the „typical“ manager approach, but please be careful when designing a „manager“ class, as this is mostly having a tendency to evolve into „one thing that does far too much“.

For this specific case, I think it’s pretty much okay to create a small „manager“ class and it therefore doesn’t really hurt the „single responsibility principle“. In the end our code could be designed like in the following image instead – but just being a suggestion or food for thought!

A manager class being responsible for all open forms inside your app
A manager class being responsible for all open forms inside your app

Centralizing responsibility

Centralizing some responsibility into a common denominator often is a good thing (but not always), because you can increase the reusability a lot this way. However you should keep in mind that modifying a centralized piece of code can therefore be breaking for multiple targets at once. So it’s basically a double-edged sword which should be thought through.

After taking those previous arguments into consideration, let’s now create a simple „WindowManager“ class for (for example) our Winforms app. Remember that this is only a small example and could be adjusted to your specific needs or ideas in your projects.

Showing the form & a word on DI

For our window manager class, we will create a generic „ShowWindow“ function. This way, we can call that function for different types of windows / forms. As I want to keep this simple, I won’t dive too hard into like „dependency injection“ or „inversion of control“. Usually I would say, that the „WindowManager“ should be „dependant“ on DI container like object (interface!) and resolve the Forms / Windows from there. But as said, let’s stay easy here!

Now the „WindowManager“ class is in charge of the „ShowForm“ logic, which enables it to be in full control and actually keep track of every show form – in multiple ways. I could for example resolve some sort of access logic with authentication, etc. as well as saving and removing the forms from a list.

The thing with modifiable lists

The thing is, that a list wouldn’t be enough here, because an instance of „List“ could simply be modified from the outside – bad! Let’s fix this by building a wrapper around that list to avoid that mentioned modification / tempering from the outside! If that’s maybe too complicated for you, kick out the „ReadOnlyCollection“ and just the „List<T>“ as a usual property.

You may notice, that I added the created form instance before actually showing it. I did this to have that form inside of that list before some error could be thrown while actually showing the form itself. If that’s weird to you, feel free to swap the lines accordingly.

using System.Windows.Forms;
using System.Collections.ObjectModel;

public class WindowManager
{

    List<Form> _openForms;

    public ReadOnlyCollection<Form> OpenForms { get; set; }

    public WindowManager()
    {
        _openForms = new List<Form>();
        OpenForms = new ReadOnlyCollection<Form>(_openForms);
    }

    // or ShowWindow?
    public void ShowForm<T>() where T : Form
    {
        // usually should be resolved from some sort of DI container
        Form form = (Form)Activator.CreateInstance<T>();
        _openForms.Add(form);
        form.Show();
    }

}
Imports System.Windows.Forms
Imports System.Collections.ObjectModel

Public Class WindowManager

    ' renamed or the VB compiler will cry
    Private _theOpenForms As List(Of Form)

    Public Property OpenForms As ReadOnlyCollection(Of Form)

    Sub New()
        _theOpenForms = New List(Of Form)()
        OpenForms = New ReadOnlyCollection(Of Form)(_theOpenForms)
    End Sub

    ' or ShowWindow?
    Public Sub ShowForm(Of T As Form)()
        ' usually should be resolved from some sort of DI container
        Dim form = CType(Activator.CreateInstance(Of T)(), Form)
        _theOpenForms.Add(form)
        form.Show()
    End Sub

End Class

Handling form & window closes

I mean we got the first part working pretty well, except for one last thing at least. Can you guess what it is? Well, okay, I just kinda spoilered it with that headline: We forgot about handling the closing processes of the form. I mean, it would be pretty unsatisfying if we said: „Hey, when a form / window gets opened, add it to our OpenForms list to track it“, but forgetting the opposite: „…if a form closes again, remove it from our tracker list again“. So we’re now going ahead and care for that!

The first step to our more reliable list would be, that we actually get notified if any form gets closed. This would enable us reacting to that being able to remove the closed form from our list again. Well, in terms of .NET „reacting“ pretty much means „handle events with a specified handler“. A handler itself is mostly just a method with a matching parameter signature.

Defining and attaching the handler

In the next step, we will attach that mentioned handler method from above. Take a look at the previously defined „ShowForm“ method in the „WindowManager“ class and focus on that locally created „form“ variable. This is our point of interest, as we can now attach a handler exactly there. Please don’t get confused, we will create the handler method itself a little bit later down below. At first, just „mention“ the handler method without being already defined.

We will use that typical .NET event pattern for this. That means, that we will name the method like „<ObjectName>_<WhichEventToHandle>“ resulting in „Form_FormClosed“. The „Form“ is capitalized because this is how methods should be named, even though the local variable is written like „form“. Let’s not talk about that poor naming of that „FormClosed“ event itself here…

Now you should (obviously) get an error, because you tried to use a method that didn’t exist (yet), but as already mentioned, we will create that one now.

Removing the closed form & null

Now we can finally remove the form itself from the „tracker list“. For this, we – like usually – cast the sender of the event (which is the closed form behind the scenes) to the „Form“ class. Keep an eye on that exclamation mark after „sender“. The newer C# signatures of most methods are nullable, so we need to handle that. In this case, the sender will never be null, so I can forcefully tell the compiler: „No worries, there WILL be a sender available“. After that, I’m casting the sender into the correct type, being able to do some things.

At first, I will unsubscribe the handler method from the event to clean up a bit. Then I will handle the closing itself, therefore removing the „closedForm“ from the tracker list. Internally it will now work without further problems and we can access the „OpenForms“ property everytime we want to see the – well – open forms.

using System.Windows.Forms;
using System.Collections.ObjectModel;

public class WindowManager
{

    List<Form> _openForms;

    public ReadOnlyCollection<Form> OpenForms { get; set; }

    public WindowManager()
    {
        _openForms = new List<Form>();
        OpenForms = new ReadOnlyCollection<Form>(_openForms);
    }

    public void ShowForm<T>() where T : Form
    {
        Form form = Activator.CreateInstance<T>();
        // this is new
        form.FormClosed += Form_FormClosed;
        _openForms.Add(form);
        form.Show();
    }

    private void Form_FormClosed(object? sender, FormClosedEventArgs e)
    {
        // should never be null
        // ignore handling of null check by appending "!"
        Form closedForm = (Form)sender!;
        closedForm.FormClosed -= Form_FormClosed;
        _openForms.Remove(closedForm);
    }

}
Imports System.Windows.Forms
Imports System.Collections.ObjectModel

Public Class WindowManager

    ' renamed or the VB compiler will cry
    Private _theOpenForms As List(Of Form)

    Public Property OpenForms As ReadOnlyCollection(Of Form)

    Sub New()
        _theOpenForms = New List(Of Form)()
        OpenForms = New ReadOnlyCollection(Of Form)(_theOpenForms)
    End Sub

    Public Sub ShowForm(Of T As Form)()
        Dim form = CType(Activator.CreateInstance(Of T)(), Form)
        ' this is new
        AddHandler form.FormClosed, AddressOf Form_FormClosed
        _theOpenForms.Add(form)
        form.Show()
    End Sub


    Private Sub Form_FormClosed(sender As Object, e As FormClosedEventArgs)
        Dim closedForm = CType(sender, Form)
        RemoveHandler closedForm.FormClosed, AddressOf Form_FormClosed
        _theOpenForms.Remove(closedForm)
    End Sub

End Class

Embedding limitations to our logic

After we’ve successfully integrated the basic handling of our forms, we can now extend this functionality even more. Things like these will make the main difference between „just getting the open forms“ and more complex functionality. Remembering the root of everything from above? I mean the customers wish: „I want to be able to limit the displayed forms“.

I will go deeper on that: My customer wanted a way, that his employees can only display like 3 appointment forms / windows at once. As the human brain is only capable of focussing at a few things at once, he didn’t want an employee to open like 10 appointment windows at once being the multitasking hero every boss is dreaming about.

The next step would now be to create some sort of limitation inside of our „ShowForm“ method. But I am a fan of „open for extension, closed for modification“, so we should actually create a separate method for that – I guess. Let’s call this method „ShowFormLimited“ and let me explain the goal of this method first: Everytime the user presses a button, we could call this function with a parameter of like 3. The method would then open (and instantiate…) the corresponding target form, but only if we aren’t exceeding the limit of 3 forms of that specific type.

Showing max 3 instances of a form

This is pretty easily done and I’ll only show this new method of the „WindowManager“ class as I don’t want you to be distracted by all the other code lines. So please don’t forget to eventually pull the other code from the examples above. Inside of the method we will first filter the forms being of the desired type by using the LINQ extension method „Where“ with a predicate lambda expression. We will then make a small check and only call (reusing) the method „ShowForm“ itself.

public void ShowFormLimited<T>(int limit) where T : Form
{
    int alreadyOpenedFormsOfThisType = _openForms
        .Where(x => x.GetType() == typeof(T))
        .Count();
    if (alreadyOpenedFormsOfThisType < limit)
        ShowForm<T>();
}
Public Sub ShowFormLimited(Of T As Form)(ByVal limit As Integer)
    Dim alreadyOpenedFormsOfThisType As Integer = _openForms _
        .Where(Function(x) x.GetType() = GetType(T)) _
        .Count()
    If alreadyOpenedFormsOfThisType < limit Then
        ShowForm(Of T)()
    End If
End Sub

Now we can make sure, that our employee can’t open like more than 3 appointment forms (Form2 in this example) at once. Take a look at this example code within a Winforms project with 3 forms (Form1, Form2, Form3):

namespace CsharpWinformsExample;

public partial class Form1 : Form
{

    WindowManager _windowManager;

    public Form1()
    {
        InitializeComponent();
        // should usually be injected by DI...
        _windowManager = new WindowManager();
    }

    private void btnShowForm2_Click(object sender, EventArgs e)
    {
        // only allow a maximum of 3 "Form2" at once
        _windowManager.ShowFormLimited<Form2>(3);
    }

}
    Public Partial Class Form1
        Inherits Form

        Private _windowManager As WindowManager

        Public Sub New()
            InitializeComponent()
            ' should usually be injected by DI...
            _windowManager = New WindowManager()
        End Sub

        Private Sub btnShowFormLimited_Click(sender As Object, e As EventArgs) Handles btnShowFormLimited.Click
            ' only allow a maximum of 3 "Form2" at once
            _windowManager.ShowFormLimited(Of Form2)(3)
        End Sub

    End Class

Handling open forms the default way

The typical or easy way of handling open forms and windows in your application
The typical or easy way of handling open forms and windows in your application

If the „stuff“ – lol – from above is a bit too complex or provides functionality that you aren’t really into right now, no problem, I got you covered as well. I would still encourage you to take a look, to understand the things behind the scenes though, but in this section I will provide the „typical“ way of handling open forms or windows in your .NET apps.

Iterating through each open form / window

In the first part I will provide an example for the most basic task when it comes to open forms in your .NET application: Some sort of iteration. This is pretty easy, as you can just grab the property corresponding to your project type (Winforms / WPF) and iterate through that.

This is how it could be done in a windows forms application:

foreach (Form form in Application.OpenForms)
{
  // access for example the text by
  // grabbing it from window.Text, etc.
  // add it to a listbox, whatever
}
For Each form As Form In Application.OpenForms
  ' access for example the text by
  ' grabbing it from window.Text, etc.
  ' add it to a listbox, whatever
Next

And here’s an example for WPF (Windows Presentation Foundation) based apps:

foreach (Window window in Application.Current.Windows) {
  // access for example the title by
  // grabbing it from window.Title, etc.
  // add it to a listbox, whatever
}
For Each window As Window In Application.Current.Windows
  ' access for example the title by
  ' grabbing it from window.Title, etc.
  ' add it to a listbox, whatever
Next

Filtering / counting open forms

Like in the example from above, we could count forms as well, to for example limit the simultaneously opened appointment forms. For this, you should filter first and count (if needed) afterwards.

Filtering & counting for Winforms:

// if you need a list
// change the declaration type
// and use .ToList() as well

// filtering only
IEnumerable<Form> formsOfTypeForm2 = Application
    .OpenForms
    .OfType<Form2>();

// actually counting
int countOfOpenForm2s = Application
    .OpenForms
    .OfType<Form2>()
    .Count();
' if you need a list
' change the declaration type
' and use .ToList() as well

' filtering only
Dim formsOfTypeForm2 As IEnumerable(Of Form) = Application _
    .OpenForms _
    .OfType(Of Form2)()

' actually counting
Dim countOfOpenForm2s As Integer = Application _
    .OpenForms _
    .OfType(Of Form2)() _
    .Count();

Filtering & counting for WPF:

// if you need a list
// change the declaration type
// and use .ToList() as well

// filtering only
IEnumerable<Form> windowsOfTypeWindow2 = Application
    .Current
    .Windows
    .OfType<Window2>();

// actually counting
int countOfOpenWindow2s = Application
    .Current
    .Windows
    .OfType<Window2>()
    .Count();
' if you need a list
' change the declaration type
' and use .ToList() as well

' filtering only
Dim windowsOfTypeWindow2 As IEnumerable(Of Form) = Application _
    .Current _
    .Windows _
    .OfType(Of Window2)()

' actually counting
Dim countOfOpenWindow2s As Integer = Application _
    .Current _
    .Windows _
    .OfType(Of Window2)() _
    .Count();

Wrapping up – Getting the applications open forms or windows in .NET

Getting the applications open forms or windows for Winforms and WPF - Wrapping Up
Getting the applications open forms or windows for Winforms and WPF – Wrapping Up

So in my today’s blog post, I’ve shown you how you can use the – sort of – internal solution to list / enumerate the open forms or windows of your application. I’ve presented a more custom way as well, which could help understanding the „behind the scenes“-mechanic of that form management. This way you could’ve even added your own logic like access controls or limitations to showing forms. In the end, it’s in your hand to pick the solution which fits your personal projects and ideas.

In short: If you just need to „quickly list / iterate“ all open forms or windows of your application, the mentioned property for Windows Forms applications:

Application.OpenForms

and the other one for WPF (Windows Presentation Foundation):

Application.Current.Windows

are potentially your way to go, as they are easy and iterable. But keep in mind that they don’t really provide more helpful things like little helper properties, centralization and more.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert