Efficient WPF Window Instance Management via Extension Methods

Managing multiple window instances in WPF applications often requires balancing resource consumption with user experience. A common challenge involves preventing redundant instences of the same window type while ensuring that previously closed windows return to their last known position. By leveraging C# extension methods and concurrent collections, developers can implement a clean, reusable mechanism for window lifecycle management.

Implementation of the WindowManager Class

The following extension class provides a thread-safe approach to tracking active window instances and caching their screen coordinates.

using System;
using System.Collections.Concurrent;
using System.Windows;

public static class WindowManager
{
    // Tracks active instances by type
    private static readonly ConcurrentDictionary<Type, Window> InstanceRegistry = new ConcurrentDictionary<Type, Window>();
    
    // Caches the last screen position for each window type
    private static readonly ConcurrentDictionary<Type, Rect> PositionCache = new ConcurrentDictionary<Type, Rect>();

    public static void DisplaySingleton<T>(this T windowInstance) where T : Window, new()
    {
        var dispatcher = Application.Current.Dispatcher;

        if (!dispatcher.CheckAccess())
        {
            dispatcher.Invoke(() => DisplaySingleton(windowInstance));
            return;
        }

        var windowType = typeof(T);

        // Attempt to retrieve an existing instance
        if (InstanceRegistry.TryGetValue(windowType, out var activeWindow) && activeWindow.IsLoaded)
        {
            FocusExistingWindow(activeWindow);
        }
        else
        {
            InitializeAndShowWindow(windowInstance, windowType);
        }
    }

    private static void FocusExistingWindow(Window window)
    {
        if (window.WindowState == WindowState.Minimized)
        {
            window.WindowState = WindowState.Normal;
        }
        window.Activate();
        window.Focus();
    }

    private static void InitializeAndShowWindow(Window window, Type type)
    {
        // Restore previous geometry if available
        if (PositionCache.TryGetValue(type, out var lastRect))
        {
            window.Left = lastRect.Left;
            window.Top = lastRect.Top;
        }

        InstanceRegistry[type] = window;

        window.Closed += (sender, args) =>
        {
            // Store final position before removal
            var closingWindow = (Window)sender;
            PositionCache[type] = new Rect(closingWindow.Left, closingWindow.Top, closingWindow.ActualWidth, closingWindow.ActualHeight);
            InstanceRegistry.TryRemove(type, out _);
        };

        window.Show();
    }
}

Technical Breakdown

Concurrent State Tracking

The use of ConcurrentDictionary ensures that window references and position data remain consistent evenif window lifecycle events are triggered from different threads. The InstanceRegistry serves as a central hub to determine if a specific window type is currently rendered in the UI tree.

Thread Affinity and Dispatcher Management

UI elements in WPF have strict thread affinity. The DisplaySingleton method checks CheckAccess() to verify if the call is on the UI thread. If not, it marshals the operation via Dispatcher.Invoke, preventing InvalidOperationException errors commonly encountered during cross-thread UI updates.

Window Geometry Persistence

The PositionCache stores a Rect (or specific coordinates) indexed by the window's Type. When a window is instantiated, the logic checks this cache. If a match is found, the Left and Top properties are applied before the Show() method is called, providing a seamless "sticky" position behavior that users expect.

Lifecycle Hooking

By subscribing to the Closed event inside the extension method, the system automatically cleans up the registry. This prevents memory leaks by allowing the Garbage Collector to reclaim window instances that are no longer in use, while simultaneously updating the coordinate cache for future sessions.

Usage Example

To use this management system, simply instantiate the desired window and call the extension method. The logic handles the singleton check internally.

private void OnOpenSettingsExecute()
{
    // If a SettingsWindow is already open, it will be focused.
    // If not, a new one is created and placed at its last closed position.
    var settings = new SettingsWindow();
    settings.DisplaySingleton();
}

This pattern decouples the window instantiation logic from the management logic, making it easier to maintain large applications with numerous independent modules.

Tags: C# WPF .NET ui-design Desktop-Development

Posted on Fri, 22 May 2026 23:19:42 +0000 by callmecheez