WPF中實現(xiàn)單例窗口的解決方案
一、前言
在WPF企業(yè)級應用開發(fā)中,我們常常需要確保某個窗口在同一時間只能有一個實例存在,同時還需要支持窗口的關聯(lián)關閉(關閉父窗體時,所有子窗體也會關閉)。
為了實現(xiàn)這些需求,我們采用一種侵入性較低的實現(xiàn)方式,通過創(chuàng)建一個通用的WindowSingletonBase類來管理窗口的單例行為。
在這里,我們實現(xiàn)線程安全的單例窗口管理、完善的父子窗口生命周期控制以及靈活的參數(shù)化窗口構(gòu)造,為WPF應用提供一套健壯的窗口管理解決方案。
二、實現(xiàn)目標
1. 單例窗口實現(xiàn)
- 支持無參和有參構(gòu)造
- 通過
GetInstance()方法獲取或創(chuàng)建實例 - 窗口關閉時自動釋放資源
2. 父子窗口關聯(lián)
- 建立父子窗口鏈路關系
- 父窗口關閉時自動關閉所有子窗口
- 子窗口關閉時自動從父窗口的集合中移除
三、代碼實現(xiàn)
1.創(chuàng)建 WindowSingletonBase 封裝單例邏輯
通過泛型基類WindowSingletonBase<T>封裝單例邏輯,同時提供父子窗口管理功能,既保證了功能的完整性,又不會對業(yè)務窗口類造成過多約束。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
namespace STC.Common.View.Component
{
/// <summary>
/// 窗口單例基類,提供單例管理和父子窗口關聯(lián)功能
/// </summary>
/// <typeparam name="T">繼承自Window的窗口類型</typeparam>
public class WindowSingletonBase<T> where T : Window
{
// 線程安全鎖
private static readonly object _lock = new object();
// 單例實例
private static T _instance;
// 窗口構(gòu)造方法
private static Func<object[], T> _constructor;
// 窗口所擁有子窗口集合
private static readonly List<Window> _childWindows = new List<Window>();
/// <summary>
/// 注冊窗口構(gòu)造函數(shù)
/// </summary>
public static void RegisterConstructor(Func<object[], T> constructor)
{
lock (_lock)
{
_constructor = constructor;
}
}
public static T Instance
{
get { return GetInstance(); }
}
/// <summary>
/// 獲取窗口單例實例
/// </summary>
public static T GetInstance(params object[] args)
{
lock (_lock)
{
if (_instance == null || !_instance.IsLoaded)
{
if (_constructor == null)
{
throw new InvalidOperationException("can not found constructor");
}
_instance = _constructor(args);
}
return _instance;
}
}
/// <summary>
/// 釋放當前實例
/// </summary>
public static void ReleaseInstance()
{
lock (_lock)
{
if (_instance != null)
{
_instance = null;
}
}
}
/// <summary>
/// 添加子窗口并建立父子關系
/// </summary>
public static void AddChildWindow(Window childWindow)
{
if (childWindow == null)
throw new ArgumentNullException(nameof(childWindow));
lock (_lock)
{
if (!_childWindows.Contains(childWindow))
{
childWindow.Owner = _instance; // 設置父子關系
_childWindows.Add(childWindow);
}
}
}
/// <summary>
/// 從子窗口集合中移除指定窗口
/// </summary>
public static void RemoveChild(Window childWindow)
{
lock (_lock)
{
if (_childWindows.Contains(childWindow))
{
_childWindows.Remove(childWindow);
}
}
}
/// <summary>
/// 關閉所有子窗口
/// </summary>
public static void CloseAllChildren()
{
lock (_lock)
{
foreach (var window in _childWindows.ToList()) // ToList避免修改集合
{
if (window != null)
{
window.Close();// 觸發(fā)子窗口的Closed事件
}
}
_childWindows.Clear();
}
}
/// <summary>
/// 顯示或激活窗口
/// </summary>
public static void ShowOrActive()
{
if (_instance != null)
{
if (_instance.IsVisible)
{
_instance.Activate();
}
else
{
_instance.Show();
}
}
}
/// <summary>
/// 關閉窗口
/// </summary>
public static void CloseWindow()
{
if (_instance != null)
{
if (_instance.IsVisible)
{
_instance.Close();
}
}
}
}
}
2.使用案例
在步驟1中,創(chuàng)建了WindowSingletonBase用來管理單例窗口?,F(xiàn)在我們來使用WindowSingletonBase。
在需要單例運行的窗體中,做三件事:
- 1.注冊構(gòu)造函數(shù)。
- 2.創(chuàng)建實例
- 3.重寫OnClose方法(用于釋放資源)
2.1 無參構(gòu)造窗體-使用單例窗體
ParentWindow.cs
public partial class ParentWindow : Window
{
static ParentWindow()
{
// 注冊無參構(gòu)造函數(shù)
WindowSingletonBase<ParentWindow>.RegisterConstructor(args => new ParentWindow());
}
public ParentWindow()
{
InitializeComponent();
}
/// <summary>
/// 聲明單例窗口 Instance
/// </summary>
public static ParentWindow CreateInstance()
{
return WindowSingletonBase<ParentWindow>.GetInstance();
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
// 釋放單例實例并關閉所有子窗口
WindowSingletonBase<ParentWindow>.ReleaseInstance();
WindowSingletonBase<ParentWindow>.CloseAllChildren();
}
private void OpenChildWindow(object sender, RoutedEventArgs e)
{
// 獲取子窗口實例并顯示
var childWindow = ChildWindow.CreateInstance();
WindowSingletonBase<ParentWindow>.AddChildWindow(childWindow);
WindowSingletonBase<ChildWindow>.ShowOrActivate();
}
}
ChildWindow.cs
public partial class ChildWindow : Window
{
static ChildWindow()
{
// 注冊無參構(gòu)造函數(shù)(方式1)
// WindowSingletonBase<ChildWindow>.RegisterConstructor(args => new ChildWindow());
// 注冊無參構(gòu)造函數(shù)(方式2)
WindowSingletonBase<ChildWindow>.RegisterConstructor(delegate (object[] args)
{
return new ChildWindow();
});
}
public ChildWindow()
{
InitializeComponent();
}
/// <summary>
/// 聲明單例窗口
/// </summary>
public static ChildWindow CreateInstance()
{
return WindowSingletonBase<ChildWindow>.GetInstance();
}
/// <summary>
/// 關閉窗口
/// </summary>
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
// 從父窗體的子窗口列表中移除
WindowSingletonBase<ParentWindow>.RemoveChild(this);
WindowSingletonBase<ChildWindow>.ReleaseInstance();
}
}
2.2 有參構(gòu)造窗體-使用單例窗體
ParentWindow.cs
public partial class ParentWindow : Window
{
static ParentWindow()
{
// 注冊無參構(gòu)造函數(shù)
WindowSingletonBase<ParentWindow>.RegisterConstructor(args => new ParentWindow());
}
public ParentWindow()
{
InitializeComponent();
}
/// <summary>
/// 聲明單例窗口 Instance
/// </summary>
public static ParentWindow CreateInstance()
{
return WindowSingletonBase<ParentWindow>.GetInstance();
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
// 釋放單例實例并關閉所有子窗口
WindowSingletonBase<ParentWindow>.ReleaseInstance();
WindowSingletonBase<ParentWindow>.CloseAllChildren();
}
private void OpenChildWindow(object sender, RoutedEventArgs e)
{
// 獲取子窗口實例并顯示
var childWindow = ChildWindow.CreateInstance("張三", 18);
WindowSingletonBase<ParentWindow>.AddChildWindow(childWindow);
WindowSingletonBase<ChildWindow>.ShowOrActivate();
}
}
ChildWindow.cs
public partial class ChildWindow : Window
{
static ChildWindow()
{
// 注冊無參構(gòu)造函數(shù)
WindowSingletonBase<ChildWindow>.RegisterConstructor(delegate (object[] args)
{
return new AddGroupWin(args[0] as string, (int)args[1]);
});
}
public ChildWindow(string name, int age)
{
InitializeComponent();
}
/// <summary>
/// 聲明單例窗口
/// </summary>
public static ChildWindow CreateInstance(string name, int age)
{
return WindowSingletonBase<ChildWindow>.GetInstance(name, age);
}
/// <summary>
/// 關閉窗口
/// </summary>
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
// 從父窗體的子窗口列表中移除
WindowSingletonBase<ParentWindow>.RemoveChild(this);
WindowSingletonBase<ChildWindow>.ReleaseInstance();
}
}
2.3 有參構(gòu)造窗體-窗體刷新
如果這個彈窗是在列表中,點擊編輯按鈕彈窗,我們需要每次點擊的時候都刷新窗口??梢栽贑reateInstance方法中做一些改變。
ParentWindow.cs
public partial class ParentWindow : Window
{
static ParentWindow()
{
// 注冊無參構(gòu)造函數(shù)
WindowSingletonBase<ParentWindow>.RegisterConstructor(args => new ParentWindow());
}
public ParentWindow()
{
InitializeComponent();
}
/// <summary>
/// 聲明單例窗口 Instance
/// </summary>
public static ParentWindow CreateInstance()
{
return WindowSingletonBase<ParentWindow>.GetInstance();
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
// 釋放單例實例并關閉所有子窗口
WindowSingletonBase<ParentWindow>.ReleaseInstance();
WindowSingletonBase<ParentWindow>.CloseAllChildren();
}
private void OpenChildWindow(object sender, RoutedEventArgs e)
{
// 獲取子窗口實例并顯示
var childWindow = ChildWindow.CreateInstance("張三", 18);
WindowSingletonBase<ParentWindow>.AddChildWindow(childWindow);
WindowSingletonBase<ChildWindow>.ShowOrActivate();
}
}
ChildWindow.cs
public partial class ChildWindow : Window
{
// 演示需要(不推薦在ChildWindow中聲明viewModel)
private ChildWindowViewModel viewModel;
private static ChildWindow _childWindow;
static ChildWindow()
{
// 注冊無參構(gòu)造函數(shù)
WindowSingletonBase<ChildWindow>.RegisterConstructor(delegate (object[] args)
{
return new AddGroupWin(args[0] as string, (int)args[1]);
});
}
public ChildWindow(string name, int age)
{
InitializeComponent();
}
/// <summary>
/// 聲明單例窗口
/// </summary>
public static ChildWindow CreateInstance(string name, int age)
{
if (_childWindow!=null)
{
ChildWindowWinViewModel model = _childWindow.viewModel as ChildWindowWinViewModel;
// 點擊的是當前展示的數(shù)據(jù),則返回當前window
if (model != null && model.name == name && model.age == age)
{
return _childWindow;
}
else
{
// 點擊的是另一條數(shù)據(jù),則重新new viewModel,重新設置 _childWindow的DataContext
// 在這里省略ChildWindowWinViewModel代碼,根據(jù)情況自行編寫
_childWindow.viewModel = new ChildWindowWinViewModel(id, instId, _childWindow);
_childWindow.DataContext = _childWindow.viewModel;
return _childWindow;
}
}
// 如果 _childWindow 是空的,則聲明單例窗口。
_childWindow = WindowSingletonBase<ChildWindow>.GetInstance(name, age);
return _childWindow;
}
/// <summary>
/// 關閉窗口
/// </summary>
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
// 從父窗體的子窗口列表中移除
WindowSingletonBase<ParentWindow>.RemoveChild(this);
WindowSingletonBase<ChildWindow>.ReleaseInstance();
}
}
以上就是WPF中實現(xiàn)單例窗口的解決方案的詳細內(nèi)容,更多關于WPF單例窗口的資料請關注腳本之家其它相關文章!
相關文章
C# Dynamic關鍵字之:dynamic為什么比反射快的詳解
本篇文章是對C#中dynamic為什么比反射快進行了詳細的分析介紹,需要的朋友參考下2013-05-05
C# OpenCVSharp實現(xiàn)顏色空間轉(zhuǎn)換功能
OpenCVSharp 是 OpenCV 的 C# 封裝,允許開發(fā)者在 .NET 環(huán)境下使用 OpenCV 提供的強大計算機視覺和圖像處理功能,它支持大多數(shù) OpenCV 的功能,包括圖像讀取、處理、特征檢測、機器學習等,所以本文給大家介紹了C# OpenCVSharp實現(xiàn)顏色空間轉(zhuǎn)換功能,需要的朋友可以參考下2025-09-09

