管理 Windows 实例的高效方法 —— 使用 WindowExtensions 类

管理 Windows 实例的高效方法 —— 使用 WindowExtensions

在现代的 Windows 应用程序开发中,尤其是使用 WPF(Windows Presentation Foundation)时,管理多个窗口实例是一个常见的需求。为了确保应用程序的用户体验流畅且一致,开发者常常需要控制窗口的创建、显示以及位置管理。本文将深入解析一个基于 C# 的 WindowExtensions 扩展类,实现单例化窗口、激活已有窗口以及保存窗口位置的高效方法。

目录

  1. 背景与需求
  2. 代码概述
  3. 详细解析
  4. 如何使用 WindowExtensions
  5. 优势与最佳实践
  6. 总结

背景与需求

在开发桌面应用程序时,常常需要管理多个不同类型的窗口。例如,应用程序可能包含主窗口、设置窗口、帮助窗口等。为了优化用户体验,通常希望:

  • 单例化窗口:确保每种类型的窗口在应用程序中只有一个实例,避免资源浪费和潜在的用户混淆。
  • 激活已有窗口:如果窗口已经打开,直接激活并置顶,而不是创建新的实例。
  • 保存窗口位置:当用户关闭窗口后,保存其位置,下次打开时恢复到上次的位置。

手动管理这些需求可能会导致重复代码和复杂的逻辑控制。因此,利用扩展方法和并发集合,可以简化这一过程。

代码概述

以下是 WindowExtensions 类的完整代码实现:

点击查看代码
/// <summary>
/// Windows 扩展类
/// </summary>
public static class WindowExtensions
{
    // 使用 ConcurrentDictionary 支持多种窗口类型
    private static readonly ConcurrentDictionary<Type, Window> _activeWindows = new ConcurrentDictionary<Type, Window>();
    // 保存关闭窗口位置
    private static readonly ConcurrentDictionary<Type, Point> _savedLocations = new ConcurrentDictionary<Type, Point>();

    /// <summary>
    /// 泛型方法,处理特定类型的窗口
    /// 单例化窗口,存在就激活
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="window"></param>
    public static void ShowActive<T>(this T window) where T : Window, new()
    {
        Application.Current.Dispatcher.Invoke(() =>
        {
            var windowType = typeof(T);
            var existingWindow = _activeWindows.GetOrAdd(windowType, _ => window);

            if (existingWindow.IsVisible)
            {
                if (existingWindow.WindowState == WindowState.Minimized)
                {
                    existingWindow.WindowState = WindowState.Normal;
                }
                existingWindow.Activate();
            }
            else
            {
                // 恢复窗口位置
                var previousLocation = _savedLocations.GetOrAdd(windowType, new Point(0, 0));
                if (previousLocation != default(Point))
                {
                    window.Left = previousLocation.X;
                    window.Top = previousLocation.Y;
                }
                _activeWindows[windowType] = window;
                window.Closed += (s, e) =>
                {
                    // 保存此类型窗口关闭的位置
                    _savedLocations[windowType] = new Point(window.Left, window.Top);
                    _activeWindows.TryRemove(windowType, out _);
                };

                window.Show();
            }
        });
    }
}

详细解析

让我们逐步拆解这段代码,了解其内部机制和实现逻辑。

使用 ConcurrentDictionary 管理窗口实例


// 使用 ConcurrentDictionary 支持多种窗口类型
private static readonly ConcurrentDictionary<Type, Window> _activeWindows = new ConcurrentDictionary<Type, Window>();
// 保存关闭窗口位置
private static readonly ConcurrentDictionary<Type, Point> _savedLocations = new ConcurrentDictionary<Type, Point>();

_activeWindows :用于存储当前活动的窗口实例。ConcurrentDictionary 确保在多线程环境下的线程安全。

_savedLocations :用于保存每种窗口类型关闭时的屏幕位置(左上角的坐标),以便下次打开时恢复。

泛型方法 ShowActive<T> 的实现

