管理 Windows 例項的高效方法 —— 使用 WindowExtensions
類
在現代的 Windows 應用程式開發中,尤其是使用 WPF(Windows Presentation Foundation)時,管理多個視窗例項是一個常見的需求。為了確保應用程式的使用者體驗流暢且一致,開發者常常需要控制視窗的建立、顯示以及位置管理。本文將深入解析一個基於 C# 的 WindowExtensions
擴充套件類,實現單例化視窗、啟用已有視窗以及儲存視窗位置的高效方法。
目錄
- 背景與需求
- 程式碼概述
- 詳細解析
- 使用
ConcurrentDictionary
管理視窗例項 - 泛型方法
ShowActive<T>
的實現 - 視窗位置的儲存與恢復
- 使用
- 如何使用
WindowExtensions
- 優勢與最佳實踐
- 總結
背景與需求
在開發桌面應用程式時,常常需要管理多個不同型別的視窗。例如,應用程式可能包含主視窗、設定視窗、幫助視窗等。為了最佳化使用者體驗,通常希望:
- 單例化視窗:確保每種型別的視窗在應用程式中只有一個例項,避免資源浪費和潛在的使用者混淆。
- 啟用已有視窗:如果視窗已經開啟,直接啟用並置頂,而不是建立新的例項。
- 儲存視窗位置:當使用者關閉視窗後,儲存其位置,下次開啟時恢復到上次的位置。
手動管理這些需求可能會導致重複程式碼和複雜的邏輯控制。因此,利用擴充套件方法和併發集合,可以簡化這一過程。
程式碼概述
以下是 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();
}
});
}
關鍵點解析:
-
泛型約束:方法
ShowActive<T>
使用泛型,約束 T 必須繼承自 Window 並且有無引數的建構函式(new())。 -
排程器呼叫:Application.Current.Dispatcher.Invoke 確保所有 UI 操作在主執行緒上執行,避免跨執行緒操作異常。
-
獲取或新增視窗例項:
- GetOrAdd 方法嘗試從 _activeWindows 中獲取現有視窗例項。
- 如果不存在,則新增傳入的 window 例項。
- 視窗可見性判斷:
- 如果視窗已可見,則啟用並置頂。
- 如果視窗未可見,則恢復上次儲存的位置。
- 視窗關閉事件:
- 訂閱視窗的 Closed 事件,在視窗關閉時儲存位置並移除視窗例項。
- 顯示視窗:
- 顯示視窗。
視窗位置的儲存與恢復
視窗位置的儲存與恢復是 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
類,它可以簡化視窗管理的複雜邏輯,並提供單例化視窗、啟用已有視窗以及儲存視窗位置的高效方法。