WPF中實(shí)現(xiàn)單例窗口的解決方案
一、前言
在WPF企業(yè)級(jí)應(yīng)用開(kāi)發(fā)中,我們常常需要確保某個(gè)窗口在同一時(shí)間只能有一個(gè)實(shí)例存在,同時(shí)還需要支持窗口的關(guān)聯(lián)關(guān)閉(關(guān)閉父窗體時(shí),所有子窗體也會(huì)關(guān)閉)。
為了實(shí)現(xiàn)這些需求,我們采用一種侵入性較低的實(shí)現(xiàn)方式,通過(guò)創(chuàng)建一個(gè)通用的WindowSingletonBase類來(lái)管理窗口的單例行為。
在這里,我們實(shí)現(xiàn)線程安全的單例窗口管理、完善的父子窗口生命周期控制以及靈活的參數(shù)化窗口構(gòu)造,為WPF應(yīng)用提供一套健壯的窗口管理解決方案。
二、實(shí)現(xiàn)目標(biāo)
1. 單例窗口實(shí)現(xiàn)
- 支持無(wú)參和有參構(gòu)造
- 通過(guò)
GetInstance()方法獲取或創(chuàng)建實(shí)例 - 窗口關(guān)閉時(shí)自動(dòng)釋放資源
2. 父子窗口關(guān)聯(lián)
- 建立父子窗口鏈路關(guān)系
- 父窗口關(guān)閉時(shí)自動(dòng)關(guān)閉所有子窗口
- 子窗口關(guān)閉時(shí)自動(dòng)從父窗口的集合中移除
三、代碼實(shí)現(xiàn)
1.創(chuàng)建 WindowSingletonBase 封裝單例邏輯
通過(guò)泛型基類WindowSingletonBase<T>封裝單例邏輯,同時(shí)提供父子窗口管理功能,既保證了功能的完整性,又不會(huì)對(duì)業(yè)務(wù)窗口類造成過(guò)多約束。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
namespace STC.Common.View.Component
{
/// <summary>
/// 窗口單例基類,提供單例管理和父子窗口關(guān)聯(lián)功能
/// </summary>
/// <typeparam name="T">繼承自Window的窗口類型</typeparam>
public class WindowSingletonBase<T> where T : Window
{
// 線程安全鎖
private static readonly object _lock = new object();
// 單例實(shí)例
private static T _instance;
// 窗口構(gòu)造方法
private static Func<object[], T> _constructor;
// 窗口所擁有子窗口集合
private static readonly List<Window> _childWindows = new List<Window>();
/// <summary>
/// 注冊(cè)窗口構(gòu)造函數(shù)
/// </summary>
public static void RegisterConstructor(Func<object[], T> constructor)
{
lock (_lock)
{
_constructor = constructor;
}
}
public static T Instance
{
get { return GetInstance(); }
}
/// <summary>
/// 獲取窗口單例實(shí)例
/// </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>
/// 釋放當(dāng)前實(shí)例
/// </summary>
public static void ReleaseInstance()
{
lock (_lock)
{
if (_instance != null)
{
_instance = null;
}
}
}
/// <summary>
/// 添加子窗口并建立父子關(guān)系
/// </summary>
public static void AddChildWindow(Window childWindow)
{
if (childWindow == null)
throw new ArgumentNullException(nameof(childWindow));
lock (_lock)
{
if (!_childWindows.Contains(childWindow))
{
childWindow.Owner = _instance; // 設(shè)置父子關(guān)系
_childWindows.Add(childWindow);
}
}
}
/// <summary>
/// 從子窗口集合中移除指定窗口
/// </summary>
public static void RemoveChild(Window childWindow)
{
lock (_lock)
{
if (_childWindows.Contains(childWindow))
{
_childWindows.Remove(childWindow);
}
}
}
/// <summary>
/// 關(guān)閉所有子窗口
/// </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>
/// 關(guān)閉窗口
/// </summary>
public static void CloseWindow()
{
if (_instance != null)
{
if (_instance.IsVisible)
{
_instance.Close();
}
}
}
}
}
2.使用案例
在步驟1中,創(chuàng)建了WindowSingletonBase用來(lái)管理單例窗口?,F(xiàn)在我們來(lái)使用WindowSingletonBase。
在需要單例運(yùn)行的窗體中,做三件事:
- 1.注冊(cè)構(gòu)造函數(shù)。
- 2.創(chuàng)建實(shí)例
- 3.重寫(xiě)OnClose方法(用于釋放資源)
2.1 無(wú)參構(gòu)造窗體-使用單例窗體
ParentWindow.cs
public partial class ParentWindow : Window
{
static ParentWindow()
{
// 注冊(cè)無(wú)參構(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);
// 釋放單例實(shí)例并關(guān)閉所有子窗口
WindowSingletonBase<ParentWindow>.ReleaseInstance();
WindowSingletonBase<ParentWindow>.CloseAllChildren();
}
private void OpenChildWindow(object sender, RoutedEventArgs e)
{
// 獲取子窗口實(shí)例并顯示
var childWindow = ChildWindow.CreateInstance();
WindowSingletonBase<ParentWindow>.AddChildWindow(childWindow);
WindowSingletonBase<ChildWindow>.ShowOrActivate();
}
}
ChildWindow.cs
public partial class ChildWindow : Window
{
static ChildWindow()
{
// 注冊(cè)無(wú)參構(gòu)造函數(shù)(方式1)
// WindowSingletonBase<ChildWindow>.RegisterConstructor(args => new ChildWindow());
// 注冊(cè)無(wú)參構(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>
/// 關(guān)閉窗口
/// </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()
{
// 注冊(cè)無(wú)參構(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);
// 釋放單例實(shí)例并關(guān)閉所有子窗口
WindowSingletonBase<ParentWindow>.ReleaseInstance();
WindowSingletonBase<ParentWindow>.CloseAllChildren();
}
private void OpenChildWindow(object sender, RoutedEventArgs e)
{
// 獲取子窗口實(shí)例并顯示
var childWindow = ChildWindow.CreateInstance("張三", 18);
WindowSingletonBase<ParentWindow>.AddChildWindow(childWindow);
WindowSingletonBase<ChildWindow>.ShowOrActivate();
}
}
ChildWindow.cs
public partial class ChildWindow : Window
{
static ChildWindow()
{
// 注冊(cè)無(wú)參構(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>
/// 關(guān)閉窗口
/// </summary>
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
// 從父窗體的子窗口列表中移除
WindowSingletonBase<ParentWindow>.RemoveChild(this);
WindowSingletonBase<ChildWindow>.ReleaseInstance();
}
}
2.3 有參構(gòu)造窗體-窗體刷新
如果這個(gè)彈窗是在列表中,點(diǎn)擊編輯按鈕彈窗,我們需要每次點(diǎn)擊的時(shí)候都刷新窗口??梢栽贑reateInstance方法中做一些改變。
ParentWindow.cs
public partial class ParentWindow : Window
{
static ParentWindow()
{
// 注冊(cè)無(wú)參構(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);
// 釋放單例實(shí)例并關(guān)閉所有子窗口
WindowSingletonBase<ParentWindow>.ReleaseInstance();
WindowSingletonBase<ParentWindow>.CloseAllChildren();
}
private void OpenChildWindow(object sender, RoutedEventArgs e)
{
// 獲取子窗口實(shí)例并顯示
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()
{
// 注冊(cè)無(wú)參構(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;
// 點(diǎn)擊的是當(dāng)前展示的數(shù)據(jù),則返回當(dāng)前window
if (model != null && model.name == name && model.age == age)
{
return _childWindow;
}
else
{
// 點(diǎn)擊的是另一條數(shù)據(jù),則重新new viewModel,重新設(shè)置 _childWindow的DataContext
// 在這里省略ChildWindowWinViewModel代碼,根據(jù)情況自行編寫(xiě)
_childWindow.viewModel = new ChildWindowWinViewModel(id, instId, _childWindow);
_childWindow.DataContext = _childWindow.viewModel;
return _childWindow;
}
}
// 如果 _childWindow 是空的,則聲明單例窗口。
_childWindow = WindowSingletonBase<ChildWindow>.GetInstance(name, age);
return _childWindow;
}
/// <summary>
/// 關(guān)閉窗口
/// </summary>
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
// 從父窗體的子窗口列表中移除
WindowSingletonBase<ParentWindow>.RemoveChild(this);
WindowSingletonBase<ChildWindow>.ReleaseInstance();
}
}
以上就是WPF中實(shí)現(xiàn)單例窗口的解決方案的詳細(xì)內(nèi)容,更多關(guān)于WPF單例窗口的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
c# 使用計(jì)時(shí)器和觀察者模式實(shí)現(xiàn)報(bào)警推送需求
這篇文章主要介紹了c# 使用計(jì)時(shí)器和觀察者模式實(shí)現(xiàn)報(bào)警推送需求,文中示例代碼非常詳細(xì),幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下2020-07-07
C#中實(shí)現(xiàn)查找字符串中指定字符位置方法小結(jié)
這篇文章主要為大家介紹了C#中實(shí)現(xiàn)查找字符串中指定字符位置的常用方法,本文將以"."字符為例,詳細(xì)講解這些方法的具體使用,需要的可以參考下2024-02-02
C#實(shí)現(xiàn)Winform小數(shù)字鍵盤(pán)模擬器
本文主要介紹了C#實(shí)現(xiàn)Winform小數(shù)字鍵盤(pán)模擬器,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11
C#定時(shí)器實(shí)現(xiàn)自動(dòng)執(zhí)行的方法
這篇文章主要介紹了C#定時(shí)器實(shí)現(xiàn)自動(dòng)執(zhí)行的方法,實(shí)例分析了C#定時(shí)器參數(shù)的設(shè)置及方法的調(diào)用與實(shí)現(xiàn),需要的朋友可以參考下2015-01-01

