管理 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 @   p1016520  阅读(24)  评论(0编辑  收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示