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

p1016520發表於2024-09-20

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

在現代的 Windows 應用程式開發中,尤其是使用 WPF(Windows Presentation Foundation)時,管理多個視窗例項是一個常見的需求。為了確保應用程式的使用者體驗流暢且一致,開發者常常需要控制視窗的建立、顯示以及位置管理。本文將深入解析一個基於 C# 的 WindowExtensions 擴充套件類,實現單例化視窗、啟用已有視窗以及儲存視窗位置的高效方法。

目錄

  1. 背景與需求
  2. 程式碼概述
  3. 詳細解析
    • 使用 ConcurrentDictionary 管理視窗例項
    • 泛型方法 ShowActive<T> 的實現
    • 視窗位置的儲存與恢復
  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 類,它可以簡化視窗管理的複雜邏輯,並提供單例化視窗、啟用已有視窗以及儲存視窗位置的高效方法。

相關文章