public static void ShowActive<T>(this T window) where T : Window, new()
{
    Application.Current.Dispatcher.Invoke(() =>
    {
        var windowType = typeof(T);
        var existingWindow = _activeWindows.GetOrAdd(windowType, _ => window);

        if (existingWindow.IsVisible)
        {
            if (existingWindow.WindowState == WindowState.Minimized)
            {
                existingWindow.WindowState = WindowState.Normal;
            }
            existingWindow.Activate();
        }
        else
        {
            // 恢复窗口位置
            var previousLocation = _savedLocations.GetOrAdd(windowType, new Point(0, 0));
            if (previousLocation != default(Point))
            {
                window.Left = previousLocation.X;
                window.Top = previousLocation.Y;
            }
            _activeWindows[windowType] = window;
            window.Closed += (s, e) =>
            {
                // 保存此类型窗口关闭的位置
                _savedLocations[windowType] = new Point(window.Left, window.Top);
                _activeWindows.TryRemove(windowType, out _);
            };

            window.Show();
        }
    });
}

关键点解析:

  1. 泛型约束:方法 ShowActive<T> 使用泛型,约束 T 必须继承自 Window 并且有无参数的构造函数(new())。

  2. 调度器调用:Application.Current.Dispatcher.Invoke 确保所有 UI 操作在主线程上执行,避免跨线程操作异常。

  3. 获取或添加窗口实例

  • GetOrAdd 方法尝试从 _activeWindows 中获取现有窗口实例。
  • 如果不存在,则添加传入的 window 实例。
  1. 窗口可见性判断
  • 如果窗口已可见,则激活并置顶。
  • 如果窗口未可见,则恢复上次保存的位置。
  1. 窗口关闭事件
  • 订阅窗口的 Closed 事件,在窗口关闭时保存位置并移除窗口实例。
  1. 显示窗口
  • 显示窗口。

窗口位置的保存与恢复

窗口位置的保存与恢复是 ShowActive<T> 方法的核心功能。

// 恢复窗口位置
var previousLocation = _savedLocations.GetOrAdd(windowType, new Point(0, 0));
if (previousLocation != default(Point))
{
    window.Left = previousLocation.X;
    window.Top = previousLocation.Y;
}
...
window.Closed += (s, e) =>
{
    // 保存此类型窗口关闭的位置
    _savedLocations[windowType] = new Point(window.Left, window.Top);
    _activeWindows.TryRemove(windowType, out _);
};

_savedLocations.GetOrAdd(windowType, new Point(0, 0)):尝试从 _savedLocations 中获取窗口类型对应的位置,如果不存在,则返回默认位置(左上角坐标为(0,0))。

window.Left = previousLocation.X;:设置窗口的左上角坐标 X。

window.Top = previousLocation.Y;:设置窗口的左上角坐标 Y。

// 保存此类型窗口关闭的位置
_savedLocations[windowType] = new Point(window.Left, window.Top);

_savedLocations[windowType] = new Point(window.Left, window.Top);:保存窗口关闭时的位置。

// 保存此类型窗口关闭的位置
_savedLocations[windowType] = new Point(window.Left, window.Top);

_activeWindows.TryRemove(windowType, out _);:移除窗口实例。

如何使用 WindowExtensions

在应用程序的各个窗口中,只需调用 ShowActive<T> 方法即可实现窗口的单例化、激活以及位置保存。

例如,在主窗口中:

// 假设有一个 SettingsWindow 类继承自 Window
var settingsWindow = new SettingsWindow();
settingsWindow.ShowActive();

在设置窗口中:

// 主窗口中的按钮点击事件
private void OpenSettings_Click(object sender, RoutedEventArgs e)
{
    var settingsWindow = new SettingsWindow();
    settingsWindow.ShowActive();
}

优势与最佳实践

  • 简化代码:利用扩展方法和并发集合,可以简化窗口管理的复杂逻辑。
  • 线程安全:使用 ConcurrentDictionary 保证线程安全。
  • 可扩展性:可以扩展支持更多窗口类型。
  • 代码可读性:代码结构清晰,命名符合规范。

总结

本文介绍了 WindowExtensions 类,它可以简化窗口管理的复杂逻辑,并提供单例化窗口、激活已有窗口以及保存窗口位置的高效方法。

posted @ 2024-09-20 09:15  lfx-Sunshine  阅读(73)  评论(0)    收藏  举报