From fddab16bd2cff1615b975eb1768d4397eac37b5e Mon Sep 17 00:00:00 2001 From: SugarChes Date: Thu, 3 Sep 2026 14:16:48 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=A8=A1=E5=9D=97=E8=B5=84?= =?UTF-8?q?=E6=BA=90=E9=87=8A=E6=94=BE=E4=B8=8E=E5=BC=82=E6=AD=A5=E9=A2=84?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FrmBaseAccraditation.Designer.cs | 7 +- .../FrmBaseAccraditation.cs | 9 +- 插件库/Lskj.BaseAccraditation/FrmMain.cs | 14 +- 插件库/Lskj.Control/AuditPanelEx.Designer.cs | 5 +- 插件库/Lskj.Control/BandedGridControlEx.cs | 18 +- .../BrowserSetting/BsOpenModuleHandler.cs | 42 +- 插件库/Lskj.Control/FrmAutoSqlLoad.cs | 3 +- 插件库/Lskj.Control/FrmGridSift.Designer.cs | 3 +- 插件库/Lskj.Control/FrmGridSift.cs | 8 +- 插件库/Lskj.Control/GridControlEx.cs | 19 +- .../Model/MenuStrip/BaseMenuStrip.cs | 46 +- 插件库/Lskj.Control/Model/MyControl.cs | 57 +- .../Lskj.Control/Model/StaticBandedControl.cs | 267 +--- 插件库/Lskj.Control/Model/StaticControl.cs | 1168 ++++++++++------- 插件库/Lskj.Control/ModuleGridEx.cs | 8 +- 插件库/Lskj.Control/TabControlEx.cs | 1 + 插件库/Lskj.Help/FrmMain.Designer.cs | 7 +- .../Lskj.Main/Control/MainPanelControlEx.cs | 23 +- 插件库/Lskj.Main/FrmMain.cs | 15 +- 插件库/Lskj.Main/Model/Manager.cs | 243 +++- .../FrmAccraditation.Designer.cs | 11 +- .../Lskj.PubAccraditation/FrmAccraditation.cs | 9 +- 插件库/Lskj.PubAccraditation/FrmMain.cs | 14 +- .../FrmAccraditation.Designer.cs | 9 +- 插件库/Lskj.PubBill/BillModule.cs | 4 +- .../BillModule.Designer.cs | 11 +- .../BillModule.Designer.cs | 13 +- 插件库/Lskj.PubBillConcise/BillModule.cs | 392 +++--- 插件库/Lskj.PubBillInfo/FrmMain.Designer.cs | 9 +- .../BillModule.Designer.cs | 13 +- 插件库/Lskj.PubBillManagement/FrmBill.cs | 2 +- .../BillModule.Designer.cs | 11 +- .../BillModule.Designer.cs | 13 +- 插件库/Lskj.PubBillSpecial/BillModule.cs | 2 +- 插件库/Lskj.Util/TaskUtil.cs | 54 +- 35 files changed, 1553 insertions(+), 977 deletions(-) diff --git a/插件库/Lskj.BaseAccraditation/FrmBaseAccraditation.Designer.cs b/插件库/Lskj.BaseAccraditation/FrmBaseAccraditation.Designer.cs index 3462636..57e1436 100644 --- a/插件库/Lskj.BaseAccraditation/FrmBaseAccraditation.Designer.cs +++ b/插件库/Lskj.BaseAccraditation/FrmBaseAccraditation.Designer.cs @@ -32,10 +32,11 @@ /// private void InitializeComponent() { + this.components = new System.ComponentModel.Container(); this.btnCancel = new DevExpress.XtraEditors.SimpleButton(); this.btnFileManger = new DevExpress.XtraEditors.SimpleButton(); - this.p_menu = new DevExpress.XtraBars.PopupMenu(); - this.barManager1 = new DevExpress.XtraBars.BarManager(); + this.p_menu = new DevExpress.XtraBars.PopupMenu(this.components); + this.barManager1 = new DevExpress.XtraBars.BarManager(this.components); this.barDockControlTop = new DevExpress.XtraBars.BarDockControl(); this.barDockControlBottom = new DevExpress.XtraBars.BarDockControl(); this.barDockControlLeft = new DevExpress.XtraBars.BarDockControl(); @@ -55,7 +56,7 @@ this.gridSign = new Lskj.Control.GridControlEx(); this.pl_bottom = new DevExpress.XtraEditors.PanelControl(); this.btnPrint = new DevExpress.XtraEditors.DropDownButton(); - this.pMprint = new DevExpress.XtraBars.PopupMenu(); + this.pMprint = new DevExpress.XtraBars.PopupMenu(this.components); this.simpleButton1 = new DevExpress.XtraEditors.SimpleButton(); this.pl_right = new DevExpress.XtraEditors.PanelControl(); this.pl_right_bottom = new DevExpress.XtraEditors.PanelControl(); diff --git a/插件库/Lskj.BaseAccraditation/FrmBaseAccraditation.cs b/插件库/Lskj.BaseAccraditation/FrmBaseAccraditation.cs index 34f0b80..3e1ba78 100644 --- a/插件库/Lskj.BaseAccraditation/FrmBaseAccraditation.cs +++ b/插件库/Lskj.BaseAccraditation/FrmBaseAccraditation.cs @@ -537,7 +537,8 @@ namespace Lskj.BaseAccraditation })); cachesDic.AddTask(tab_basicInfo, "DynamicModel", dynamicModelTask); cachesDic.AddTask(tab_basicInfo, "Details", detailTask); - tab_basicInfo.GetDataCaches(cachesDic); + // TabControlEx.GetDataCaches 会创建明细页中的 WinForms/DevExpress + // 控件,不能在此后台预加载阶段调用;BindTab 在 UI 线程补充创建。 //初始化常用工具 Task GetBaseGridRightMenusTask = cachesDic.AddTask(this, "GetBaseGridRightMenus", new Task(() => { @@ -1191,6 +1192,12 @@ namespace Lskj.BaseAccraditation Dictionary dataCaches = SysModel != null ? SysModel.DataCaches : null; this.tab_basicInfo.Model = this.SysModel; this.tab_basicInfo.ParentControlEx = this.ControlObj; + if (dataCaches != null && + dataCaches.GetTask(tab_basicInfo, "DynamicModel") != null && + dataCaches.GetTask>(tab_basicInfo, "Details") != null) + { + tab_basicInfo.GetDataCaches(dataCaches); + } if (!dataCaches.GetValue(tab_basicInfo, "Details", out List details)) { details = GetTabDetails(); diff --git a/插件库/Lskj.BaseAccraditation/FrmMain.cs b/插件库/Lskj.BaseAccraditation/FrmMain.cs index 633ec4d..ab948a5 100644 --- a/插件库/Lskj.BaseAccraditation/FrmMain.cs +++ b/插件库/Lskj.BaseAccraditation/FrmMain.cs @@ -353,11 +353,16 @@ namespace Lskj.BaseAccraditation tp.Controls.Add(frmbaseAccradit); tp.ShowCloseButton = DefaultBoolean.True; tp.Dock = DockStyle.Fill; + StaticControl.ModuleResourceScope moduleScope = + StaticControl.RegisterModulePage(module, tp, frmbaseAccradit); ERPInfo.Instance.AddModulePage(tp); ERPInfo.Instance.CurrentModulePage = tp; frmbaseAccradit.Opacity = 0; frmbaseAccradit.SetFormSize(); - frmbaseAccradit.Show(); + using (StaticControl.PushModuleScope(moduleScope)) + { + frmbaseAccradit.Show(); + } ERPInfo.Instance.ModuleForms.Add(guid, module); } else @@ -477,11 +482,16 @@ namespace Lskj.BaseAccraditation tp.Controls.Add(frmbaseAccradit); tp.ShowCloseButton = DefaultBoolean.True; tp.Dock = DockStyle.Fill; + StaticControl.ModuleResourceScope moduleScope = + StaticControl.RegisterModulePage(module, tp, frmbaseAccradit); ERPInfo.Instance.AddModulePage(tp); ERPInfo.Instance.CurrentModulePage = tp; frmbaseAccradit.Opacity = 0; frmbaseAccradit.SetFormSize(); - frmbaseAccradit.Show(); + using (StaticControl.PushModuleScope(moduleScope)) + { + frmbaseAccradit.Show(); + } ERPInfo.Instance.ModuleForms.Add(guid, module); } else diff --git a/插件库/Lskj.Control/AuditPanelEx.Designer.cs b/插件库/Lskj.Control/AuditPanelEx.Designer.cs index cad81cb..c5573d6 100644 --- a/插件库/Lskj.Control/AuditPanelEx.Designer.cs +++ b/插件库/Lskj.Control/AuditPanelEx.Designer.cs @@ -32,14 +32,15 @@ /// private void InitializeComponent() { + this.components = new System.ComponentModel.Container(); this.label_tips = new DevExpress.XtraEditors.LabelControl(); this.label_date = new DevExpress.XtraEditors.LabelControl(); this.pl_bottom = new DevExpress.XtraEditors.PanelControl(); this.btnReverseSelection = new DevExpress.XtraEditors.SimpleButton(); this.btnSelectAll = new DevExpress.XtraEditors.SimpleButton(); this.dpb_Tools = new DevExpress.XtraEditors.DropDownButton(); - this.p_menu = new DevExpress.XtraBars.PopupMenu(); - this.barManager1 = new DevExpress.XtraBars.BarManager(); + this.p_menu = new DevExpress.XtraBars.PopupMenu(this.components); + this.barManager1 = new DevExpress.XtraBars.BarManager(this.components); this.barDockControlTop = new DevExpress.XtraBars.BarDockControl(); this.barDockControlBottom = new DevExpress.XtraBars.BarDockControl(); this.barDockControlLeft = new DevExpress.XtraBars.BarDockControl(); diff --git a/插件库/Lskj.Control/BandedGridControlEx.cs b/插件库/Lskj.Control/BandedGridControlEx.cs index ab9cb05..d114264 100644 --- a/插件库/Lskj.Control/BandedGridControlEx.cs +++ b/插件库/Lskj.Control/BandedGridControlEx.cs @@ -2747,10 +2747,17 @@ namespace Lskj.Control /// The instance containing the event data. protected virtual void Multifunction(object sender, EventArgs e) { + StaticControl.ModuleResourceScope moduleScope = null; try { //DataTable newDt = this.SetDealWithValueTab(); - FrmGridSift frmGridSift = new FrmGridSift(); + string guid = Guid.NewGuid().ToString(); + moduleScope = new StaticControl.ModuleResourceScope(guid); + FrmGridSift frmGridSift; + using (StaticControl.PushModuleScope(moduleScope)) + { + frmGridSift = new FrmGridSift(); + } frmGridSift.CustomColumKey = this.CustomColumKey; //frmGridSift.source = newDt; frmGridSift.ParentGridEx = this; @@ -2762,7 +2769,6 @@ namespace Lskj.Control BeforePage = ERPInfo.Instance.CurrentModulePage }; module.ModuleForm = frmGridSift; - string guid = Guid.NewGuid().ToString(); DevExpress.XtraTab.XtraTabPage tp = new DevExpress.XtraTab.XtraTabPage(); tp.Tag = guid; // 这个必须有不然会提示:"不能向tabControl中添加顶级控件" @@ -2776,13 +2782,19 @@ namespace Lskj.Control tp.Controls.Add(frmGridSift); tp.ShowCloseButton = DefaultBoolean.True; tp.Dock = DockStyle.Fill; + StaticControl.RegisterModulePage(module, tp, frmGridSift); ERPInfo.Instance.AddModulePage(tp); ERPInfo.Instance.CurrentModulePage = tp; ERPInfo.Instance.ModuleForms.Add(guid, module); - frmGridSift.Show(); + using (StaticControl.PushModuleScope(moduleScope)) + { + frmGridSift.Show(); + } } catch (Exception ex) { + if (moduleScope != null) + moduleScope.Dispose(); string Message = ErrorMessage.PromptErrorMessage(ex, Model.ModuleCode); MessageUtil.Show(Message, ex.Message); } diff --git a/插件库/Lskj.Control/BrowserSetting/BsOpenModuleHandler.cs b/插件库/Lskj.Control/BrowserSetting/BsOpenModuleHandler.cs index 5f6ff0c..2ba20fa 100644 --- a/插件库/Lskj.Control/BrowserSetting/BsOpenModuleHandler.cs +++ b/插件库/Lskj.Control/BrowserSetting/BsOpenModuleHandler.cs @@ -247,13 +247,25 @@ namespace Lskj.Control.BrowserSetting /// The arguments. public static void AddModuleToTab(DllModule module, string[] args) { + StaticControl.ModuleResourceScope moduleScope = null; try { string guid = Guid.NewGuid().ToString(); XtraTabPage tp = new XtraTabPage(); + moduleScope = new StaticControl.ModuleResourceScope(guid); + StaticControl.AttachModuleScope(tp, moduleScope); + moduleScope.Register(tp, () => + { + if (!tp.IsDisposed) + tp.Dispose(); + }); ERPInfo.Instance.selectFormModleId = module.Id; - IForm iform = FormHelper.LoadDllForm(module.DllName, args); + IForm iform; + using (StaticControl.PushModuleScope(moduleScope)) + { + iform = FormHelper.LoadDllForm(module.DllName, args); + } // 菜单打开的模块默认最大化,界面FormState设置为Normal模式,否则会出现界面不是全屏,会显示默认阴影界面 //iform.SubForm.WindowState = _frmMain.WindowState; iform.SubForm.WindowState = FormWindowState.Normal; ; @@ -278,28 +290,26 @@ namespace Lskj.Control.BrowserSetting tp.ShowCloseButton = DefaultBoolean.True; tp.Dock = DockStyle.Fill; - //module.Tag = "1"; - module.Tag = guid; - module.ModuleForm = form; - form.Show(); + StaticControl.RegisterModulePage(module, tp, form); + // OnLoad may create additional non-visual helpers. Keep the + // module scope active until the form has finished loading so + // those helpers are registered with the same page lifetime. + using (StaticControl.PushModuleScope(moduleScope)) + { + form.Show(); + } if (args != null && !string.IsNullOrEmpty(args[4] + "")) { ModuleModel moduleModel = new ModuleModel(MainImpl.GetSystemdllTab(args[4] + "")); if (!string.IsNullOrEmpty(moduleModel.SearchCondFormModuleCode)) { - ModuleConditionsPanelEx mcpex = new ModuleConditionsPanelEx(); - mcpex.InitializeControl(new ModuleModel(MainImpl.GetSystemdllTab(moduleModel.SearchCondFormModuleCode)), null, null, true); - if (StaticControl.ConditionsPanelDic.ContainsKey(args[4] + "")) + using (ModuleConditionsPanelEx mcpex = new ModuleConditionsPanelEx()) { - StaticControl.ConditionsPanelDic[args[4] + ""].Dispose(); - StaticControl.ConditionsPanelDic[args[4] + ""] = mcpex; + mcpex.InitializeControl(new ModuleModel(MainImpl.GetSystemdllTab(moduleModel.SearchCondFormModuleCode)), null, null, true); + StaticControl.RegisterConditionPanel(args[4] + "", mcpex); + DialogResult dialogResult = mcpex.ShowDialog(); } - else - { - StaticControl.ConditionsPanelDic.Add(args[4] + "", mcpex); - } - DialogResult dialogResult = mcpex.ShowDialog(); } } ERPInfo.Instance.AddModulePage(tp); @@ -309,6 +319,8 @@ namespace Lskj.Control.BrowserSetting } catch (Exception ex) { + if (moduleScope != null) + moduleScope.Dispose(); string Message = ErrorMessage.PromptErrorMessage(ex); MessageUtil.Show(ex.InnerException != null ? ex.InnerException.Message : ex.Message); LogHelper.Instance.WriteError(ex); diff --git a/插件库/Lskj.Control/FrmAutoSqlLoad.cs b/插件库/Lskj.Control/FrmAutoSqlLoad.cs index 558ead2..60b19c3 100644 --- a/插件库/Lskj.Control/FrmAutoSqlLoad.cs +++ b/插件库/Lskj.Control/FrmAutoSqlLoad.cs @@ -102,7 +102,8 @@ namespace Lskj.Control this.SizeChanged += OnFrmAutoSqlLoad_SizeChanged; this.topSplitContainerControl.SplitterPositionChanged += OnTopSplitContainerControl_SplitterPositionChanged; this.clearBtn.Click += OnClearBtn_Click;//清除按钮点击 - popupMenu = new DevExpress.XtraBars.PopupMenu(barManager);//右键菜单 + popupMenu = new DevExpress.XtraBars.PopupMenu(this.components);//右键菜单 + popupMenu.Manager = barManager; BarButtonItem barButtonItemFull = new DevExpress.XtraBars.BarButtonItem(); barButtonItemFull.Caption = "全连接"; barButtonItemFull.Name = "join"; diff --git a/插件库/Lskj.Control/FrmGridSift.Designer.cs b/插件库/Lskj.Control/FrmGridSift.Designer.cs index 1a6ca76..75117cd 100644 --- a/插件库/Lskj.Control/FrmGridSift.Designer.cs +++ b/插件库/Lskj.Control/FrmGridSift.Designer.cs @@ -28,7 +28,8 @@ /// private void InitializeComponent() { - this.barManager = new DevExpress.XtraBars.BarManager(); + this.components = new System.ComponentModel.Container(); + this.barManager = new DevExpress.XtraBars.BarManager(this.components); this.barDockControlTop = new DevExpress.XtraBars.BarDockControl(); this.barDockControlBottom = new DevExpress.XtraBars.BarDockControl(); this.barDockControlLeft = new DevExpress.XtraBars.BarDockControl(); diff --git a/插件库/Lskj.Control/FrmGridSift.cs b/插件库/Lskj.Control/FrmGridSift.cs index 129ebeb..6018848 100644 --- a/插件库/Lskj.Control/FrmGridSift.cs +++ b/插件库/Lskj.Control/FrmGridSift.cs @@ -1660,7 +1660,7 @@ namespace Lskj.Control BarButtonItem itemExportDetail = new BarButtonItem(); itemExportDetail.Caption = "导出明细Excel"; itemExportDetail.ItemClick += new ItemClickEventHandler(OnItemExportDetail_ItemClick); - PopupMenu exportPopupMenu = new PopupMenu(); + PopupMenu exportPopupMenu = new PopupMenu(this.components); exportPopupMenu.Manager = this.barManager; exportPopupMenu.AddItems(new BarItem[] { itemExportExcel, itemExportDetail }); this.exportDropBtn.DropDownControl = exportPopupMenu; @@ -1682,7 +1682,8 @@ namespace Lskj.Control #region 初始化统计右键菜单 private void InitResultRightMenu() { - popupMenu = new DevExpress.XtraBars.PopupMenu(barManager); + popupMenu = new DevExpress.XtraBars.PopupMenu(this.components); + popupMenu.Manager = barManager; BarButtonItem barButtonItemSum = new DevExpress.XtraBars.BarButtonItem(); barButtonItemSum.Caption = "求和"; barButtonItemSum.Name = "Sum"; @@ -1704,7 +1705,8 @@ namespace Lskj.Control popupMenu.ItemLinks[i].BeginGroup = true;//设置分割条 popupMenu.ItemLinks[i].Item.ItemClick += OnItem_ItemClick; } - allpopupMenu = new DevExpress.XtraBars.PopupMenu(barManager); + allpopupMenu = new DevExpress.XtraBars.PopupMenu(this.components); + allpopupMenu.Manager = barManager; BarButtonItem barButtonItemAllSum = new DevExpress.XtraBars.BarButtonItem(); barButtonItemAllSum.Caption = "一键求和"; barButtonItemAllSum.Name = "Sum"; diff --git a/插件库/Lskj.Control/GridControlEx.cs b/插件库/Lskj.Control/GridControlEx.cs index e4cecce..2ea9355 100644 --- a/插件库/Lskj.Control/GridControlEx.cs +++ b/插件库/Lskj.Control/GridControlEx.cs @@ -508,6 +508,7 @@ namespace Lskj.Control { SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.Selectable | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.SupportsTransparentBackColor, true); InitializeComponent(); + StaticControl.RegisterOwnedControl(this); this.Disposed += OnGridControlExDisposed; // 自定义选中区域方式 @@ -3940,10 +3941,17 @@ namespace Lskj.Control /// The instance containing the event data. protected virtual void Multifunction(object sender, EventArgs e) { + StaticControl.ModuleResourceScope moduleScope = null; try { //DataTable newDt = this.SetDealWithValueTab(); - FrmGridSift frmGridSift = new FrmGridSift(); + string guid = Guid.NewGuid().ToString(); + moduleScope = new StaticControl.ModuleResourceScope(guid); + FrmGridSift frmGridSift; + using (StaticControl.PushModuleScope(moduleScope)) + { + frmGridSift = new FrmGridSift(); + } frmGridSift.CustomColumKey = this.CustomColumKey; //frmGridSift.source = newDt; frmGridSift.ParentGridEx = this; @@ -3955,7 +3963,6 @@ namespace Lskj.Control BeforePage = ERPInfo.Instance.CurrentModulePage }; module.ModuleForm = frmGridSift; - string guid = Guid.NewGuid().ToString(); DevExpress.XtraTab.XtraTabPage tp = new DevExpress.XtraTab.XtraTabPage(); tp.Tag = guid; // 这个必须有不然会提示:"不能向tabControl中添加顶级控件" @@ -3969,13 +3976,19 @@ namespace Lskj.Control tp.Controls.Add(frmGridSift); tp.ShowCloseButton = DefaultBoolean.True; tp.Dock = DockStyle.Fill; + StaticControl.RegisterModulePage(module, tp, frmGridSift); ERPInfo.Instance.AddModulePage(tp); ERPInfo.Instance.CurrentModulePage = tp; ERPInfo.Instance.ModuleForms.Add(guid, module); - frmGridSift.Show(); + using (StaticControl.PushModuleScope(moduleScope)) + { + frmGridSift.Show(); + } } catch (Exception ex) { + if (moduleScope != null) + moduleScope.Dispose(); string Message = ErrorMessage.PromptErrorMessage(ex, Model.ModuleCode); MessageUtil.Show(Message, ex.Message); } diff --git a/插件库/Lskj.Control/Model/MenuStrip/BaseMenuStrip.cs b/插件库/Lskj.Control/Model/MenuStrip/BaseMenuStrip.cs index 57d1743..95a9330 100644 --- a/插件库/Lskj.Control/Model/MenuStrip/BaseMenuStrip.cs +++ b/插件库/Lskj.Control/Model/MenuStrip/BaseMenuStrip.cs @@ -1330,14 +1330,45 @@ namespace Lskj.Control.Model SmartX1Api.SmartX1Close(keyHandle); } } - IForm form = FormHelper.LoadDllForm(dllName, str); + string guid = isAddPage ? Guid.NewGuid().ToString() : null; + StaticControl.ModuleResourceScope moduleScope = null; + XtraTabPage modulePage = null; + if (isAddPage) + { + modulePage = new XtraTabPage(); + modulePage.Tag = guid; + moduleScope = new StaticControl.ModuleResourceScope(guid); + StaticControl.AttachModuleScope(modulePage, moduleScope); + moduleScope.Register(modulePage, () => + { + if (!modulePage.IsDisposed) + modulePage.Dispose(); + }); + } + + IForm form; + if (moduleScope == null) + { + form = FormHelper.LoadDllForm(dllName, str); + } + else + { + using (StaticControl.PushModuleScope(moduleScope)) + { + form = FormHelper.LoadDllForm(dllName, str); + } + } if (form == null) { + if (moduleScope != null) + moduleScope.Dispose(); MessageUtil.Show(ResourceKeys.DyncmicDllNotFount); return false; } if (form.SubForm == null) { + if (moduleScope != null) + moduleScope.Dispose(); MessageUtil.Show(ResourceKeys.DyncmicDllInitFault); return false; } @@ -1356,7 +1387,7 @@ namespace Lskj.Control.Model form.SubForm.Text = model.MenuName; if (isPrivateDll) { - StaticControl.DogVerifyNoPageForms.Add(form); + StaticControl.RegisterProtectedForm(form); } if (form.SubForm is BaseForm)//dllName.Equals("Lskj.BaseAccraditation.dll", StringComparison.OrdinalIgnoreCase) { @@ -1384,8 +1415,7 @@ namespace Lskj.Control.Model } else { - string guid = Guid.NewGuid().ToString(); - XtraTabPage tp = new XtraTabPage(); + XtraTabPage tp = modulePage; tp.Text = model.MenuName; if (!string.IsNullOrWhiteSpace(model.PageName)) @@ -1403,6 +1433,7 @@ namespace Lskj.Control.Model tp.Controls.Add(form.SubForm); tp.ShowCloseButton = DefaultBoolean.True; tp.Dock = DockStyle.Fill; + StaticControl.RegisterModulePage(module, tp, form.SubForm); //配置了右键刷新的情况下,在当前页签关闭时触发刷新 if (model.Refresh && this.OnRightCallback != null) { @@ -1412,13 +1443,16 @@ namespace Lskj.Control.Model ERPInfo.Instance.CurrentModulePage = tp; if (isPrivateDll) { - StaticControl.DogVerifyModuleForms.Add(tp); + StaticControl.RegisterProtectedPage(tp); } if (form.SubForm is BaseForm) { (form.SubForm as BaseForm).ParentView = this.BaseGridView; } - form.SubForm.Show(); + using (StaticControl.PushModuleScope(moduleScope)) + { + form.SubForm.Show(); + } ERPInfo.Instance.ModuleForms.Add(guid, module); //直接页签打开模块会直接向后面运行,默认返回false避免外面执行右键刷新 return false; diff --git a/插件库/Lskj.Control/Model/MyControl.cs b/插件库/Lskj.Control/Model/MyControl.cs index e9d98b2..46b3058 100644 --- a/插件库/Lskj.Control/Model/MyControl.cs +++ b/插件库/Lskj.Control/Model/MyControl.cs @@ -15,6 +15,7 @@ using Newtonsoft.Json; using System; using System.Collections; using System.Collections.Generic; +using System.ComponentModel; using System.Data; using System.Data.Common; using System.Data.SqlClient; @@ -34,6 +35,13 @@ namespace Lskj.Control.Model /// public class MyControl : IDisposable { + // Runtime menus and managers are owned by this model instance. + private readonly IContainer _components = new Container(); + /// + /// Raised once when this non-visual control model releases its owned + /// resources. Static runtime references use it to clear exact owners. + /// + public event EventHandler Disposed; private const long UserSearchCooldownTicks = TimeSpan.TicksPerMillisecond * 350; private string _searchSql; @@ -54,6 +62,10 @@ namespace Lskj.Control.Model private bool mUserSearchInProgress; private long mLastUserSearchCompletedTicks; private int mAcceptedUserSearchCount; + public bool IsDisposed + { + get { return mDisposed; } + } /// /// 计算字段 /// @@ -112,7 +124,7 @@ namespace Lskj.Control.Model /// public string RefreshControl; protected bool mLoading = false; - protected Timer mTimer = new Timer(); + protected Timer mTimer; /// /// 查询按钮 /// @@ -304,7 +316,8 @@ namespace Lskj.Control.Model /// public MyControl() { - + mTimer = new Timer(_components); + StaticControl.RegisterModuleResource(this, Dispose); } /// /// Initializes a new instance of the class. @@ -315,12 +328,14 @@ namespace Lskj.Control.Model /// The tree view. public MyControl(string searchSql, GridControlEx mainGridEx, TreeViewEx treeView = null, GridControlEx gridLeft = null, MyControl leftSearch = null) { + mTimer = new Timer(_components); this._searchSql = searchSql; this._mainGridEx = mainGridEx; if (this._mainGridEx != null) this._gridControl = _mainGridEx.GridControl; this._tvLeft = treeView; this._gridLeft = gridLeft; this._leftSearch = leftSearch; + StaticControl.RegisterModuleResource(this, mainGridEx, Dispose); } /// /// 说明:设置GridControlEx @@ -336,6 +351,7 @@ namespace Lskj.Control.Model { this._mainGridEx = mainGridEx; if (this._mainGridEx != null) this._gridControl = _mainGridEx.GridControl; + StaticControl.RegisterModuleResource(this, mainGridEx, Dispose); } /// /// 说明:设置GridLeft @@ -1039,7 +1055,7 @@ namespace Lskj.Control.Model { if (_popupMenu == null) { - _popupMenu = new PopupMenu(); + _popupMenu = new PopupMenu(_components); } _dropDown = new DropDownButton(); _dropDown.DropDownArrowStyle = DropDownArrowStyle.Show; @@ -1052,7 +1068,7 @@ namespace Lskj.Control.Model _dropDown.Leave += new EventHandler(OnDropDownLeave); width = _dropDown.Location.X + _dropDown.Width; - BarManager barManager1 = new BarManager(); + BarManager barManager1 = new BarManager(_components); _popupMenu.Manager = barManager1; _popupMenu.Name = "pm_search"; CreateSearchScheme(); @@ -1234,7 +1250,7 @@ namespace Lskj.Control.Model { if (_popupMenu == null) { - _popupMenu = new PopupMenu(); + _popupMenu = new PopupMenu(_components); } _dropDown = new DropDownButton(); _dropDown.DropDownArrowStyle = DropDownArrowStyle.Show; @@ -1247,7 +1263,7 @@ namespace Lskj.Control.Model _dropDown.Leave += new EventHandler(OnDropDownLeave); width = _dropDown.Location.X + _dropDown.Width; - BarManager barManager1 = new BarManager(); + BarManager barManager1 = new BarManager(_components); _popupMenu.Manager = barManager1; _popupMenu.Name = "pm_search"; CreateSearchScheme(); @@ -1778,6 +1794,7 @@ namespace Lskj.Control.Model { timer.Tick -= mTimerTick; timer.Stop(); + _components.Remove(timer); timer.Dispose(); } } @@ -3294,7 +3311,7 @@ namespace Lskj.Control.Model //menu.ApplyBefore += new CommonToolApplyEventHandler(OnMenuApplyBefore); //menu.DynamicLinkAfter += new CommonToolAfterDynamicLinkEventHandler(OnDynamicLinkAfter); //StaticControl.RightMenuGridView = MainGridEx.GridView; - StaticControl.RightMenuMyControl = this; + StaticControl.SetRightMenuMyControl(this); menu.Apply(menuRow); if (!menu.issuccess) { @@ -6833,6 +6850,20 @@ namespace Lskj.Control.Model mDisposed = true; mUserSearchInProgress = false; + EventHandler disposed = Disposed; + Disposed = null; + if (disposed != null) + { + try + { + disposed(this, EventArgs.Empty); + } + catch + { + // Reference cleanup must not interrupt disposal. + } + } + Timer timer = mTimer; mTimer = null; if (timer != null) @@ -6841,6 +6872,7 @@ namespace Lskj.Control.Model { timer.Tick -= mTimerTick; timer.Stop(); + _components.Remove(timer); timer.Dispose(); } catch @@ -6882,17 +6914,10 @@ namespace Lskj.Control.Model } } - BarManager searchSchemeManager = - _popupMenu == null ? null : _popupMenu.Manager; if (_popupMenu != null) { _popupMenu.Manager = null; _popupMenu.ItemLinks.Clear(); - _popupMenu.Dispose(); - } - if (searchSchemeManager != null) - { - searchSchemeManager.Dispose(); } if (_mainGridEx != null && @@ -6937,6 +6962,10 @@ namespace Lskj.Control.Model ModuleResultDic.Clear(); ParaContrlsDic.Clear(); + // PopupMenu, BarManager and any future non-visual components are + // disposed together with this MyControl instance. + _components.Dispose(); + _controls = null; _schemes = null; _fixField = null; diff --git a/插件库/Lskj.Control/Model/StaticBandedControl.cs b/插件库/Lskj.Control/Model/StaticBandedControl.cs index da8b13b..db907e2 100644 --- a/插件库/Lskj.Control/Model/StaticBandedControl.cs +++ b/插件库/Lskj.Control/Model/StaticBandedControl.cs @@ -1,268 +1,55 @@ using DevExpress.XtraGrid.Views.BandedGrid; using DevExpress.XtraGrid.Views.Grid; -using DevExpress.XtraTab; using Lskj.Control.ZKFinger; -using Lskj.Util; -using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Windows.Forms; -using WinFormsControl = System.Windows.Forms.Control; +using Lskj.Util; +using DevExpress.XtraTab; namespace Lskj.Control.Model { + /// + /// Static registries for banded-grid drag helpers. The helpers unregister + /// themselves from these dictionaries when their GridView is disposed; + /// module closing therefore does not need to scan a control tree. + /// public static class StaticBandedControl { - public static void ReleaseModuleReferences(WinFormsControl moduleRoot, XtraTabPage modulePage, string moduleCode) - { - List moduleControls = CollectModuleControls(moduleRoot, modulePage); - ReleaseModuleReferences(moduleControls, modulePage, moduleCode); - } - - internal static void ReleaseModuleReferences( - IEnumerable moduleControls, - XtraTabPage modulePage, - string moduleCode) - { - try - { - List controls = new List(); - if (moduleControls != null) - foreach (WinFormsControl control in moduleControls) - if (control != null && !ContainsControl(controls, control)) - controls.Add(control); - List bandedViews = CollectBandedGridViews(controls); - List targetViews = CollectTargetGridViews(controls); - foreach (BandedGridView view in bandedViews) - BandedGridDragGrid.DisposeRegistrations( - BandedGridViewDragGridDic, - BandedTargetViewDragGridDic, - view, - null); - foreach (GridView view in targetViews) - BandedGridDragGrid.DisposeRegistrations( - BandedGridViewDragGridDic, - BandedTargetViewDragGridDic, - null, - view); - if (ContainsBandedGridView(bandedViews, SourceDragBandedGridView)) - SourceDragBandedGridView = null; - RemoveConditionPanels(moduleCode, controls); - RemoveProtectedReferences(controls, modulePage); - } - catch (Exception exception) - { - System.Diagnostics.Debug.WriteLine("释放旧 Banded 模块引用失败:" + exception); - } - } - - private static List CollectModuleControls( - WinFormsControl moduleRoot, - XtraTabPage modulePage) - { - List result = new List(); - AddModuleControls(result, moduleRoot); - if (!ReferenceEquals(moduleRoot, modulePage)) - AddModuleControls(result, modulePage); - return result; - } - - private static void AddModuleControls( - List target, - WinFormsControl root) - { - if (root == null) - return; - foreach (WinFormsControl control in EnumerateControls(root)) - if (!ContainsControl(target, control)) - target.Add(control); - } - - private static List CollectBandedGridViews( - IEnumerable controls) - { - List result = new List(); - if (controls == null) - return result; - foreach (WinFormsControl control in controls) - { - DevExpress.XtraGrid.GridControl grid = control as DevExpress.XtraGrid.GridControl; - if (grid == null) - continue; - try - { - AddBandedGridView(result, grid.MainView as BandedGridView); - foreach (DevExpress.XtraGrid.Views.Base.BaseView baseView in grid.ViewCollection) - AddBandedGridView(result, baseView as BandedGridView); - } - catch (Exception exception) - { - System.Diagnostics.Debug.WriteLine("读取旧 Banded 表格视图失败:" + exception); - } - } - return result; - } - - private static void AddBandedGridView( - List target, - BandedGridView view) - { - if (view != null && !ContainsBandedGridView(target, view)) - target.Add(view); - } - - private static bool ContainsBandedGridView( - IEnumerable gridViews, - BandedGridView value) - { - if (gridViews == null || value == null) - return false; - foreach (BandedGridView gridView in gridViews) - if (ReferenceEquals(gridView, value)) - return true; - return false; - } - - private static List CollectTargetGridViews( - IEnumerable controls) - { - List result = new List(); - if (controls == null) - return result; - foreach (WinFormsControl control in controls) - { - DevExpress.XtraGrid.GridControl grid = control as DevExpress.XtraGrid.GridControl; - if (grid == null) - continue; - try - { - AddGridView(result, grid.MainView as GridView); - foreach (DevExpress.XtraGrid.Views.Base.BaseView baseView in grid.ViewCollection) - AddGridView(result, baseView as GridView); - } - catch (Exception exception) - { - System.Diagnostics.Debug.WriteLine("读取旧 Banded 目标表格视图失败:" + exception); - } - } - return result; - } - - private static void AddGridView(List target, GridView view) - { - if (view != null && !ContainsGridView(target, view)) - target.Add(view); - } - - private static bool ContainsGridView( - IEnumerable gridViews, - GridView value) - { - if (gridViews == null || value == null) - return false; - foreach (GridView gridView in gridViews) - if (ReferenceEquals(gridView, value)) - return true; - return false; - } - - private static IEnumerable EnumerateControls(WinFormsControl root) - { - yield return root; - foreach (WinFormsControl child in root.Controls) - foreach (WinFormsControl nested in EnumerateControls(child)) - yield return nested; - } - - private static void RemoveConditionPanels( - string moduleCode, - IEnumerable moduleControls) - { - foreach (string key in ConditionsPanelDic.Keys.ToList()) - { - ModuleConditionsPanelEx panel; - if (!ConditionsPanelDic.TryGetValue(key, out panel) || panel == null || panel.IsDisposed) - { - ConditionsPanelDic.Remove(key); - continue; - } - - if (string.Equals(key, moduleCode, StringComparison.OrdinalIgnoreCase) && - IsConditionPanelOwnedByModule(panel, moduleControls)) - ConditionsPanelDic.Remove(key); - } - } - - private static bool IsConditionPanelOwnedByModule( - ModuleConditionsPanelEx panel, - IEnumerable moduleControls) - { - if (panel == null) - return false; - if (ContainsControl(moduleControls, panel) || ContainsControl(moduleControls, panel.Owner)) - return true; - - MyControl parentControl = panel.ParentControlObj; - return parentControl != null && - parentControl.MainGridEx != null && - ContainsControl(moduleControls, parentControl.MainGridEx); - } - - private static void RemoveProtectedReferences( - IEnumerable moduleControls, - XtraTabPage modulePage) - { - while (modulePage != null && DogVerifyModuleForms.Remove(modulePage)) - { - } - foreach (XtraTabPage item in DogVerifyModuleForms.ToList()) - if (item == null || item.IsDisposed || ContainsControl(moduleControls, item)) - DogVerifyModuleForms.Remove(item); - foreach (IForm item in DogVerifyNoPageForms.ToList()) - if (item == null || item.SubForm == null || item.SubForm.IsDisposed || - ContainsControl(moduleControls, item.SubForm)) - DogVerifyNoPageForms.Remove(item); - } - - private static bool ContainsControl( - IEnumerable moduleControls, - WinFormsControl value) - { - if (moduleControls == null || value == null) - return false; - foreach (WinFormsControl control in moduleControls) - if (ReferenceEquals(control, value)) - return true; - return false; - } - /// - /// 记录当前U盾权限窗口 - /// - public static List DogVerifyModuleForms = new List(); - public static List DogVerifyNoPageForms = new List(); - /// - ///模块打开时的条件界面 - /// - public static Dictionary ConditionsPanelDic = new Dictionary(); + // Keep the old public fields as aliases so already deployed plug-ins + // loaded from lib do not fail with MissingFieldException. + [System.Obsolete("请使用 StaticControl.DogVerifyModuleForms")] + public static List DogVerifyModuleForms = + StaticControl.DogVerifyModuleForms; + [System.Obsolete("请使用 StaticControl.DogVerifyNoPageForms")] + public static List DogVerifyNoPageForms = + StaticControl.DogVerifyNoPageForms; + [System.Obsolete("请使用 StaticControl.ConditionsPanelDic")] + public static Dictionary ConditionsPanelDic = + StaticControl.ConditionsPanelDic; /// /// 表格拖拽类,key值为源表,多表格 /// - public static Dictionary> BandedGridViewDragGridDic = new Dictionary>(); + public static Dictionary> + BandedGridViewDragGridDic = + new Dictionary>(); + /// /// 表格拖拽类,key值为目标表 /// - public static Dictionary> BandedTargetViewDragGridDic = new Dictionary>(); + public static Dictionary> + BandedTargetViewDragGridDic = + new Dictionary>(); /// /// 当前拖拽源表格 /// public static BandedGridView SourceDragBandedGridView; + /// /// 指纹识别模块 /// public static FingerHelper fingerHelper; + /// /// 进度条 /// diff --git a/插件库/Lskj.Control/Model/StaticControl.cs b/插件库/Lskj.Control/Model/StaticControl.cs index 1fd8246..ce45a67 100644 --- a/插件库/Lskj.Control/Model/StaticControl.cs +++ b/插件库/Lskj.Control/Model/StaticControl.cs @@ -2,316 +2,636 @@ using DevExpress.XtraGrid.Views.BandedGrid; using DevExpress.XtraTab; using Lskj.Control.ZKFinger; +using Lskj.Model; using Lskj.Util; using System; -using System.Collections; using System.Collections.Generic; using System.Data; -using System.Diagnostics; -using System.Linq; using System.Runtime.CompilerServices; -using System.Text; -using System.Threading; using System.Windows.Forms; -using Lskj.Model; -using System.Reflection; using WinFormsControl = System.Windows.Forms.Control; namespace Lskj.Control.Model { + /// + /// WinForms-owned runtime state. A module registers the objects it creates + /// while it is being opened; closing a module disposes only that module's + /// scope. This deliberately has no control-tree traversal. + /// public static class StaticControl { - /// - /// 在窗体关闭前捕获模块控件树及其 GridView。WinForms 的 Close/Dispose - /// 流程可能会先清空 Controls 或重建 GridView;保留这个快照可以让后续 - /// 引用解绑仍然针对原始实例执行。 - /// - public sealed class ModuleReferenceSnapshot - { - internal readonly List Controls; - internal readonly List GridViews; - - internal ModuleReferenceSnapshot( - List controls, - List gridViews) - { - Controls = controls ?? new List(); - GridViews = gridViews ?? new List(); - } - } - - public static ModuleReferenceSnapshot CaptureModuleReferences( - WinFormsControl moduleRoot, - XtraTabPage modulePage) - { - List moduleControls = new List(); - try - { - moduleControls = CollectModuleControls(moduleRoot, modulePage); - } - catch (Exception exception) - { - TraceCleanupFailure("收集模块控件引用", exception); - } - - List gridViews = new List(); - try - { - gridViews = CollectGridViews(moduleControls); - } - catch (Exception exception) - { - TraceCleanupFailure("收集模块表格引用", exception); - } - return new ModuleReferenceSnapshot(moduleControls, gridViews); - } + private static readonly object ModuleRegistrySync = new object(); /// - /// Releases static references owned by a closed legacy module. Cleanup is - /// best-effort so it cannot change the module close result. + /// Owns cleanup callbacks for one opened module instance. /// - public static void ReleaseModuleReferences(WinFormsControl moduleRoot, XtraTabPage modulePage, string moduleCode) + public sealed class ModuleResourceScope : IDisposable { - ReleaseModuleReferences( - CaptureModuleReferences(moduleRoot, modulePage), - modulePage, - moduleCode); - } + private sealed class Registration + { + internal readonly object Owner; + internal readonly Action Cleanup; - /// - /// Releases references using a snapshot captured before Close/Dispose. - /// This overload is used by the WPF host, where the legacy form can be - /// detached from its Controls collection during Form.Close. - /// - public static void ReleaseModuleReferences( - ModuleReferenceSnapshot snapshot, - XtraTabPage modulePage, - string moduleCode) - { - List moduleControls = snapshot == null || snapshot.Controls == null - ? new List() - : snapshot.Controls; - List gridViews = snapshot == null || snapshot.GridViews == null - ? new List() - : snapshot.GridViews; - List tabControls = new List(); - try - { - tabControls = moduleControls.OfType().ToList(); - } - catch (Exception exception) - { - TraceCleanupFailure("收集模块引用", exception); - } - - try - { - // WPF 承载旧 WinForms 页面时,移除页签不一定会同步触发 - // TabControlEx.Disposed。先解除左右联动事件,避免 GridView - // 的事件委托继续闭包持有已经关闭的 TabControlEx/模块。 - foreach (TabControlEx tabControl in tabControls) + internal Registration(object owner, Action cleanup) { + Owner = owner; + Cleanup = cleanup; + } + } + + private readonly object _sync = new object(); + private readonly List _registrations = + new List(); + private bool _disposed; + + public ModuleResourceScope(string moduleId) + { + ModuleId = moduleId ?? string.Empty; + TrackModuleScope(this); + } + + public string ModuleId { get; private set; } + + public bool IsDisposed + { + get + { + lock (_sync) + { + return _disposed; + } + } + } + + public void Register(object owner, Action cleanup) + { + if (cleanup == null) + return; + + bool runImmediately = false; + lock (_sync) + { + if (_disposed) + runImmediately = true; + else + _registrations.Add(new Registration(owner, cleanup)); + } + + if (runImmediately) + InvokeCleanup(cleanup); + } + + public void Unregister(object owner) + { + lock (_sync) + { + for (int index = _registrations.Count - 1; index >= 0; index--) + if (ReferenceEquals(_registrations[index].Owner, owner)) + _registrations.RemoveAt(index); + } + } + + public void Dispose() + { + List registrations; + lock (_sync) + { + if (_disposed) + return; + + _disposed = true; + registrations = new List(_registrations); + _registrations.Clear(); + } + + // Reverse order mirrors normal ownership: the last registered + // helper is released before its owner. + for (int index = registrations.Count - 1; index >= 0; index--) + InvokeCleanup(registrations[index].Cleanup); + + UntrackModuleScope(this); + } + + private static void InvokeCleanup(Action cleanup) + { + try + { + cleanup(); + } + catch (Exception exception) + { + System.Diagnostics.Debug.WriteLine( + "释放模块登记资源失败:" + exception); + } + } + } + + private static void TrackModuleScope(ModuleResourceScope scope) + { + if (scope == null) + return; + + lock (ActiveModuleScopesSync) + { + if (!ActiveModuleScopes.Contains(scope)) + ActiveModuleScopes.Add(scope); + } + } + + private static void UntrackModuleScope(ModuleResourceScope scope) + { + if (scope == null) + return; + + lock (ActiveModuleScopesSync) + { + ActiveModuleScopes.Remove(scope); + } + } + + /// + /// Releases scopes created by legacy page-open paths that are not + /// present in Manager.ModuleScopes. Dispose is idempotent, so this is + /// safe to call after the managed module close pass. + /// + public static void CompleteAllModuleResourceScopes() + { + ModuleResourceScope[] scopes; + lock (ActiveModuleScopesSync) + { + scopes = ActiveModuleScopes.ToArray(); + } + + foreach (ModuleResourceScope scope in scopes) + { + if (scope != null) + scope.Dispose(); + } + } + + private sealed class ScopeCookie : IDisposable + { + private readonly ModuleResourceScope _previous; + private bool _disposed; + + internal ScopeCookie(ModuleResourceScope previous) + { + _previous = previous; + } + + public void Dispose() + { + if (_disposed) + return; + + _disposed = true; + _currentModuleScope = _previous; + } + } + + [ThreadStatic] + private static ModuleResourceScope _currentModuleScope; + + // The table does not keep a disposed control alive. A short parent + // chain lookup is used only for controls created after PushModuleScope; + // there is never a global scan of the module's control tree. + private static readonly ConditionalWeakTable + ModuleScopeOwners = + new ConditionalWeakTable(); + private static readonly object ActiveModuleScopesSync = new object(); + private static readonly List ActiveModuleScopes = + new List(); + private static readonly Dictionary ModuleSearchControls = + new Dictionary(StringComparer.OrdinalIgnoreCase); + + public static ModuleResourceScope CurrentModuleScope + { + get { return _currentModuleScope; } + } + + public static IDisposable PushModuleScope(ModuleResourceScope scope) + { + ModuleResourceScope previous = _currentModuleScope; + _currentModuleScope = scope != null && !scope.IsDisposed ? scope : null; + return new ScopeCookie(previous); + } + + private static ModuleResourceScope GetLiveCurrentModuleScope() + { + return _currentModuleScope != null && !_currentModuleScope.IsDisposed + ? _currentModuleScope + : null; + } + + public static void AttachModuleScope( + WinFormsControl owner, + ModuleResourceScope scope) + { + if (owner == null || scope == null || scope.IsDisposed) + return; + + ModuleScopeOwners.Remove(owner); + ModuleScopeOwners.Add(owner, scope); + } + + /// + /// Finds a scope attached to the owner or one of its parents without + /// falling back to the calling thread's current scope. + /// + public static ModuleResourceScope FindAttachedModuleScope( + WinFormsControl owner) + { + ModuleResourceScope scope = FindParentModuleScope(owner); + if (scope != null) + return scope; + + if (owner != null && ModuleScopeOwners.TryGetValue(owner, out scope) && + scope != null && !scope.IsDisposed) + return scope; + return null; + } + + public static ModuleResourceScope FindModuleScope(WinFormsControl owner) + { + ModuleResourceScope parentScope = FindParentModuleScope(owner); + if (parentScope != null) + return parentScope; + + ModuleResourceScope scope; + if (owner != null && ModuleScopeOwners.TryGetValue(owner, out scope) && + scope != null && !scope.IsDisposed) + return scope; + return _currentModuleScope != null && !_currentModuleScope.IsDisposed + ? _currentModuleScope + : null; + } + + /// + /// Registers a page created by a legacy caller that cannot go through + /// Manager.AddModuleToTab. The page GUID is made the module GUID as + /// well, and the page/form roots are attached to one scope so the + /// normal close path can discover and dispose that scope. + /// + public static ModuleResourceScope RegisterModulePage( + DllModule module, + XtraTabPage page, + Form form) + { + if (page == null) + return null; + + string moduleId = Convert.ToString(page.Tag); + if (string.IsNullOrWhiteSpace(moduleId)) + { + moduleId = Guid.NewGuid().ToString(); + page.Tag = moduleId; + } + + if (module != null) + { + module.Tag = moduleId; + if (form != null) + module.ModuleForm = form; + } + + ModuleResourceScope scope = FindAttachedModuleScope(form) ?? + FindAttachedModuleScope(page); + if (scope == null) + scope = new ModuleResourceScope(moduleId); + + AttachModuleScope(page, scope); + AttachModuleScope(form, scope); + scope.Register(page, () => + { + if (!page.IsDisposed) + page.Dispose(); + }); + if (form != null) + { + scope.Register(form, () => + { + if (!form.IsDisposed) + form.Dispose(); + }); + } + return scope; + } + + public static void RegisterModuleResource( + object owner, + Action cleanup) + { + RegisterModuleResource(owner, owner as WinFormsControl, cleanup); + } + + public static void RegisterModuleResource( + object owner, + WinFormsControl context, + Action cleanup) + { + ModuleResourceScope scope = null; + if (context != null) + scope = FindModuleScope(context); + if (scope == null) + scope = GetLiveCurrentModuleScope(); + if (scope != null) + scope.Register(owner, cleanup); + } + + /// + /// Registers one visual control as owned by its current module. A + /// ParentChanged subscription updates ownership when the same control + /// is explicitly moved to another live module; it never walks children. + /// + public static void RegisterOwnedControl(WinFormsControl owner) + { + if (owner == null) + return; + + // A legacy control can be constructed before it is parented. Keep + // one exact ParentChanged hook so ownership is registered as soon + // as it joins a module, including controls prepared off the UI path. + owner.ParentChanged -= OnOwnedControlParentChanged; + owner.ParentChanged += OnOwnedControlParentChanged; + + ModuleResourceScope scope = GetLiveCurrentModuleScope(); + if (scope == null) + scope = FindParentModuleScope(owner); + if (scope == null) + return; + + ModuleResourceScope existing; + if (ModuleScopeOwners.TryGetValue(owner, out existing) && + ReferenceEquals(existing, scope)) + { + return; + } + + AttachModuleScope(owner, scope); + scope.Register(owner, () => + { + // A complete subtree may have been moved to another module + // without changing every descendant's Parent. Follow the live + // parent scope and leave that subtree owned by the open module. + ModuleResourceScope parentScope = FindParentModuleScope(owner); + if (parentScope != null && !ReferenceEquals(parentScope, scope)) + { + ModuleResourceScope previous = _currentModuleScope; + _currentModuleScope = parentScope; try { - tabControl?.ReleaseRuntimeReferences(); + RegisterOwnedControl(owner); } - catch (Exception exception) + finally { - TraceCleanupFailure("释放多页签运行时引用", exception); + _currentModuleScope = previous; } + return; } - // GridControlEx 自身也会把事件处理器、右键菜单和父控件 - // 引用挂在 GridView/菜单对象上。这里只处理关闭前快照中的 - // 控件,不调用 Control.Dispose,也不扫描全局控件集合,因而 - // 不会影响仍然打开的模块或共享的 GridView 生命周期。 - ReleaseGridControlRuntimeReferences(moduleControls); - ReleaseLookupControlRuntimeReferences(moduleControls); - - foreach (GridView gridView in gridViews) + ModuleResourceScope current; + if (!ModuleScopeOwners.TryGetValue(owner, out current) || + !ReferenceEquals(current, scope)) { - try - { - ReleaseGridViewReferences(gridView); - } - catch (Exception exception) - { - TraceCleanupFailure("释放表格拖拽引用", exception); - } + return; } - TryCleanup( - () => RemoveConditionPanels(moduleCode, moduleControls), - "释放条件面板引用"); - TryCleanup( - () => RemoveProtectedReferences(moduleControls, modulePage), - "释放权限保护引用"); - TryCleanup( - () => ClearRightMenuReferences(moduleControls, gridViews), - "释放右键菜单引用"); - TryCleanup( - () => StaticBandedControl.ReleaseModuleReferences(moduleControls, modulePage, moduleCode), - "释放带状表格引用"); - } - finally - { - // Only remove cache entries owned by this module. The cached - // values are owned by their module/control and must not be - // disposed from a global static cleanup routine. - TryCleanup( - () => RemoveModuleCacheReferences(moduleControls, gridViews), - "移除动态数据缓存引用"); - } + owner.ParentChanged -= OnOwnedControlParentChanged; + ModuleScopeOwners.Remove(owner); + if (!owner.IsDisposed) + owner.Dispose(); + }); } - private static void TryCleanup(Action cleanup, string operation) + private static ModuleResourceScope FindParentModuleScope(WinFormsControl owner) { - try + for (WinFormsControl current = owner == null ? null : owner.Parent; + current != null; + current = current.Parent) { - cleanup(); - } - catch (Exception exception) - { - TraceCleanupFailure(operation, exception); + ModuleResourceScope scope; + if (ModuleScopeOwners.TryGetValue(current, out scope) && + scope != null && !scope.IsDisposed) + return scope; } + return null; } - private static void TraceCleanupFailure(string operation, Exception exception) + private static void OnOwnedControlParentChanged(object sender, EventArgs args) { - Debug.WriteLine("释放旧模块引用失败[" + operation + "]:" + exception); - } - - private static void ReleaseGridControlRuntimeReferences( - IEnumerable moduleControls) - { - if (moduleControls == null) + WinFormsControl owner = sender as WinFormsControl; + if (owner == null || owner.IsDisposed) return; - HashSet released = - new HashSet(ReferenceEqualityComparer.Instance); - foreach (GridControlEx gridControl in moduleControls.OfType()) + ModuleResourceScope parentScope = FindParentModuleScope(owner); + ModuleResourceScope currentScope; + if (parentScope != null && + (!ModuleScopeOwners.TryGetValue(owner, out currentScope) || + !ReferenceEquals(parentScope, currentScope))) { - if (gridControl == null || !released.Add(gridControl)) - continue; - + ModuleResourceScope previous = _currentModuleScope; + _currentModuleScope = parentScope; try { - gridControl.ReleaseResourcesForDispose(); + RegisterOwnedControl(owner); } - catch (Exception exception) + finally { - TraceCleanupFailure("释放表格控件运行时引用", exception); + _currentModuleScope = previous; } } } - private static void ReleaseLookupControlRuntimeReferences( - IEnumerable moduleControls) + public static void RegisterModuleSearchControl( + WinFormsControl context, + MyControl searchControl) { - if (moduleControls == null) - { - return; - } - - foreach (Lskj.Control.LabelMultiAutoTextEdit4 lookupControl in - moduleControls.OfType()) - { - if (lookupControl == null) - { - continue; - } - - try - { - lookupControl.ReleaseLookupFormForDispose(); - } - catch (Exception exception) - { - TraceCleanupFailure("release lookup helper form", exception); - } - } - } - - private static List CollectModuleControls( - WinFormsControl moduleRoot, - XtraTabPage modulePage) - { - List result = new List(); - AddModuleControls(result, moduleRoot); - if (!ReferenceEquals(moduleRoot, modulePage)) - AddModuleControls(result, modulePage); - return result; - } - - private static void AddModuleControls( - List target, - WinFormsControl root) - { - if (root == null) + if (searchControl == null) return; - foreach (WinFormsControl control in EnumerateControls(root)) - if (!ContainsControl(target, control)) - target.Add(control); - } + ModuleResourceScope scope = context == null + ? GetLiveCurrentModuleScope() + : FindModuleScope(context); + if (scope == null || string.IsNullOrWhiteSpace(scope.ModuleId)) + return; - private static List CollectGridViews(IEnumerable controls) - { - List result = new List(); - if (controls == null) - return result; - - foreach (WinFormsControl control in controls) + string moduleId = scope.ModuleId; + lock (ModuleRegistrySync) { - DevExpress.XtraGrid.GridControl grid = control as DevExpress.XtraGrid.GridControl; - if (grid == null) - continue; - - try + MyControl existing; + if (ModuleSearchControls.TryGetValue(moduleId, out existing)) { - GridView mainView = grid.MainView as GridView; - AddGridView(result, mainView); - - // A GridControl can retain detail views in ViewCollection even - // when they are not the current MainView. - foreach (DevExpress.XtraGrid.Views.Base.BaseView baseView in grid.ViewCollection) - AddGridView(result, baseView as GridView); - } - catch (Exception exception) - { - // A form can already be partially disposed when the shell - // releases its static references. Keep the views collected - // from the remaining controls. - TraceCleanupFailure("读取表格视图", exception); + if (ReferenceEquals(existing, searchControl)) + return; + if (existing != null && !existing.IsDisposed) + return; + ModuleSearchControls.Remove(moduleId); } + ModuleSearchControls[moduleId] = searchControl; } - return result; + EventHandler disposed = null; + disposed = (sender, args) => + { + searchControl.Disposed -= disposed; + lock (ModuleRegistrySync) + { + MyControl current; + if (ModuleSearchControls.TryGetValue(moduleId, out current) && + ReferenceEquals(current, searchControl)) + { + ModuleSearchControls.Remove(moduleId); + } + } + }; + searchControl.Disposed += disposed; + scope.Register(searchControl, () => + { + searchControl.Disposed -= disposed; + lock (ModuleRegistrySync) + { + MyControl current; + if (ModuleSearchControls.TryGetValue(moduleId, out current) && + ReferenceEquals(current, searchControl)) + { + ModuleSearchControls.Remove(moduleId); + } + } + }); } - private static void AddGridView(List target, GridView view) + public static MyControl FindModuleSearchControl(string moduleId) { - if (view != null && !ContainsGridView(target, view)) - target.Add(view); + if (string.IsNullOrWhiteSpace(moduleId)) + return null; + + lock (ModuleRegistrySync) + { + MyControl searchControl; + if (!ModuleSearchControls.TryGetValue(moduleId, out searchControl)) + return null; + if (searchControl == null || searchControl.IsDisposed) + { + ModuleSearchControls.Remove(moduleId); + return null; + } + return searchControl; + } } - private static bool ContainsGridView( - IEnumerable gridViews, - GridView value) + public static void RegisterConditionPanel( + string key, + ModuleConditionsPanelEx panel) { - if (gridViews == null || value == null) - return false; - foreach (GridView gridView in gridViews) - if (ReferenceEquals(gridView, value)) - return true; - return false; + if (panel == null) + return; + + key = key ?? string.Empty; + lock (ModuleRegistrySync) + { + ModuleConditionsPanelEx previous; + if (ConditionsPanelDic.TryGetValue(key, out previous) && + previous != null && previous.IsDisposed) + { + ConditionsPanelDic.Remove(key); + } + + // A key can be reused by a second module instance. Never + // dispose the first live dialog from the second registration. + ConditionsPanelDic[key] = panel; + } + EventHandler disposed = null; + disposed = (sender, args) => + { + panel.Disposed -= disposed; + lock (ModuleRegistrySync) + { + ModuleConditionsPanelEx current; + if (ConditionsPanelDic.TryGetValue(key, out current) && + ReferenceEquals(current, panel)) + { + ConditionsPanelDic.Remove(key); + } + } + }; + panel.Disposed += disposed; + RegisterModuleResource(panel, panel, () => + { + panel.Disposed -= disposed; + lock (ModuleRegistrySync) + { + ModuleConditionsPanelEx current; + if (ConditionsPanelDic.TryGetValue(key, out current) && + ReferenceEquals(current, panel)) + { + ConditionsPanelDic.Remove(key); + } + } + if (!panel.IsDisposed) + { + panel.Dispose(); + } + }); } - private static IEnumerable EnumerateControls(WinFormsControl root) + public static void RegisterProtectedPage(XtraTabPage page) { - yield return root; - foreach (WinFormsControl child in root.Controls) - foreach (WinFormsControl nested in EnumerateControls(child)) - yield return nested; + if (page == null) + return; + lock (ModuleRegistrySync) + { + if (!DogVerifyModuleForms.Contains(page)) + DogVerifyModuleForms.Add(page); + } + + EventHandler disposed = null; + disposed = (sender, args) => + { + page.Disposed -= disposed; + lock (ModuleRegistrySync) + { + DogVerifyModuleForms.Remove(page); + } + }; + page.Disposed += disposed; + RegisterModuleResource(page, () => + { + page.Disposed -= disposed; + lock (ModuleRegistrySync) + { + DogVerifyModuleForms.Remove(page); + } + }); + } + + public static void RegisterProtectedForm(IForm form) + { + if (form == null || form.SubForm == null) + return; + lock (ModuleRegistrySync) + { + if (!DogVerifyNoPageForms.Contains(form)) + DogVerifyNoPageForms.Add(form); + } + + Form subForm = form.SubForm; + EventHandler disposed = null; + disposed = (sender, args) => + { + subForm.Disposed -= disposed; + lock (ModuleRegistrySync) + { + DogVerifyNoPageForms.Remove(form); + } + }; + subForm.Disposed += disposed; + RegisterModuleResource(subForm, () => + { + subForm.Disposed -= disposed; + lock (ModuleRegistrySync) + { + DogVerifyNoPageForms.Remove(form); + } + }); } internal static void ReleaseGridViewReferences(GridView gridView) @@ -319,11 +639,10 @@ namespace Lskj.Control.Model if (gridView == null) return; - GridDragGrid.DisposeRegistrations(GridViewDragGridDic, TargetViewDragGridDic, gridView); - // BandedGridDragGrid uses a separate source dictionary but shares - // the GridView target dictionary. Clean both sides whenever a - // GridView is released so control-level Dispose also removes - // banded drag helpers without disposing the view itself. + GridDragGrid.DisposeRegistrations( + GridViewDragGridDic, + TargetViewDragGridDic, + gridView); BandedGridDragGrid.DisposeRegistrations( StaticBandedControl.BandedGridViewDragGridDic, StaticBandedControl.BandedTargetViewDragGridDic, @@ -334,226 +653,58 @@ namespace Lskj.Control.Model RightMenuGridView = null; } - private static void RemoveConditionPanels( - string moduleCode, - IEnumerable moduleControls) + private static void OnRightMenuGridViewDisposed(object sender, EventArgs args) { - foreach (string key in ConditionsPanelDic.Keys.ToList()) - { - ModuleConditionsPanelEx panel; - if (!ConditionsPanelDic.TryGetValue(key, out panel) || panel == null || panel.IsDisposed) - { - ConditionsPanelDic.Remove(key); - continue; - } - - // The dictionary is keyed only by module code. Two instances of - // the same module can therefore share that key. Remove a live - // panel only when its owner can be tied to the closing instance; - // otherwise leave it for the still-open module. - if (string.Equals(key, moduleCode, StringComparison.OrdinalIgnoreCase) && - IsConditionPanelOwnedByModule(panel, moduleControls)) - ConditionsPanelDic.Remove(key); - } - } - - private static bool IsConditionPanelOwnedByModule( - ModuleConditionsPanelEx panel, - IEnumerable moduleControls) - { - if (panel == null) - return false; - if (ContainsControl(moduleControls, panel) || ContainsControl(moduleControls, panel.Owner)) - return true; - - MyControl parentControl = panel.ParentControlObj; - return parentControl != null && - parentControl.MainGridEx != null && - ContainsControl(moduleControls, parentControl.MainGridEx); - } - - private static void RemoveProtectedReferences( - IEnumerable moduleControls, - XtraTabPage page) - { - while (page != null && DogVerifyModuleForms.Remove(page)) - { - } - foreach (XtraTabPage item in DogVerifyModuleForms.ToList()) - if (item == null || item.IsDisposed || ContainsControl(moduleControls, item)) - DogVerifyModuleForms.Remove(item); - foreach (IForm item in DogVerifyNoPageForms.ToList()) - if (item == null || item.SubForm == null || item.SubForm.IsDisposed || - ContainsControl(moduleControls, item.SubForm)) - DogVerifyNoPageForms.Remove(item); - } - - private static bool ContainsControl( - IEnumerable moduleControls, - WinFormsControl value) - { - if (moduleControls == null || value == null) - return false; - foreach (WinFormsControl control in moduleControls) - if (ReferenceEquals(control, value)) - return true; - return false; - } - - private static void ClearRightMenuReferences( - IEnumerable moduleControls, - List gridViews) - { - if (_RightMenuGridView != null && ContainsGridView(gridViews, _RightMenuGridView)) + GridView view = sender as GridView; + if (ReferenceEquals(_RightMenuGridView, view)) RightMenuGridView = null; - if (RightMenuMyControl != null && RightMenuMyControl.MainGridEx != null && - (RightMenuMyControl.MainGridEx.IsDisposed || - ContainsControl(moduleControls, RightMenuMyControl.MainGridEx))) - RightMenuMyControl = null; - if (BomUnionPage != null && - (BomUnionPage.IsDisposed || ContainsControl(moduleControls, BomUnionPage))) - BomUnionPage = null; - if (AddParentGrid.Value != null && - (AddParentGrid.Value.IsDisposed || ContainsControl(moduleControls, AddParentGrid.Value))) - AddParentGrid = new KeyValuePair(); } - private static void RemoveModuleCacheReferences( - IEnumerable moduleControls, - IEnumerable gridViews) + private static void OnBomUnionPageDisposed(object sender, EventArgs args) { - if (moduleControls == null) - return; - - HashSet moduleReferences = new HashSet(ReferenceEqualityComparer.Instance); - foreach (WinFormsControl control in moduleControls) - if (control != null) - moduleReferences.Add(control); - if (gridViews != null) - foreach (GridView gridView in gridViews) - if (gridView != null) - moduleReferences.Add(gridView); - - HashSet> moduleCaches = - new HashSet>(ReferenceEqualityComparer>.Instance); - HashSet moduleModels = - new HashSet(ReferenceEqualityComparer.Instance); - foreach (WinFormsControl control in moduleControls.ToList()) - { - try - { - CollectDynamicModels(control, moduleCaches, moduleModels); - } - catch (Exception exception) - { - TraceCleanupFailure("收集动态缓存引用", exception); - } - } - - foreach (DynamicModel model in moduleModels) - if (model != null) - moduleReferences.Add(model); - - // A DataCaches dictionary is normally module-local, but the legacy - // code can share one dictionary between the form, its child controls - // and model objects. Never clear a whole dictionary here: a module. - // can be opening concurrently and may not have been registered in - // ERPInfo.ModuleForms yet. Removing only keys that belong to the - // closing controls/views keeps every other module's cache intact. - foreach (Dictionary caches in moduleCaches) - { - try - { - RemoveModuleCacheEntries(caches, moduleReferences); - } - catch (Exception exception) - { - TraceCleanupFailure("移除动态缓存引用", exception); - } - } - - foreach (DynamicModel model in moduleModels) - if (model != null && model.DataCaches != null) - model.DataCaches = null; + XtraTabPage page = sender as XtraTabPage; + if (ReferenceEquals(BomUnionPage, page)) + SetBomUnionPage(null); } - private static void CollectDynamicModels( - object instance, - HashSet> caches, - HashSet models) + private static void OnRightMenuMyControlDisposed(object sender, EventArgs args) { - if (instance == null || caches == null || models == null) - return; - BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; - for (Type type = instance.GetType(); type != null; type = type.BaseType) - foreach (FieldInfo field in type.GetFields(flags | BindingFlags.DeclaredOnly)) - try - { - DynamicModel model = field.GetValue(instance) as DynamicModel; - if (model == null || model.DataCaches == null) - continue; - - caches.Add(model.DataCaches); - models.Add(model); - } - catch (Exception exception) - { - Debug.WriteLine("收集动态缓存字段失败:" + exception); - } + MyControl control = sender as MyControl; + if (ReferenceEquals(RightMenuMyControl, control)) + SetRightMenuMyControl(null); } - private static void RemoveModuleCacheEntries( - Dictionary caches, - HashSet moduleReferences) + private static void OnAddParentGridDisposed(object sender, EventArgs args) { - if (caches == null || moduleReferences == null || moduleReferences.Count == 0) - return; - - // DataCaches is shared by several controls in a module. Remove only - // entries keyed by controls/views in the closing module and leave - // all cached values untouched. Their actual owner decides when and - // how to dispose them. - lock (caches) - { - foreach (object key in caches.Keys.Cast().ToList()) - if (moduleReferences.Contains(key)) - caches.Remove(key); - } + GridControlEx grid = sender as GridControlEx; + if (ReferenceEquals(AddParentGrid.Value, grid)) + SetAddParentGrid(new KeyValuePair()); } - private sealed class ReferenceEqualityComparer : IEqualityComparer - where T : class - { - public static readonly ReferenceEqualityComparer Instance = - new ReferenceEqualityComparer(); - - public bool Equals(T x, T y) - { - return ReferenceEquals(x, y); - } - - public int GetHashCode(T value) - { - return value == null ? 0 : RuntimeHelpers.GetHashCode(value); - } - } /// /// 记录当前U盾权限窗口 /// - public static List DogVerifyModuleForms = new List(); - public static List DogVerifyNoPageForms = new List(); + public static List DogVerifyModuleForms = + new List(); + public static List DogVerifyNoPageForms = + new List(); + /// - ///模块打开时的条件界面 + /// 模块打开时的条件界面 /// - public static Dictionary ConditionsPanelDic = new Dictionary(); + public static Dictionary ConditionsPanelDic = + new Dictionary(); + /// /// 表格拖拽类,key值为源表 /// - public static Dictionary> GridViewDragGridDic = new Dictionary>(); + public static Dictionary> GridViewDragGridDic = + new Dictionary>(); /// /// 表格拖拽类,key值为目标表 /// - public static Dictionary> TargetViewDragGridDic = new Dictionary>(); + public static Dictionary> TargetViewDragGridDic = + new Dictionary>(); /// /// 当前拖拽源表格 /// @@ -562,40 +713,127 @@ namespace Lskj.Control.Model /// 指纹识别模块 /// public static FingerHelper fingerHelper; - ///// - ///// 外部exe进程 - ///// - //public static Dictionary loadExeProcess = new Dictionary(); - /// - /// 打开的Add模块对应的父表格控件(模块编号,GridControlEx) - /// - public static KeyValuePair AddParentGrid = new KeyValuePair(); + + public static KeyValuePair AddParentGrid = + new KeyValuePair(); + public static void SetAddParentGrid( + KeyValuePair value) + { + if (ReferenceEquals(AddParentGrid.Value, value.Value) && + string.Equals(AddParentGrid.Key, value.Key, StringComparison.Ordinal)) + { + return; + } + + GridControlEx previous = AddParentGrid.Value; + if (previous != null) + previous.Disposed -= OnAddParentGridDisposed; + + AddParentGrid = value; + GridControlEx current = value.Value; + if (current == null) + return; + + current.Disposed += OnAddParentGridDisposed; + RegisterModuleResource(current, () => + { + current.Disposed -= OnAddParentGridDisposed; + if (ReferenceEquals(AddParentGrid.Value, current)) + AddParentGrid = new KeyValuePair(); + }); + } + /// /// 当前右键点击时的表格 /// public static GridView _RightMenuGridView; - public static GridView RightMenuGridView + public static GridView RightMenuGridView { - get - { - return StaticControl._RightMenuGridView; - } + get { return _RightMenuGridView; } set { - StaticControl._RightMenuGridView = value; - StaticControl.Comprefix = ""; - StaticControl.ReturnRowLisy.Clear(); + if (ReferenceEquals(_RightMenuGridView, value)) + { + Comprefix = string.Empty; + ReturnRowLisy.Clear(); + return; + } + + if (_RightMenuGridView != null) + _RightMenuGridView.Disposed -= OnRightMenuGridViewDisposed; + + _RightMenuGridView = value; + Comprefix = string.Empty; + ReturnRowLisy.Clear(); + if (value == null) + return; + + value.Disposed += OnRightMenuGridViewDisposed; + RegisterModuleResource( + value, + value.GridControl as WinFormsControl, + () => + { + value.Disposed -= OnRightMenuGridViewDisposed; + if (ReferenceEquals(_RightMenuGridView, value)) + _RightMenuGridView = null; + }); } - } + /// /// bom附件修改时的关联页签 /// public static XtraTabPage BomUnionPage; + public static void SetBomUnionPage(XtraTabPage value) + { + if (ReferenceEquals(BomUnionPage, value)) + return; + + if (BomUnionPage != null) + BomUnionPage.Disposed -= OnBomUnionPageDisposed; + + BomUnionPage = value; + if (value == null) + return; + + value.Disposed += OnBomUnionPageDisposed; + RegisterModuleResource(value, () => + { + value.Disposed -= OnBomUnionPageDisposed; + if (ReferenceEquals(BomUnionPage, value)) + BomUnionPage = null; + }); + } + /// /// 当前右键点击时的控件对象 /// public static MyControl RightMenuMyControl; + public static void SetRightMenuMyControl(MyControl value) + { + if (ReferenceEquals(RightMenuMyControl, value)) + return; + + if (RightMenuMyControl != null) + RightMenuMyControl.Disposed -= OnRightMenuMyControlDisposed; + + RightMenuMyControl = value; + if (value == null) + return; + + value.Disposed += OnRightMenuMyControlDisposed; + RegisterModuleResource( + value, + value.MainGridEx, + () => + { + value.Disposed -= OnRightMenuMyControlDisposed; + if (ReferenceEquals(RightMenuMyControl, value)) + RightMenuMyControl = null; + }); + } + /// /// 当前右键点击时的表格的列前缀 /// @@ -604,7 +842,5 @@ namespace Lskj.Control.Model /// 浏览器返回数据 /// public static List ReturnRowLisy = new List(); - - } } diff --git a/插件库/Lskj.Control/ModuleGridEx.cs b/插件库/Lskj.Control/ModuleGridEx.cs index f4489be..f2dc07d 100644 --- a/插件库/Lskj.Control/ModuleGridEx.cs +++ b/插件库/Lskj.Control/ModuleGridEx.cs @@ -595,6 +595,7 @@ namespace Lskj.Control out this.MrpClickMenus); } this.SearchObj = searchObj; + StaticControl.RegisterModuleSearchControl(this, searchObj); // 搜索区域复用当前模块配置,MyControl 内部会校验模块号,不一致时仍按原逻辑查询。 this.SearchObj.SystemModel = this.SysModel; this.SearchObj.MrpClickMenus = this.MrpClickMenus; @@ -2410,7 +2411,7 @@ namespace Lskj.Control /// private void AddPanelViewRecord() { - StaticControl.AddParentGrid = new KeyValuePair(Model.ModuleCode, gcMain); + StaticControl.SetAddParentGrid(new KeyValuePair(Model.ModuleCode, gcMain)); string mLeftFieldId = string.Empty; string mLeftFieldName = string.Empty; string mLeftTreeData = string.Empty; @@ -3375,7 +3376,7 @@ namespace Lskj.Control { try { - StaticControl.AddParentGrid = new KeyValuePair(Model.ModuleCode, gcMain); + StaticControl.SetAddParentGrid(new KeyValuePair(Model.ModuleCode, gcMain)); if (!string.IsNullOrWhiteSpace(this.SysModel.MenuAddName)) { string modifyCond = this.SysModel.ModifyCond; @@ -3786,6 +3787,7 @@ namespace Lskj.Control public ModuleGridEx() { InitializeComponent(); + StaticControl.RegisterOwnedControl(this); InitializeAltButtonShortcuts(); this.OperShortMode = true; this.Disposed += OnModuleGridExDisposed; @@ -3838,7 +3840,7 @@ namespace Lskj.Control searchObj.OnDataSourceBindCallBack -= OnMainSearchDataSourceBindCallBack; if (ReferenceEquals(StaticControl.RightMenuMyControl, searchObj)) { - StaticControl.RightMenuMyControl = null; + StaticControl.SetRightMenuMyControl(null); } searchObj.Dispose(); } diff --git a/插件库/Lskj.Control/TabControlEx.cs b/插件库/Lskj.Control/TabControlEx.cs index 7d7503d..6de034e 100644 --- a/插件库/Lskj.Control/TabControlEx.cs +++ b/插件库/Lskj.Control/TabControlEx.cs @@ -194,6 +194,7 @@ namespace Lskj.Control public TabControlEx() { InitializeComponent(); + StaticControl.RegisterOwnedControl(this); this.Disposed += OnTabControlExDisposed; } #region ▼ 通用工具 ▼ diff --git a/插件库/Lskj.Help/FrmMain.Designer.cs b/插件库/Lskj.Help/FrmMain.Designer.cs index 367e457..67d59c6 100644 --- a/插件库/Lskj.Help/FrmMain.Designer.cs +++ b/插件库/Lskj.Help/FrmMain.Designer.cs @@ -28,13 +28,14 @@ /// private void InitializeComponent() { - this.pm_print = new DevExpress.XtraBars.PopupMenu(); - this.barManager1 = new DevExpress.XtraBars.BarManager(); + this.components = new System.ComponentModel.Container(); + this.pm_print = new DevExpress.XtraBars.PopupMenu(this.components); + this.barManager1 = new DevExpress.XtraBars.BarManager(this.components); this.barDockControlTop = new DevExpress.XtraBars.BarDockControl(); this.barDockControlBottom = new DevExpress.XtraBars.BarDockControl(); this.barDockControlLeft = new DevExpress.XtraBars.BarDockControl(); this.barDockControlRight = new DevExpress.XtraBars.BarDockControl(); - this.pm_oper = new DevExpress.XtraBars.PopupMenu(); + this.pm_oper = new DevExpress.XtraBars.PopupMenu(this.components); this.panelControl1 = new DevExpress.XtraEditors.PanelControl(); this.gcMain = new Lskj.Control.GridControlEx(); this.btnDownLoad = new DevExpress.XtraEditors.SimpleButton(); diff --git a/插件库/Lskj.Main/Control/MainPanelControlEx.cs b/插件库/Lskj.Main/Control/MainPanelControlEx.cs index b9ae326..0c89dd8 100644 --- a/插件库/Lskj.Main/Control/MainPanelControlEx.cs +++ b/插件库/Lskj.Main/Control/MainPanelControlEx.cs @@ -3245,8 +3245,11 @@ namespace Lskj.Main.Control if (Manager.ModuleForms.ContainsKey(guid)) { DllModule m = Manager.ModuleForms[guid]; + Manager.ModuleCloseSnapshot closeSnapshot = + Manager.PrepareModuleClose(m, item); tabMain.TabPages.Remove(item); - Manager.ModuleForms.Remove(m.Tag + ""); + Manager.ModuleForms.Remove(guid); + Manager.CompleteModuleClose(closeSnapshot); } } } @@ -3821,10 +3824,12 @@ namespace Lskj.Main.Control string guid = tabPage.Tag + ""; if (!string.IsNullOrWhiteSpace(guid)) { - DllModule m = Manager.ModuleForms[guid]; - if (Manager.ModuleForms.ContainsKey(guid)) + DllModule m; + if (Manager.ModuleForms.TryGetValue(guid, out m) && m != null) { Form form = m.ModuleForm as Form; + Manager.ModuleCloseSnapshot closeSnapshot = + Manager.PrepareModuleClose(m, tabPage); if (form != null) { form.Close(); @@ -3850,7 +3855,8 @@ namespace Lskj.Main.Control { if (this.PreviousTab != null) this.tabMain.SelectedTabPage = this.PreviousTab; tabMain.TabPages.Remove(tabPage); - Manager.ModuleForms.Remove(m.Tag + ""); + Manager.ModuleForms.Remove(guid); + Manager.CompleteModuleClose(closeSnapshot); LogUtil.WriteDebug(m.Code, "关闭模块", m.Name, m.Name, m.Id); } @@ -3956,10 +3962,12 @@ namespace Lskj.Main.Control string guid = page.Tag + ""; if (!string.IsNullOrWhiteSpace(guid)) { - DllModule m = Manager.ModuleForms[guid]; - if (Manager.ModuleForms.ContainsKey(guid)) + DllModule m; + if (Manager.ModuleForms.TryGetValue(guid, out m) && m != null) { Form form = m.ModuleForm as Form; + Manager.ModuleCloseSnapshot closeSnapshot = + Manager.PrepareModuleClose(m, page); if (form != null) form.Close(); bool isclose = true; @@ -3975,7 +3983,8 @@ namespace Lskj.Main.Control { if (this.PreviousTab != null) this.tabMain.SelectedTabPage = this.PreviousTab; tabMain.TabPages.Remove(page); - Manager.ModuleForms.Remove(m.Tag + ""); + Manager.ModuleForms.Remove(guid); + Manager.CompleteModuleClose(closeSnapshot); LogUtil.WriteDebug(m.Code, "关闭模块", m.Name, m.Name, m.Id); } } diff --git a/插件库/Lskj.Main/FrmMain.cs b/插件库/Lskj.Main/FrmMain.cs index 50ea5e8..b725343 100644 --- a/插件库/Lskj.Main/FrmMain.cs +++ b/插件库/Lskj.Main/FrmMain.cs @@ -338,17 +338,24 @@ namespace Lskj.Main int closeCount = 0; if (StaticControl.DogVerifyModuleForms.Count > 0) { - foreach (DevExpress.XtraTab.XtraTabPage page in StaticControl.DogVerifyModuleForms) + foreach (DevExpress.XtraTab.XtraTabPage page in + new List(StaticControl.DogVerifyModuleForms)) { this.ActiveMainPanel.TabMain.Invoke(new Action(() => { try { + string moduleId = Convert.ToString(page.Tag); + DllModule module; + Manager.ModuleForms.TryGetValue(moduleId, out module); + Manager.ModuleCloseSnapshot closeSnapshot = + Manager.PrepareModuleClose(module, page); if (this.ActiveMainPanel.TabMain.TabPages.Contains(page)) { this.ActiveMainPanel.TabMain.TabPages.Remove(page); - page.Dispose(); } + Manager.ModuleForms.Remove(moduleId); + Manager.CompleteModuleClose(closeSnapshot); } catch (Exception) { @@ -360,7 +367,8 @@ namespace Lskj.Main } if (StaticControl.DogVerifyNoPageForms.Count > 0) { - foreach (IForm iForm in StaticControl.DogVerifyNoPageForms) + foreach (IForm iForm in + new List(StaticControl.DogVerifyNoPageForms)) { try { @@ -580,6 +588,7 @@ namespace Lskj.Main try { CloseWatermarkForm(); + Manager.CompleteAllModuleCloses(); Manager.ModuleForms.Clear(); } catch (Exception ex) diff --git a/插件库/Lskj.Main/Model/Manager.cs b/插件库/Lskj.Main/Model/Manager.cs index bab4e66..4c75c78 100644 --- a/插件库/Lskj.Main/Model/Manager.cs +++ b/插件库/Lskj.Main/Model/Manager.cs @@ -52,6 +52,170 @@ namespace Lskj.Main.Model DeferredForWpf } + /// + /// WinForms-owned module lifetime token. Resources are registered while + /// the module is opened and released from this scope on close. + /// + public sealed class ModuleCloseSnapshot + { + internal readonly string ModuleId; + internal readonly XtraTabPage Page; + internal readonly Form Form; + internal readonly StaticControl.ModuleResourceScope Scope; + + internal ModuleCloseSnapshot( + string moduleId, + XtraTabPage page, + Form form, + StaticControl.ModuleResourceScope scope) + { + ModuleId = moduleId ?? string.Empty; + Page = page; + Form = form; + Scope = scope; + } + } + + /// + /// Prepares a close token without inspecting the hosted control tree. + /// The module scope has already registered its resources during open; + /// the fallback registration only covers legacy callers that create a + /// page outside AddModuleToTab. + /// + public static ModuleCloseSnapshot PrepareModuleClose( + DllModule module, + XtraTabPage page) + { + Form form = module == null ? null : module.ModuleForm as Form; + string moduleId = module == null + ? Convert.ToString(page == null ? null : page.Tag) + : Convert.ToString(module.Tag); + // Legacy callers historically set only XtraTabPage.Tag. Prefer the + // page key when the module object has not been assigned its GUID so + // each close operation resolves its own scope and dictionary entry. + if (string.IsNullOrWhiteSpace(moduleId)) + moduleId = Convert.ToString(page == null ? null : page.Tag); + moduleId = moduleId ?? string.Empty; + if (module != null && !string.IsNullOrWhiteSpace(moduleId)) + module.Tag = moduleId; + StaticControl.ModuleResourceScope scope; + bool created = false; + lock (ModuleScopesSync) + { + if (!ModuleScopes.TryGetValue(moduleId, out scope) || scope == null) + { + scope = StaticControl.FindAttachedModuleScope(form) ?? + StaticControl.FindAttachedModuleScope(page); + if (scope == null) + { + scope = new StaticControl.ModuleResourceScope(moduleId); + created = true; + } + ModuleScopes[moduleId] = scope; + } + } + if (created) + { + StaticControl.AttachModuleScope(form, scope); + StaticControl.AttachModuleScope(page, scope); + RegisterOwnedModuleObjects(scope, form, page); + } + return new ModuleCloseSnapshot(moduleId, page, form, scope); + } + + /// + /// Completes a previously prepared module close by disposing its + /// registration scope. No control-tree traversal is performed here. + /// + public static void CompleteModuleClose(ModuleCloseSnapshot snapshot) + { + if (snapshot == null) + return; + + try + { + if (snapshot.Scope != null) + snapshot.Scope.Dispose(); + } + catch (Exception exception) + { + LogHelper.Instance.WriteError(exception); + } + lock (ModuleScopesSync) + { + StaticControl.ModuleResourceScope current; + if (ModuleScopes.TryGetValue(snapshot.ModuleId ?? string.Empty, out current) && + ReferenceEquals(current, snapshot.Scope)) + { + ModuleScopes.Remove(snapshot.ModuleId ?? string.Empty); + } + } + } + + public static void CompleteAllModuleCloses() + { + List scopes; + lock (ModuleScopesSync) + { + scopes = ModuleScopes.Values.Where(scope => scope != null).Distinct().ToList(); + ModuleScopes.Clear(); + } + foreach (StaticControl.ModuleResourceScope scope in scopes) + { + try + { + scope.Dispose(); + } + catch (Exception exception) + { + LogHelper.Instance.WriteError(exception); + } + } + + // Legacy page-open paths create scopes directly and therefore do + // not enter ModuleScopes. Release those scopes during the global + // shutdown pass as well. + StaticControl.CompleteAllModuleResourceScopes(); + } + + private static void RegisterOwnedModuleObjects( + StaticControl.ModuleResourceScope scope, + Form form, + XtraTabPage page) + { + if (scope == null) + return; + + if (form != null) + { + scope.Register(form, () => + { + if (!form.IsDisposed) + form.Dispose(); + }); + } + if (page != null) + { + scope.Register(page, () => + { + if (!page.IsDisposed) + page.Dispose(); + }); + } + } + + /// + /// Finds the search control registered by the module during creation. + /// The lookup is direct and does not inspect the WinForms control tree. + /// + public static MyControl FindModuleSearchControl(string moduleId) + { + if (string.IsNullOrWhiteSpace(moduleId)) + return null; + + return StaticControl.FindModuleSearchControl(moduleId); + } + /// /// 主程序 /// @@ -91,6 +255,12 @@ namespace Lskj.Main.Model /// public static Dictionary ModuleForms = new Dictionary(); /// + /// 每个已打开模块的资源登记作用域。键与 ModuleForms 中的页签标识一致。 + /// + private static readonly Dictionary ModuleScopes = + new Dictionary(StringComparer.OrdinalIgnoreCase); + private static readonly object ModuleScopesSync = new object(); + /// /// 记录F4配置快速菜单模块 /// public static Form QuickMenuForm = null; @@ -513,7 +683,30 @@ namespace Lskj.Main.Model return false; if (!form.Visible) - form.Show(); + { + StaticControl.ModuleResourceScope scope; + lock (ModuleScopesSync) + { + ModuleScopes.TryGetValue(moduleId, out scope); + } + if (scope != null && scope.IsDisposed) + return false; + + // DeferredForWpf returns before Form.OnLoad. Re-establish the + // module scope for activation so MyControl and its Timer are + // registered even though the original open call has returned. + if (scope == null) + { + form.Show(); + } + else + { + using (StaticControl.PushModuleScope(scope)) + { + form.Show(); + } + } + } if (form.IsDisposed) return false; @@ -601,6 +794,8 @@ namespace Lskj.Main.Model string guid = null; XtraTabPage tp = null; Form form = null; + StaticControl.ModuleResourceScope moduleScope = null; + IDisposable moduleScopeContext = null; bool trackDogVerifiedModule = false; try { @@ -701,10 +896,22 @@ namespace Lskj.Main.Model } guid = Guid.NewGuid().ToString(); + moduleScope = new StaticControl.ModuleResourceScope(guid); + lock (ModuleScopesSync) + { + ModuleScopes[guid] = moduleScope; + } + moduleScopeContext = StaticControl.PushModuleScope(moduleScope); tp = new XtraTabPage(); + StaticControl.AttachModuleScope(tp, moduleScope); + moduleScope.Register(tp, () => + { + if (!tp.IsDisposed) + tp.Dispose(); + }); if (trackDogVerifiedModule) { - StaticControl.DogVerifyModuleForms.Add(tp); + StaticControl.RegisterProtectedPage(tp); } ERPInfo.Instance.selectFormModleId = module.Id; @@ -715,6 +922,12 @@ namespace Lskj.Main.Model //iform.SubForm.WindowState = _frmMain.WindowState; iform.SubForm.WindowState = FormWindowState.Normal; ; form = iform.SubForm; // 表示组成应用程序的用户界面的窗口或对话框。 + StaticControl.AttachModuleScope(form, moduleScope); + moduleScope.Register(form, () => + { + if (!form.IsDisposed) + form.Dispose(); + }); form.Tag = guid; // 这个必须有不然会提示:"不能向tabControl中添加顶级控件" form.TopLevel = isQuickLoad; @@ -792,12 +1005,22 @@ namespace Lskj.Main.Model } catch (Exception ex) { + if (moduleScopeContext != null) + { + moduleScopeContext.Dispose(); + moduleScopeContext = null; + } CleanupFailedModuleOpen(guid, tp, form); string Message = ErrorMessage.PromptErrorMessage(ex); MessageUtil.Show(ex.InnerException != null ? ex.InnerException.Message : ex.Message); LogHelper.Instance.WriteError(ex); //LogUtil.WriteError("打开" + module.Name + "模块失败!", ex); } + finally + { + if (moduleScopeContext != null) + moduleScopeContext.Dispose(); + } } private static void CleanupFailedModuleOpen( @@ -812,19 +1035,31 @@ namespace Lskj.Main.Model ModuleForms.Remove(guid); } + StaticControl.ModuleResourceScope moduleScope = null; + if (!string.IsNullOrWhiteSpace(guid)) + { + lock (ModuleScopesSync) + { + if (ModuleScopes.TryGetValue(guid, out moduleScope)) + ModuleScopes.Remove(guid); + } + if (moduleScope != null) + moduleScope.Dispose(); + } + if (tabPage != null) { StaticControl.DogVerifyModuleForms.Remove(tabPage); ERPInfo.Instance.RemoveModulePage(tabPage); } - if (form != null && !form.IsDisposed) + if (moduleScope == null && form != null && !form.IsDisposed) { form.Close(); form.Dispose(); } - if (tabPage != null && !tabPage.IsDisposed) + if (moduleScope == null && tabPage != null && !tabPage.IsDisposed) { tabPage.Dispose(); } diff --git a/插件库/Lskj.PubAccraditation/FrmAccraditation.Designer.cs b/插件库/Lskj.PubAccraditation/FrmAccraditation.Designer.cs index 2eb5e06..33ab0b4 100644 --- a/插件库/Lskj.PubAccraditation/FrmAccraditation.Designer.cs +++ b/插件库/Lskj.PubAccraditation/FrmAccraditation.Designer.cs @@ -32,13 +32,14 @@ /// private void InitializeComponent() { - this.pMenu = new DevExpress.XtraBars.PopupMenu(); - this.barManager1 = new DevExpress.XtraBars.BarManager(); + this.components = new System.ComponentModel.Container(); + this.pMenu = new DevExpress.XtraBars.PopupMenu(this.components); + this.barManager1 = new DevExpress.XtraBars.BarManager(this.components); this.barDockControlTop = new DevExpress.XtraBars.BarDockControl(); this.barDockControlBottom = new DevExpress.XtraBars.BarDockControl(); this.barDockControlLeft = new DevExpress.XtraBars.BarDockControl(); this.barDockControlRight = new DevExpress.XtraBars.BarDockControl(); - this.pMprint = new DevExpress.XtraBars.PopupMenu(); + this.pMprint = new DevExpress.XtraBars.PopupMenu(this.components); this.pl_right_top = new DevExpress.XtraEditors.PanelControl(); this.gridFlow = new Lskj.Control.GridControlEx(); this.label_flow = new DevExpress.XtraEditors.LabelControl(); @@ -75,8 +76,8 @@ this.label_managerAudit = new Lskj.Control.LabelTextEdit(); this.CheckSuggestion = new Lskj.Control.LabelComboxEdit(); this.dbpFP = new DevExpress.XtraEditors.DropDownButton(); - this.pmFP = new DevExpress.XtraBars.PopupMenu(); - this.barManager2 = new DevExpress.XtraBars.BarManager(); + this.pmFP = new DevExpress.XtraBars.PopupMenu(this.components); + this.barManager2 = new DevExpress.XtraBars.BarManager(this.components); this.barDockControl1 = new DevExpress.XtraBars.BarDockControl(); this.barDockControl2 = new DevExpress.XtraBars.BarDockControl(); this.barDockControl3 = new DevExpress.XtraBars.BarDockControl(); diff --git a/插件库/Lskj.PubAccraditation/FrmAccraditation.cs b/插件库/Lskj.PubAccraditation/FrmAccraditation.cs index 13b5206..21690cc 100644 --- a/插件库/Lskj.PubAccraditation/FrmAccraditation.cs +++ b/插件库/Lskj.PubAccraditation/FrmAccraditation.cs @@ -449,7 +449,8 @@ namespace Lskj.PubAccraditation })); cachesDic.AddTask(tab_basicInfo, "DynamicModel", dynamicModelTask); cachesDic.AddTask(tab_basicInfo, "Details", detailTask); - tab_basicInfo.GetDataCaches(cachesDic); + // TabControlEx.GetDataCaches 会创建明细页中的 WinForms/DevExpress + // 控件,不能在此后台预加载阶段调用;BindTab 在 UI 线程补充创建。 //BindBill Task controlLocationTask = cachesDic.AddTask(this, "ControlLocation", new Task(() => { @@ -975,6 +976,12 @@ namespace Lskj.PubAccraditation { this.tab_basicInfo.OnDetailSaveGridCallBack += Tab_basicInfo_OnDetailSaveGridCallBack; } + if (dataCaches != null && + dataCaches.GetTask(tab_basicInfo, "DynamicModel") != null && + dataCaches.GetTask>(tab_basicInfo, "Details") != null) + { + tab_basicInfo.GetDataCaches(dataCaches); + } if (!dataCaches.GetValue(tab_basicInfo, "Details", out List details)) { details = GetTabDetails(); diff --git a/插件库/Lskj.PubAccraditation/FrmMain.cs b/插件库/Lskj.PubAccraditation/FrmMain.cs index 48a57b6..0c08508 100644 --- a/插件库/Lskj.PubAccraditation/FrmMain.cs +++ b/插件库/Lskj.PubAccraditation/FrmMain.cs @@ -295,9 +295,14 @@ namespace Lskj.PubAccraditation tp.Controls.Add(frm); tp.ShowCloseButton = DefaultBoolean.True; tp.Dock = DockStyle.Fill; + StaticControl.ModuleResourceScope moduleScope = + StaticControl.RegisterModulePage(module, tp, frm); ERPInfo.Instance.AddModulePage(tp); ERPInfo.Instance.CurrentModulePage = tp; - frm.Show(); + using (StaticControl.PushModuleScope(moduleScope)) + { + frm.Show(); + } tp.Text = frm.Text; ERPInfo.Instance.ModuleForms.Add(guid, module); } @@ -382,9 +387,14 @@ namespace Lskj.PubAccraditation tp.Controls.Add(frm); tp.ShowCloseButton = DefaultBoolean.True; tp.Dock = DockStyle.Fill; + StaticControl.ModuleResourceScope moduleScope = + StaticControl.RegisterModulePage(module, tp, frm); ERPInfo.Instance.AddModulePage(tp); ERPInfo.Instance.CurrentModulePage = tp; - frm.Show(); + using (StaticControl.PushModuleScope(moduleScope)) + { + frm.Show(); + } tp.Text = frm.Text; ERPInfo.Instance.ModuleForms.Add(guid, module); diff --git a/插件库/Lskj.PubAccraditation2/FrmAccraditation.Designer.cs b/插件库/Lskj.PubAccraditation2/FrmAccraditation.Designer.cs index e2cd6e5..fe62c01 100644 --- a/插件库/Lskj.PubAccraditation2/FrmAccraditation.Designer.cs +++ b/插件库/Lskj.PubAccraditation2/FrmAccraditation.Designer.cs @@ -28,6 +28,7 @@ /// private void InitializeComponent() { + this.components = new System.ComponentModel.Container(); this.splitMain = new DevExpress.XtraEditors.SplitContainerControl(); this.TabControl = new DevExpress.XtraTab.XtraTabControl(); this.MainTab = new DevExpress.XtraTab.XtraTabPage(); @@ -54,12 +55,12 @@ this.btnExport = new DevExpress.XtraEditors.SimpleButton(); this.btnSpare = new DevExpress.XtraEditors.SimpleButton(); this.btnPrint = new DevExpress.XtraEditors.DropDownButton(); - this.pMprint = new DevExpress.XtraBars.PopupMenu(); - this.barManager1 = new DevExpress.XtraBars.BarManager(); + this.pMprint = new DevExpress.XtraBars.PopupMenu(this.components); + this.barManager1 = new DevExpress.XtraBars.BarManager(this.components); this.btnSave = new DevExpress.XtraEditors.SimpleButton(); this.btnAttach = new DevExpress.XtraEditors.SimpleButton(); this.dpb_Tools = new DevExpress.XtraEditors.DropDownButton(); - this.pMenu = new DevExpress.XtraBars.PopupMenu(); + this.pMenu = new DevExpress.XtraBars.PopupMenu(this.components); this.btnClose = new DevExpress.XtraEditors.SimpleButton(); this.btnBack = new DevExpress.XtraEditors.SimpleButton(); this.btnAccradit = new DevExpress.XtraEditors.SimpleButton(); @@ -619,4 +620,4 @@ private DevExpress.XtraEditors.XtraScrollableControl pl_left_top; private DevExpress.XtraEditors.SimpleButton btnExport; } -} \ No newline at end of file +} diff --git a/插件库/Lskj.PubBill/BillModule.cs b/插件库/Lskj.PubBill/BillModule.cs index 2b0cfe2..7b6b78f 100644 --- a/插件库/Lskj.PubBill/BillModule.cs +++ b/插件库/Lskj.PubBill/BillModule.cs @@ -4186,7 +4186,7 @@ namespace Lskj.PubBill if (pageControl.Tag is GridDetailModel detailModel && (detailModel.Orderid + "").Equals(movepage)) { this.tb_buttom.TabControlObj.SelectedTabPage = xtraTabPage; - StaticControl.BomUnionPage = xtraTabPage; + StaticControl.SetBomUnionPage(xtraTabPage); if (xtraTabPage.Tag is GridControlEx) { DataRow dataRow = this.gcMain.GetViewFocusedDataRow(); @@ -6186,7 +6186,7 @@ namespace Lskj.PubBill menu.DynamicLinkAfter += new CommonToolAfterDynamicLinkEventHandler(OnDynamicLinkAfter); StaticControl.RightMenuGridView = gcMain.GridView; StaticControl.Comprefix = this.BillModel.DetailPreFix; - StaticControl.RightMenuMyControl = ControlObj; + StaticControl.SetRightMenuMyControl(ControlObj); menu.Apply(menuRow); if (!menu.issuccess) { diff --git a/插件库/Lskj.PubBillAccount/BillModule.Designer.cs b/插件库/Lskj.PubBillAccount/BillModule.Designer.cs index 6111155..0b47bd3 100644 --- a/插件库/Lskj.PubBillAccount/BillModule.Designer.cs +++ b/插件库/Lskj.PubBillAccount/BillModule.Designer.cs @@ -38,15 +38,16 @@ /// private void InitializeComponent() { - this.barManager1 = new DevExpress.XtraBars.BarManager(); + this.components = new System.ComponentModel.Container(); + this.barManager1 = new DevExpress.XtraBars.BarManager(this.components); this.barDockControl1 = new DevExpress.XtraBars.BarDockControl(); this.barDockControl2 = new DevExpress.XtraBars.BarDockControl(); this.barDockControl3 = new DevExpress.XtraBars.BarDockControl(); this.barDockControl5 = new DevExpress.XtraBars.BarDockControl(); - this.pmBillExp = new DevExpress.XtraBars.PopupMenu(); - this.pmBill = new DevExpress.XtraBars.PopupMenu(); - this.pMprint = new DevExpress.XtraBars.PopupMenu(); - this.pMenu = new DevExpress.XtraBars.PopupMenu(); + this.pmBillExp = new DevExpress.XtraBars.PopupMenu(this.components); + this.pmBill = new DevExpress.XtraBars.PopupMenu(this.components); + this.pMprint = new DevExpress.XtraBars.PopupMenu(this.components); + this.pMenu = new DevExpress.XtraBars.PopupMenu(this.components); this.ssc_main_top = new DevExpress.XtraEditors.SplitContainerControl(); this.pl_left = new DevExpress.XtraEditors.PanelControl(); this.xtc_left_container = new DevExpress.XtraTab.XtraTabControl(); diff --git a/插件库/Lskj.PubBillConcise/BillModule.Designer.cs b/插件库/Lskj.PubBillConcise/BillModule.Designer.cs index 607072a..ba00cfc 100644 --- a/插件库/Lskj.PubBillConcise/BillModule.Designer.cs +++ b/插件库/Lskj.PubBillConcise/BillModule.Designer.cs @@ -28,6 +28,7 @@ /// private void InitializeComponent() { + this.components = new System.ComponentModel.Container(); this.ssc_main = new DevExpress.XtraEditors.SplitContainerControl(); this.ssc_main_top = new DevExpress.XtraEditors.SplitContainerControl(); this.pl_left = new DevExpress.XtraEditors.PanelControl(); @@ -39,7 +40,7 @@ this.pl_treeview_detault_search = new DevExpress.XtraEditors.PanelControl(); this.panelControl4 = new DevExpress.XtraEditors.PanelControl(); this.textEdit1 = new DevExpress.XtraEditors.TextEdit(); - this.barManager1 = new DevExpress.XtraBars.BarManager(); + this.barManager1 = new DevExpress.XtraBars.BarManager(this.components); this.barDockControl1 = new DevExpress.XtraBars.BarDockControl(); this.barDockControl2 = new DevExpress.XtraBars.BarDockControl(); this.barDockControl3 = new DevExpress.XtraBars.BarDockControl(); @@ -49,7 +50,7 @@ this.btnTreeSearch = new DevExpress.XtraEditors.SimpleButton(); this.pl_main_bottom = new DevExpress.XtraEditors.PanelControl(); this.dbpFP = new DevExpress.XtraEditors.DropDownButton(); - this.pmFP = new DevExpress.XtraBars.PopupMenu(); + this.pmFP = new DevExpress.XtraBars.PopupMenu(this.components); this.btnRefresh = new DevExpress.XtraEditors.SimpleButton(); this.btnHiding = new DevExpress.XtraEditors.SimpleButton(); this.btnHelp = new DevExpress.XtraEditors.SimpleButton(); @@ -62,15 +63,15 @@ this.labelControl2 = new DevExpress.XtraEditors.LabelControl(); this.btn_input = new DevExpress.XtraEditors.SimpleButton(); this.dpbImport = new DevExpress.XtraEditors.DropDownButton(); - this.pmBillExp = new DevExpress.XtraBars.PopupMenu(); + this.pmBillExp = new DevExpress.XtraBars.PopupMenu(this.components); this.btnBillAdd = new DevExpress.XtraEditors.SimpleButton(); this.btnBillApply = new DevExpress.XtraEditors.SimpleButton(); this.dpCopyBill = new DevExpress.XtraEditors.DropDownButton(); - this.pmBill = new DevExpress.XtraBars.PopupMenu(); + this.pmBill = new DevExpress.XtraBars.PopupMenu(this.components); this.btnPrint = new DevExpress.XtraEditors.DropDownButton(); - this.pMprint = new DevExpress.XtraBars.PopupMenu(); + this.pMprint = new DevExpress.XtraBars.PopupMenu(this.components); this.dpbTools = new DevExpress.XtraEditors.DropDownButton(); - this.pMenu = new DevExpress.XtraBars.PopupMenu(); + this.pMenu = new DevExpress.XtraBars.PopupMenu(this.components); this.btnDelete = new DevExpress.XtraEditors.SimpleButton(); this.btnBillSave = new DevExpress.XtraEditors.SimpleButton(); this.pl_main_top = new DevExpress.XtraEditors.PanelControl(); diff --git a/插件库/Lskj.PubBillConcise/BillModule.cs b/插件库/Lskj.PubBillConcise/BillModule.cs index 7cd12ef..98260a6 100644 --- a/插件库/Lskj.PubBillConcise/BillModule.cs +++ b/插件库/Lskj.PubBillConcise/BillModule.cs @@ -204,6 +204,8 @@ namespace Lskj.PubBillConcise /// 单据来源控件集合 /// public Dictionary BillModuleModelDic = new Dictionary(); + private volatile bool _moduleDisposed; + private readonly object _billModuleModelSync = new object(); /// /// 主表动态生成的日期列 /// @@ -232,6 +234,36 @@ namespace Lskj.PubBillConcise public BillModule() { InitializeComponent(); + this.Disposed += BillModule_Disposed; + } + + private void BillModule_Disposed(object sender, EventArgs e) + { + _moduleDisposed = true; + // The preloading graph keeps delegates pointing at this module. + // Remove this owner from the cache as soon as the visual root is + // disposed so later lookups cannot resurrect a closed module. + try + { + if (SysModel != null && SysModel.DataCaches != null) + SysModel.DataCaches.RemoveCache(this); + } + catch + { + // Disposal must remain best-effort. + } + + lock (_billModuleModelSync) + { + BillModuleModelDic.Clear(); + } + DateColumnList.Clear(); + RemoveRowitem.Clear(); + ToWithdrawRows.Clear(); + ChangeColorRow.Clear(); + SysModel = null; + BillModel = null; + ControlObj = null; } #region 初始化操作 @@ -250,6 +282,8 @@ namespace Lskj.PubBillConcise try { this.SysModel = Model;// 单据动态链接库参数,入口参数 + if (this.SysModel != null && this.SysModel.DataCaches == null) + this.SysModel.DataCaches = new Dictionary(); this._isInitFinish = false;// 是否为初始化创建 this.ssc_main.Visible = false; //获取或设置一个值,该值指示是否显示该控件及其所有子控件。 //this.ProcessExists = (Lskj.Data.Caches.CacheSYSConfig.Instance().JudgeProcessExists() && SystemInfo.Instance.isExecload); @@ -263,8 +297,13 @@ namespace Lskj.PubBillConcise } Dictionary dataCaches = Model != null ? Model.DataCaches : null; - dataCaches.GetValue(this, "DataCaches", out bool _);//等待缓存数据加载完成 + if (dataCaches != null) + dataCaches.GetValue(this, "DataCaches", out bool _);//等待缓存数据加载完成 + if (this.IsDisposed || Model == null) + return; this.InitializeSetting();//初始化系统配置 + if (this.IsDisposed || this.BillModel == null) + return; this.InitializeControl();//初始化控件 this.InitlizeSpiltLocation(); @@ -302,31 +341,62 @@ namespace Lskj.PubBillConcise finally { this._isInitFinish = true; - this.ssc_main.Visible = true; - if (this.BillModel.MainVerticalScroll || this.BillModel.MainHorizontalScrolling) + if (!this.IsDisposed && !this.Disposing) { - this.pl_main_center_master.AutoScroll = true; - this.pl_main_center_master.VerticalScroll.Visible = this.BillModel.MainVerticalScroll;//隐藏纵向滚动条 - this.pl_main_center_master.HorizontalScroll.Visible = this.BillModel.MainHorizontalScrolling;//隐藏横向滚动条 + this.ssc_main.Visible = true; + if (this.BillModel != null && + (this.BillModel.MainVerticalScroll || this.BillModel.MainHorizontalScrolling)) + { + this.pl_main_center_master.AutoScroll = true; + this.pl_main_center_master.VerticalScroll.Visible = this.BillModel.MainVerticalScroll;//隐藏纵向滚动条 + this.pl_main_center_master.HorizontalScroll.Visible = this.BillModel.MainHorizontalScrolling;//隐藏横向滚动条 + } } } } + private static void AttachCachedTask( + Dictionary cachesDic, + object cacheOwner, + string cacheKey, + object control, + string controlKey) + { + if (cachesDic == null || cacheOwner == null || control == null || + cachesDic.GetTask(control, controlKey) != null) + { + return; + } + + Task task = cachesDic.GetTask(cacheOwner, cacheKey); + if (task != null) + { + cachesDic.AddTask(control, controlKey, task); + } + } + /// /// 获取数据源缓存 /// /// public void GetDataCaches(Dictionary cachesDic) { + if (cachesDic == null) + return; + //获取通用数据 Task sysModelTask = cachesDic.GetTask(this, "SysModel"); Task systemDllRowTask = cachesDic.GetTask(this, "SystemDllRow"); Task dynamicModelTask = cachesDic.GetTask(this, "DynamicModel"); + if (dynamicModelTask == null) + return; Task getDataCachesTask = cachesDic.AddTask(this, "DataCaches", new Task(() => { DynamicBillModel dynamicModel = (DynamicBillModel)dynamicModelTask.Result; + if (dynamicModel == null) + return false; #region InitializeSetting初始化系统配置 Task billInfoRowTask = cachesDic.AddTask(this, "BillInfo", new Task(() => { @@ -342,6 +412,8 @@ namespace Lskj.PubBillConcise return model; })); BillModel billModel = billModelTask.Result; + if (billModel == null) + return false; if (billModel != null) { Task masterPreFixTask = cachesDic.AddTask(this, "MasterPreFix", new Task(() => @@ -358,18 +430,25 @@ namespace Lskj.PubBillConcise DataRow dataRow = BaseImpl.GetMenuConfigTab(dynamicModel.ModuleId); if (dataRow != null) { - billModel.BillSourceIds = dataRow.Table.Columns.Contains("BillSourceIds") ? (dataRow["BillSourceIds"] + "").Trim(',') : string.Empty; - billModel.BillFlag = dataRow.Table.Columns.Contains("BillFlag") && !string.IsNullOrEmpty(dataRow["BillFlag"] + "") ? Convert.ToInt32(dataRow["BillFlag"] + "") : -1; + if (billModel != null) + { + billModel.BillSourceIds = dataRow.Table.Columns.Contains("BillSourceIds") ? (dataRow["BillSourceIds"] + "").Trim(',') : string.Empty; + billModel.BillFlag = dataRow.Table.Columns.Contains("BillFlag") && !string.IsNullOrEmpty(dataRow["BillFlag"] + "") ? Convert.ToInt32(dataRow["BillFlag"] + "") : -1; + } //billModel } return dataRow;//获取菜单其他配置 })); #region GetPrintOtherParam获取打印其他参数 - if (string.IsNullOrEmpty(billModel.PrintSql) && billModel.PrintSql.Contains("<<")) + if (!string.IsNullOrEmpty(billModel.PrintSql) && + billModel.PrintSql.Contains("<<")) { - string[] strs = billModel.PrintSql.Split(new[] { ">>" }, 2, StringSplitOptions.None); - _mainPrintSql = strs[1]; - _printModeCond = strs[0].Replace("<<", ""); + int printEnd = billModel.PrintSql.IndexOf(">>", StringComparison.Ordinal); + if (printEnd > 1 && printEnd + 2 < billModel.PrintSql.Length) + { + _printModeCond = billModel.PrintSql.Substring(2, printEnd - 2); + _mainPrintSql = billModel.PrintSql.Substring(printEnd + 2); + } } if (!string.IsNullOrEmpty(_printModeCond) && _printModeCond.IndexOf(';') > 0) { @@ -435,15 +514,8 @@ namespace Lskj.PubBillConcise { return BaseModuleImpl.GetControlLocation(billModel.FormKey, dynamicModel.ModuleCode); })); - Task controlObjTask = cachesDic.AddTask(this, "ControlObj", new Task(() => - { - MyControl myControl = new MyControl() { Model = dynamicModel }; - myControl.OtherParams = dynamicModel.OtherParams(); - cachesDic.AddTask(myControl, "DynamicModel", dynamicModelTask); - cachesDic.AddTask(myControl, "ControlsTable", controlLocationTask); - myControl.GetControlDataCaches(cachesDic); - return myControl;//主表控件 - })); + // MyControl 持有 WinForms Timer。后台预加载只取得控件配置数据, + // MyControl 及其实际编辑控件统一在真正的 UI 线程创建。 Task addGroupDataTask = cachesDic.AddTask(this, "AddGroupData", new Task(() => { return BaseModuleImpl.GetAddGroupData(billModel.FormKey);//获取主信息分组数据 @@ -500,14 +572,21 @@ namespace Lskj.PubBillConcise })); cachesDic.AddTask(tb_buttom, "DynamicModel", dynamicModelTask); cachesDic.AddTask(tb_buttom, "Details", detailTask); - tb_buttom.GetDataCaches(cachesDic); + // TabControlEx.GetDataCaches 会创建 Grid、ModuleGrid、浏览器等 + // WinForms/DevExpress 控件。这里只预取来源数据,实际控件在 + // InitializeBillSource 的 UI 线程阶段创建。 Task initSourcesTask = cachesDic.AddTask(this, "InitSourcesTask", new Task(() => { + if (_moduleDisposed) + return false; Hashtable htTable = sourcesTask.Result; DataTable masterTable = htTable["master"] as DataTable; - DataTable detailTable = htTable["detail"] as DataTable; + if (masterTable == null) + return false; for (int i = 0; i < masterTable.Rows.Count; i++) { + if (_moduleDisposed) + return false; DataRow sourceModelRow = masterTable.Rows[i]; BillModuleModel billModuleModel = new BillModuleModel(); BillSourceModel model = new BillSourceModel(sourceModelRow); @@ -516,137 +595,78 @@ namespace Lskj.PubBillConcise { return BaseModuleImpl.GetCustomQueryFields(model.FormKey); })); - #region 加载来源表头 - GridControlEx gridControl = null; - TreeViewEx treeView = null; + + // 后台阶段只加载数据,不构造任何 WinForms/DevExpress + // 对象。缓存以 BillModuleModel 为所有者,UI 创建实际控件 + // 后再把需要的任务绑定到该控件。 if (model.SourceType == 1) { - // 来源为树类型 - treeView = new TreeViewEx(); - billModuleModel.SourceControl = treeView; - Task bindTreeTableTask = cachesDic.AddTask(treeView, "BindTreeTable", new Task(() => + cachesDic.AddTask(billModuleModel, "BindTreeTable", new Task(() => { return BaseImpl.GetDataTableResult(model.SourceSql); })); } - else if (model.SourceType == 5) - { - model.ChangeSourceType(2);//单据来源显示条数 - } - else if (model.SourceType == 3 || model.SourceType == 4) - { - gridControl = new TreeGridControlEx(); - billModuleModel.SourceControl = gridControl; - Task sourceColumnsTask = cachesDic.AddTask(gridControl, "SourceColumns", new Task(() => - { - return BillImpl.GetSourceColumns(model.Id); - })); - Task sourceGridRowColorsTask = cachesDic.AddTask(gridControl, "BaseGridRowColors", new Task(() => - { - return BaseModuleImpl.GetBaseGridRowColors("BILLSOURCE_" + model.FormKey); - })); - Task sourceGridRightMenusTask = cachesDic.AddTask(gridControl, "BaseGridRightMenus", new Task(() => - { - return BaseModuleImpl.GetBaseGridRightMenus("BILLSOURCE_" + model.FormKey); - })); - } else { - // 来源为表格类型 - gridControl = new GridControlEx(); - billModuleModel.SourceControl = gridControl; - Task sourceColumnsTask = cachesDic.AddTask(gridControl, "SourceColumns", new Task(() => - { - return BillImpl.GetSourceColumns(model.Id); - })); - //创建表格列 - cachesDic.AddTask(gridControl, "DynamicModel", dynamicModelTask); - gridControl.GetDataCaches(cachesDic, GridCustomColumnStruct.BillSourceGridView + model.FormKey); - Task sourceGridRowColorsTask = cachesDic.AddTask(gridControl, "BaseGridRowColors", new Task(() => - { - return BaseModuleImpl.GetBaseGridRowColors("BILLSOURCE_" + model.FormKey); - })); - Task sourceGridRightMenusTask = cachesDic.AddTask(gridControl, "BaseGridRightMenus", new Task(() => - { - return BaseModuleImpl.GetBaseGridRightMenus("BILLSOURCE_" + model.FormKey); - })); - } - #endregion - #region 加载来源明细 - GridControlEx gridDetail = new GridControlEx(); - if (model.sourceDetailType.Equals("1")) - { - //明细为树表格 - gridDetail = new TreeGridControlEx(); - billModuleModel.SourceDetailControl = gridDetail; - //创建表格列 - cachesDic.AddTask(gridDetail, "DynamicModel", dynamicModelTask); - gridDetail.GetDataCaches(cachesDic, GridCustomColumnStruct.BillSourceDetailGridView + model.FormKey); - Task detailGridRowColorsTask = cachesDic.AddTask(gridDetail, "BaseGridRowColors", new Task(() => - { - return BaseModuleImpl.GetBaseGridRowColors("SOURCEDETAIL_" + model.FormKey); - })); - Task detailGridRightMenusTask = cachesDic.AddTask(gridDetail, "BaseGridRightMenus", new Task(() => - { - return BaseModuleImpl.GetBaseGridRightMenus("BILLSOURCEDT_" + model.FormKey); - })); + if (model.SourceType == 5) + model.ChangeSourceType(2);//单据来源显示条数 + cachesDic.AddTask(billModuleModel, "SourceColumns", new Task(() => + { + return BillImpl.GetSourceColumns(model.Id); + })); + cachesDic.AddTask(billModuleModel, "SourceCustomColumnByDatabase", new Task(() => + { + return GridExtend.GetCustomColumnByDatabase( + GridCustomColumnStruct.BillSourceGridView + model.FormKey); + })); + cachesDic.AddTask(billModuleModel, "SourceBaseGridRowColors", new Task(() => + { + return BaseModuleImpl.GetBaseGridRowColors("BILLSOURCE_" + model.FormKey); + })); + cachesDic.AddTask(billModuleModel, "SourceBaseGridRightMenus", new Task(() => + { + return BaseModuleImpl.GetBaseGridRightMenus("BILLSOURCE_" + model.FormKey); + })); } - else + + if (string.IsNullOrWhiteSpace(model.DetailModeCode)) { - gridDetail = new GridControlEx(); - billModuleModel.SourceDetailControl = gridDetail; - //创建表格列 - cachesDic.AddTask(gridDetail, "DynamicModel", dynamicModelTask); - gridDetail.GetDataCaches(cachesDic, GridCustomColumnStruct.BillSourceDetailGridView + model.FormKey); - Task detailGridRowColorsTask = cachesDic.AddTask(gridDetail, "BaseGridRowColors", new Task(() => + cachesDic.AddTask(billModuleModel, "SourceDetailCustomColumnByDatabase", new Task(() => + { + return GridExtend.GetCustomColumnByDatabase( + GridCustomColumnStruct.BillSourceDetailGridView + model.FormKey); + })); + cachesDic.AddTask(billModuleModel, "SourceDetailBaseGridRowColors", new Task(() => { return BaseModuleImpl.GetBaseGridRowColors("SOURCEDETAIL_" + model.FormKey); })); - Task detailGridRightMenusTask = cachesDic.AddTask(gridDetail, "BaseGridRightMenus", new Task(() => + cachesDic.AddTask(billModuleModel, "SourceDetailBaseGridRightMenus", new Task(() => { return BaseModuleImpl.GetBaseGridRightMenus("BILLSOURCEDT_" + model.FormKey); })); - //明细的明细(明细分成左右2个) if (!string.IsNullOrEmpty(model.SourceDetailsDetailsSql)) { - GridControlEx gridDetail_Details = new GridControlEx(); - billModuleModel.SourceDetailDetailControl = gridDetail_Details; - //创建只读列 - Task detailDetailsTask = cachesDic.AddTask(gridDetail_Details, "DetailDetails", new Task(() => + cachesDic.AddTask(billModuleModel, "DetailDetails", new Task(() => { return BaseModuleImpl.getDetail_Details(model.FormKey); })); - //创建表格列 - cachesDic.AddTask(gridDetail_Details, "DynamicModel", dynamicModelTask); - gridDetail_Details.GetDataCaches(cachesDic); - Task detailDetailsGridRightMenusTask = cachesDic.AddTask(gridDetail_Details, "BaseGridRightMenus", new Task(() => + cachesDic.AddTask(billModuleModel, "SourceDetailDetailsCustomColumnByDatabase", new Task(() => + { + return GridExtend.GetCustomColumnByDatabase(string.Empty); + })); + cachesDic.AddTask(billModuleModel, "SourceDetailDetailsBaseGridRightMenus", new Task(() => { return BaseModuleImpl.GetBaseGridRightMenus("BILLSOURCEDTX_" + model.FormKey); })); } } - #endregion - BillModuleModelDic.Add(sourceModelRow, billModuleModel); - #region 加载来源条件 - Task searchControlTask = cachesDic.AddTask(sourceModelRow, "SearchControl", new Task(() => + lock (_billModuleModelSync) { - DataTable controls = customQueryFieldsTask.Result; - MyControl searchControl = null; - if (model.SourceType != 1 || (controls != null && controls.Rows.Count > 0)) - { - // 创建自定义条件 - string searchSql = model.SourceType == 1 ? Regex.Replace(model.DetailSql, "{speciesno}", "", RegexOptions.IgnoreCase) : model.SourceSql; - searchControl = new MyControl(searchSql, gridControl == null ? gridDetail : gridControl, treeView); - searchControl.Model = dynamicModel; - searchControl.OtherParams = dynamicModel.OtherParams(); - cachesDic.AddTask(searchControl, "DynamicModel", dynamicModelTask); - cachesDic.AddTask(searchControl, "ControlsTable", customQueryFieldsTask); - searchControl.GetSearchDataCaches(cachesDic); - } - return searchControl; - })); - #endregion + if (_moduleDisposed) + return false; + BillModuleModelDic[sourceModelRow] = billModuleModel; + } } return true; })); @@ -715,6 +735,10 @@ namespace Lskj.PubBillConcise private void InitializeSetting() { Dictionary dataCaches = SysModel != null ? SysModel.DataCaches : null; + if (this.SysModel == null) + return; + if (dataCaches == null) + dataCaches = new Dictionary(); if (!dataCaches.GetValue(this, "BillInfo", out DataRow modelRow)) { modelRow = BillImpl.GetBillInfo(this.SysModel.ModuleCode);//获取单个模块信息 @@ -1040,6 +1064,10 @@ namespace Lskj.PubBillConcise private void InitializeBillInfo() { Dictionary dataCaches = SysModel != null ? SysModel.DataCaches : null; + if (this.BillModel == null || this.SysModel == null) + return; + if (dataCaches == null) + dataCaches = new Dictionary(); if (!dataCaches.GetValue(this, "ControlLocation", out DataTable controls)) { controls = BaseModuleImpl.GetControlLocation(this.BillModel.FormKey, this.SysModel.ModuleCode); @@ -1653,6 +1681,18 @@ namespace Lskj.PubBillConcise this.xtc_left_container.TabPages.Clear(); Dictionary dataCaches = SysModel != null ? SysModel.DataCaches : null; + if (this.BillModel == null || this.SysModel == null) + return; + if (dataCaches == null) + dataCaches = new Dictionary(); + if (tb_buttom != null && dataCaches != null && + dataCaches.GetTask(tb_buttom, "DynamicModel") != null && + dataCaches.GetTask>(tb_buttom, "Details") != null) + { + // TabControlEx.GetDataCaches 会创建来源明细页中的控件,必须在 + // 当前 WinForms UI 线程执行;其内部的数据任务仍可并行准备。 + tb_buttom.GetDataCaches(dataCaches); + } if (!dataCaches.GetValue(this, "Details", out List attachModels)) { attachModels = GetAttachTabs(); @@ -1666,8 +1706,13 @@ namespace Lskj.PubBillConcise } htTable = BillImpl.GetSources(this.SysModel.ModuleCode, SourceIds);//获取单据来源 } + if (htTable == null) + return; + DataTable masterTable = htTable["master"] as DataTable; - DataTable detailTable = htTable["detail"] as DataTable; + if (masterTable == null) + return; + DataTable detailTable = htTable["detail"] as DataTable ?? new DataTable(); /* 构造原理 * 1. 假如存在附加信息,则底部显示来源明细(来源明细摆放在标签第一个位置)+多标签,假如标签只有一个则隐藏标签头. @@ -1700,7 +1745,13 @@ namespace Lskj.PubBillConcise for (int i = 0; i < masterTable.Rows.Count; i++) { DataRow sourceModelRow = masterTable.Rows[i]; - BillModuleModel billModuleModel = BillModuleModelDic.ContainsKey(sourceModelRow) ? BillModuleModelDic[sourceModelRow] : new BillModuleModel(); + BillModuleModel billModuleModel; + lock (_billModuleModelSync) + { + BillModuleModelDic.TryGetValue(sourceModelRow, out billModuleModel); + } + if (billModuleModel == null) + billModuleModel = new BillModuleModel(); BillSourceModel model = billModuleModel.SourceModel != null ? billModuleModel.SourceModel : new BillSourceModel(sourceModelRow); object sourceControl = billModuleModel.SourceControl != null ? billModuleModel.SourceControl : null; object sourceDetailControl = billModuleModel.SourceDetailControl != null ? billModuleModel.SourceDetailControl : null; @@ -1743,7 +1794,7 @@ namespace Lskj.PubBillConcise treeView.Dock = DockStyle.Fill; treeView.TreeNodeKeyField = model.SourceKeyField; treeView.TreeNodeTextField = model.SourceResult; - if (!dataCaches.GetValue(treeView, "BindTreeTable", out DataTable bindTreeTable)) + if (!dataCaches.GetValue(billModuleModel, "BindTreeTable", out DataTable bindTreeTable)) { bindTreeTable = BaseImpl.GetDataTableResult(model.SourceSql); } @@ -1765,15 +1816,15 @@ namespace Lskj.PubBillConcise gridControl.BorderStyle = System.Windows.Forms.BorderStyle.None; gridControl.Model = this.SysModel; - if (!dataCaches.GetValue(gridControl, "SourceColumns", out DataTable dtSourceColumns)) + if (!dataCaches.GetValue(billModuleModel, "SourceColumns", out DataTable dtSourceColumns)) { dtSourceColumns = BillImpl.GetSourceColumns(model.Id); } - if (!dataCaches.GetValue(gridControl, "BaseGridRowColors", out DataTable dtBaseGridRowColors)) + if (!dataCaches.GetValue(billModuleModel, "SourceBaseGridRowColors", out DataTable dtBaseGridRowColors)) { dtBaseGridRowColors = BaseModuleImpl.GetBaseGridRowColors("BILLSOURCE_" + model.FormKey); } - if (!dataCaches.GetValue(gridControl, "BaseGridRightMenus", out DataTable dtBaseGridRightMenus)) + if (!dataCaches.GetValue(billModuleModel, "SourceBaseGridRightMenus", out DataTable dtBaseGridRightMenus)) { dtBaseGridRightMenus = BaseModuleImpl.GetBaseGridRightMenus("BILLSOURCE_" + model.FormKey); } @@ -1783,6 +1834,9 @@ namespace Lskj.PubBillConcise //父节点编号 if (!string.IsNullOrWhiteSpace(model.SourceParentFieldName)) (gridControl as TreeGridControlEx).TreeListObj.ParentFieldName = model.SourceParentFieldName; + AttachCachedTask(dataCaches, billModuleModel, + "SourceCustomColumnByDatabase", gridControl, + "CustomColumnByDatabase"); gridControl.SetReadOnlyColumns(dtSourceColumns, GridCustomColumnStruct.BillSourceGridView + model.FormKey); gridControl.SetGridRowColors(dtBaseGridRowColors); //gridControl.SetGridRightMenus(dtBaseGridRightMenus, this.SysModel, OnGridControlRightCallback); @@ -1829,18 +1883,21 @@ namespace Lskj.PubBillConcise { gridControl.GridView.OptionsView.ShowAutoFilterRow = true; } - if (!dataCaches.GetValue(gridControl, "SourceColumns", out DataTable dtSourceColumns)) + if (!dataCaches.GetValue(billModuleModel, "SourceColumns", out DataTable dtSourceColumns)) { dtSourceColumns = BillImpl.GetSourceColumns(model.Id); } - if (!dataCaches.GetValue(gridControl, "BaseGridRowColors", out DataTable dtBaseGridRowColors)) + if (!dataCaches.GetValue(billModuleModel, "SourceBaseGridRowColors", out DataTable dtBaseGridRowColors)) { dtBaseGridRowColors = BaseModuleImpl.GetBaseGridRowColors("BILLSOURCE_" + model.FormKey); } - if (!dataCaches.GetValue(gridControl, "BaseGridRightMenus", out DataTable dtBaseGridRightMenus)) + if (!dataCaches.GetValue(billModuleModel, "SourceBaseGridRightMenus", out DataTable dtBaseGridRightMenus)) { dtBaseGridRightMenus = BaseModuleImpl.GetBaseGridRightMenus("BILLSOURCE_" + model.FormKey); } + AttachCachedTask(dataCaches, billModuleModel, + "SourceCustomColumnByDatabase", gridControl, + "CustomColumnByDatabase"); gridControl.SetReadOnlyColumns(dtSourceColumns, GridCustomColumnStruct.BillSourceGridView + model.FormKey); gridControl.SetGridRowColors(dtBaseGridRowColors); gridControl.SetGridRightMenus(dtBaseGridRightMenus, this.SysModel, OnGridControlRightCallback); @@ -1893,8 +1950,9 @@ namespace Lskj.PubBillConcise DataRow[] detailColumns = detailTable.Select("sourceId=" + model.Id); - //明细为表格 - GridControlEx gridDetail = new GridControlEx(); + // 明细控件由下面的分支按实际类型创建,避免先构造一个 + // 随即被替换、且无法进入父容器 Dispose 链的临时 GridControlEx。 + GridControlEx gridDetail = null; if (model.sourceDetailType.Equals("1")) @@ -1903,12 +1961,15 @@ namespace Lskj.PubBillConcise gridDetail = sourceDetailControl != null ? (TreeGridControlEx)sourceDetailControl : new TreeGridControlEx(); gridDetail.Model = this.SysModel; gridDetail.Dock = DockStyle.Fill; + AttachCachedTask(dataCaches, billModuleModel, + "SourceDetailCustomColumnByDatabase", gridDetail, + "CustomColumnByDatabase"); gridDetail.SetReadOnlyColumns(detailColumns == null || detailColumns.Length == 0 ? new DataTable() : detailColumns.CopyToDataTable(), GridCustomColumnStruct.BillSourceDetailGridView + model.FormKey); - if (!dataCaches.GetValue(gridDetail, "BaseGridRowColors", out DataTable dtBaseGridRowColors)) + if (!dataCaches.GetValue(billModuleModel, "SourceDetailBaseGridRowColors", out DataTable dtBaseGridRowColors)) { dtBaseGridRowColors = BaseModuleImpl.GetBaseGridRowColors("SOURCEDETAIL_" + model.FormKey); } - if (!dataCaches.GetValue(gridDetail, "BaseGridRightMenus", out DataTable dtBaseGridRightMenus)) + if (!dataCaches.GetValue(billModuleModel, "SourceDetailBaseGridRightMenus", out DataTable dtBaseGridRightMenus)) { dtBaseGridRightMenus = BaseModuleImpl.GetBaseGridRightMenus("BILLSOURCEDT_" + model.FormKey); } @@ -1940,17 +2001,20 @@ namespace Lskj.PubBillConcise gridDetail = sourceDetailControl != null ? (GridControlEx)sourceDetailControl : new GridControlEx(); gridDetail.Model = this.SysModel; gridDetail.Dock = DockStyle.Fill; + AttachCachedTask(dataCaches, billModuleModel, + "SourceDetailCustomColumnByDatabase", gridDetail, + "CustomColumnByDatabase"); gridDetail.SetReadOnlyColumns(detailColumns == null || detailColumns.Length == 0 ? new DataTable() : detailColumns.CopyToDataTable(), GridCustomColumnStruct.BillSourceDetailGridView + model.FormKey); if (model.DetailedLoadFilter) { gridDetail.GridView.OptionsView.ShowAutoFilterRow = true; } - if (!dataCaches.GetValue(gridDetail, "BaseGridRowColors", out DataTable dtBaseGridRowColors)) + if (!dataCaches.GetValue(billModuleModel, "SourceDetailBaseGridRowColors", out DataTable dtBaseGridRowColors)) { dtBaseGridRowColors = BaseModuleImpl.GetBaseGridRowColors("SOURCEDETAIL_" + model.FormKey); } - if (!dataCaches.GetValue(gridDetail, "BaseGridRightMenus", out DataTable dtBaseGridRightMenus)) + if (!dataCaches.GetValue(billModuleModel, "SourceDetailBaseGridRightMenus", out DataTable dtBaseGridRightMenus)) { dtBaseGridRightMenus = BaseModuleImpl.GetBaseGridRightMenus("BILLSOURCEDT_" + model.FormKey); } @@ -1966,14 +2030,17 @@ namespace Lskj.PubBillConcise GridControlEx gridDetail_Details = sourceDetailDetailControl != null ? (GridControlEx)sourceDetailDetailControl : new GridControlEx(); gridDetail_Details.Model = this.SysModel; gridDetail_Details.Dock = DockStyle.Fill; + AttachCachedTask(dataCaches, billModuleModel, + "SourceDetailDetailsCustomColumnByDatabase", gridDetail_Details, + "CustomColumnByDatabase"); //创建只读列 - if (!dataCaches.GetValue(gridDetail_Details, "Detail_Details", out DataTable dataTable)) + if (!dataCaches.GetValue(billModuleModel, "DetailDetails", out DataTable dataTable)) { dataTable = BaseModuleImpl.getDetail_Details(model.FormKey); } gridDetail_Details.SetReadOnlyColumns(dataTable); //设置右键 - if (!dataCaches.GetValue(gridDetail_Details, "BaseGridRightMenus", out DataTable dttBaseGridRightMenus)) + if (!dataCaches.GetValue(billModuleModel, "SourceDetailDetailsBaseGridRightMenus", out DataTable dttBaseGridRightMenus)) { dttBaseGridRightMenus = BaseModuleImpl.GetBaseGridRightMenus("BILLSOURCEDTX_" + model.FormKey); } @@ -2092,9 +2159,23 @@ namespace Lskj.PubBillConcise string searchSql = model.SourceType == 1 ? Regex.Replace(model.DetailSql, "{speciesno}", "", RegexOptions.IgnoreCase) : model.SourceSql; //searchSql = ReplaceHelper.ReplaceParam(searchSql); PanelControl searchPanel = new PanelControl(); - if (!dataCaches.GetValue(sourceModelRow, "SearchControl", out MyControl searchControl)) + MyControl searchControl = new MyControl( + searchSql, + gridControl == null ? gridDetail : gridControl, + treeView) { - searchControl = new MyControl(searchSql, gridControl == null ? gridDetail : gridControl, treeView); + Model = this.SysModel, + OtherParams = this.SysModel.OtherParams() + }; + if (dataCaches != null) + { + dataCaches.AddTask(searchControl, "DynamicModel", + new Task(() => this.SysModel)); + dataCaches.AddTask(searchControl, "ControlsTable", + new Task(() => controls)); + // 查询条件模型和默认值可以后台准备,编辑控件由 + // InitSearchControl 在当前 UI 线程创建。 + searchControl.GetSearchDataCaches(dataCaches); } searchControl.OnSearchBeforeCallBack += new SearchEventHandler(OnSearchBefore); searchControl.OnSearchAfterCallBack += new EventHandler(OnSearchAfterd); @@ -2132,6 +2213,21 @@ namespace Lskj.PubBillConcise } + // Keep the actual UI-owned controls on the per-source record. + // This preserves reuse on a later source rebuild and gives + // disposal/debugging code an exact ownership list without + // traversing the visual tree. + billModuleModel.SourceModel = model; + billModuleModel.SourceControl = treeView ?? (object)gridControl; + billModuleModel.SourceDetailControl = gridDetail; + billModuleModel.SourceDetailDetailControl = + unionModel.GridDetailDetailControlObj; + lock (_billModuleModelSync) + { + if (!_moduleDisposed) + BillModuleModelDic[sourceModelRow] = billModuleModel; + } + unionModel.ModelObj = model; @@ -3215,7 +3311,7 @@ namespace Lskj.PubBillConcise if (pageControl.Tag is GridDetailModel detailModel && (detailModel.Orderid + "").Equals(movepage)) { this.tb_buttom.TabControlObj.SelectedTabPage = xtraTabPage; - StaticControl.BomUnionPage = xtraTabPage; + StaticControl.SetBomUnionPage(xtraTabPage); if (xtraTabPage.Tag is GridControlEx) { DataRow dataRow = this.gcMain.GetViewFocusedDataRow(); @@ -5157,7 +5253,7 @@ namespace Lskj.PubBillConcise menu.ApplyBefore += new CommonToolApplyEventHandler(OnMenuApplyBefore); menu.DynamicLinkAfter += new CommonToolAfterDynamicLinkEventHandler(OnDynamicLinkAfter); StaticControl.RightMenuGridView = gcMain.GridView; - StaticControl.RightMenuMyControl = ControlObj; + StaticControl.SetRightMenuMyControl(ControlObj); menu.Apply(menuRow); if (!menu.issuccess) { @@ -9269,4 +9365,4 @@ namespace Lskj.PubBillConcise } } } -} \ No newline at end of file +} diff --git a/插件库/Lskj.PubBillInfo/FrmMain.Designer.cs b/插件库/Lskj.PubBillInfo/FrmMain.Designer.cs index 9a88db9..a4f04c3 100644 --- a/插件库/Lskj.PubBillInfo/FrmMain.Designer.cs +++ b/插件库/Lskj.PubBillInfo/FrmMain.Designer.cs @@ -28,11 +28,12 @@ /// private void InitializeComponent() { + this.components = new System.ComponentModel.Container(); this.txt_billDocumentId = new Lskj.Control.LabelTextEdit(); this.pl_right_bottom = new DevExpress.XtraEditors.PanelControl(); this.dpb_Tools = new DevExpress.XtraEditors.DropDownButton(); - this.pMenu = new DevExpress.XtraBars.PopupMenu(); - this.barManager1 = new DevExpress.XtraBars.BarManager(); + this.pMenu = new DevExpress.XtraBars.PopupMenu(this.components); + this.barManager1 = new DevExpress.XtraBars.BarManager(this.components); this.barDockControlTop = new DevExpress.XtraBars.BarDockControl(); this.barDockControlBottom = new DevExpress.XtraBars.BarDockControl(); this.barDockControlLeft = new DevExpress.XtraBars.BarDockControl(); @@ -41,7 +42,7 @@ this.btnHelp = new DevExpress.XtraEditors.SimpleButton(); this.btnAtt = new DevExpress.XtraEditors.SimpleButton(); this.btnPrint = new DevExpress.XtraEditors.DropDownButton(); - this.pMprint = new DevExpress.XtraBars.PopupMenu(); + this.pMprint = new DevExpress.XtraBars.PopupMenu(this.components); this.pl_right = new DevExpress.XtraEditors.PanelControl(); this.gridFlow = new Lskj.Control.GridControlEx(); this.label_flowRecord = new DevExpress.XtraEditors.LabelControl(); @@ -382,4 +383,4 @@ private DevExpress.XtraEditors.SimpleButton btnHelp; private DevExpress.XtraEditors.SplitContainerControl splitMain; } -} \ No newline at end of file +} diff --git a/插件库/Lskj.PubBillManagement/BillModule.Designer.cs b/插件库/Lskj.PubBillManagement/BillModule.Designer.cs index e93b354..66cbc79 100644 --- a/插件库/Lskj.PubBillManagement/BillModule.Designer.cs +++ b/插件库/Lskj.PubBillManagement/BillModule.Designer.cs @@ -28,16 +28,17 @@ /// private void InitializeComponent() { - this.barManager1 = new DevExpress.XtraBars.BarManager(); + this.components = new System.ComponentModel.Container(); + this.barManager1 = new DevExpress.XtraBars.BarManager(this.components); this.barDockControl1 = new DevExpress.XtraBars.BarDockControl(); this.barDockControl2 = new DevExpress.XtraBars.BarDockControl(); this.barDockControl3 = new DevExpress.XtraBars.BarDockControl(); this.barDockControl5 = new DevExpress.XtraBars.BarDockControl(); - this.pmFP = new DevExpress.XtraBars.PopupMenu(); - this.pmBillExp = new DevExpress.XtraBars.PopupMenu(); - this.pmBill = new DevExpress.XtraBars.PopupMenu(); - this.pMprint = new DevExpress.XtraBars.PopupMenu(); - this.pMenu = new DevExpress.XtraBars.PopupMenu(); + this.pmFP = new DevExpress.XtraBars.PopupMenu(this.components); + this.pmBillExp = new DevExpress.XtraBars.PopupMenu(this.components); + this.pmBill = new DevExpress.XtraBars.PopupMenu(this.components); + this.pMprint = new DevExpress.XtraBars.PopupMenu(this.components); + this.pMenu = new DevExpress.XtraBars.PopupMenu(this.components); this.tb_buttom = new Lskj.Control.TabControlEx(); this.pl_Main = new DevExpress.XtraEditors.PanelControl(); this.xtc_main_container = new DevExpress.XtraTab.XtraTabControl(); diff --git a/插件库/Lskj.PubBillManagement/FrmBill.cs b/插件库/Lskj.PubBillManagement/FrmBill.cs index 8670557..89bbae4 100644 --- a/插件库/Lskj.PubBillManagement/FrmBill.cs +++ b/插件库/Lskj.PubBillManagement/FrmBill.cs @@ -3529,7 +3529,7 @@ namespace Lskj.PubBillManagement menu.DynamicLinkAfter += new CommonToolAfterDynamicLinkEventHandler(OnDynamicLinkAfter); StaticControl.RightMenuGridView = gcMain.GridView; StaticControl.Comprefix = this.BillModel.DetailPreFix; - StaticControl.RightMenuMyControl = ControlObj; + StaticControl.SetRightMenuMyControl(ControlObj); menu.Apply(menuRow); if (!menu.issuccess) { diff --git a/插件库/Lskj.PubBillNewDrag/BillModule.Designer.cs b/插件库/Lskj.PubBillNewDrag/BillModule.Designer.cs index 49eed05..07aa82d 100644 --- a/插件库/Lskj.PubBillNewDrag/BillModule.Designer.cs +++ b/插件库/Lskj.PubBillNewDrag/BillModule.Designer.cs @@ -28,6 +28,7 @@ /// private void InitializeComponent() { + this.components = new System.ComponentModel.Container(); this.ssc_main = new DevExpress.XtraEditors.SplitContainerControl(); this.ssc_main_top = new DevExpress.XtraEditors.SplitContainerControl(); this.pl_left = new DevExpress.XtraEditors.PanelControl(); @@ -40,7 +41,7 @@ this.pl_treeview_detault_search = new DevExpress.XtraEditors.PanelControl(); this.panelControl4 = new DevExpress.XtraEditors.PanelControl(); this.textEdit1 = new DevExpress.XtraEditors.TextEdit(); - this.barManager1 = new DevExpress.XtraBars.BarManager(); + this.barManager1 = new DevExpress.XtraBars.BarManager(this.components); this.barDockControl1 = new DevExpress.XtraBars.BarDockControl(); this.barDockControl2 = new DevExpress.XtraBars.BarDockControl(); this.barDockControl3 = new DevExpress.XtraBars.BarDockControl(); @@ -60,15 +61,15 @@ this.labelControl2 = new DevExpress.XtraEditors.LabelControl(); this.btn_input = new DevExpress.XtraEditors.SimpleButton(); this.dpbImport = new DevExpress.XtraEditors.DropDownButton(); - this.pmBillExp = new DevExpress.XtraBars.PopupMenu(); + this.pmBillExp = new DevExpress.XtraBars.PopupMenu(this.components); this.btnBillAdd = new DevExpress.XtraEditors.SimpleButton(); this.btnBillApply = new DevExpress.XtraEditors.SimpleButton(); this.dpCopyBill = new DevExpress.XtraEditors.DropDownButton(); - this.pmBill = new DevExpress.XtraBars.PopupMenu(); + this.pmBill = new DevExpress.XtraBars.PopupMenu(this.components); this.btnPrint = new DevExpress.XtraEditors.DropDownButton(); - this.pMprint = new DevExpress.XtraBars.PopupMenu(); + this.pMprint = new DevExpress.XtraBars.PopupMenu(this.components); this.dpbTools = new DevExpress.XtraEditors.DropDownButton(); - this.pMenu = new DevExpress.XtraBars.PopupMenu(); + this.pMenu = new DevExpress.XtraBars.PopupMenu(this.components); this.btnDelete = new DevExpress.XtraEditors.SimpleButton(); this.btnBillSave = new DevExpress.XtraEditors.SimpleButton(); this.pl_main_top = new DevExpress.XtraEditors.PanelControl(); diff --git a/插件库/Lskj.PubBillSpecial/BillModule.Designer.cs b/插件库/Lskj.PubBillSpecial/BillModule.Designer.cs index 93fbe3c..0988990 100644 --- a/插件库/Lskj.PubBillSpecial/BillModule.Designer.cs +++ b/插件库/Lskj.PubBillSpecial/BillModule.Designer.cs @@ -28,6 +28,7 @@ /// private void InitializeComponent() { + this.components = new System.ComponentModel.Container(); this.ssc_main = new DevExpress.XtraEditors.SplitContainerControl(); this.ssc_main_top = new DevExpress.XtraEditors.SplitContainerControl(); this.pl_main = new DevExpress.XtraEditors.PanelControl(); @@ -40,7 +41,7 @@ this.pl_treeview_detault_search = new DevExpress.XtraEditors.PanelControl(); this.panelControl4 = new DevExpress.XtraEditors.PanelControl(); this.textEdit1 = new DevExpress.XtraEditors.TextEdit(); - this.barManager1 = new DevExpress.XtraBars.BarManager(); + this.barManager1 = new DevExpress.XtraBars.BarManager(this.components); this.barDockControl1 = new DevExpress.XtraBars.BarDockControl(); this.barDockControl2 = new DevExpress.XtraBars.BarDockControl(); this.barDockControl3 = new DevExpress.XtraBars.BarDockControl(); @@ -74,11 +75,11 @@ this.rdRed = new System.Windows.Forms.RadioButton(); this.pl_blue = new DevExpress.XtraEditors.PanelControl(); this.rdBlue = new System.Windows.Forms.RadioButton(); - this.pMprint = new DevExpress.XtraBars.PopupMenu(); - this.pmFP = new DevExpress.XtraBars.PopupMenu(); - this.pmBillExp = new DevExpress.XtraBars.PopupMenu(); - this.pmBill = new DevExpress.XtraBars.PopupMenu(); - this.pMenu = new DevExpress.XtraBars.PopupMenu(); + this.pMprint = new DevExpress.XtraBars.PopupMenu(this.components); + this.pmFP = new DevExpress.XtraBars.PopupMenu(this.components); + this.pmBillExp = new DevExpress.XtraBars.PopupMenu(this.components); + this.pmBill = new DevExpress.XtraBars.PopupMenu(this.components); + this.pMenu = new DevExpress.XtraBars.PopupMenu(this.components); this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker(); this.backgroundWorker2 = new System.ComponentModel.BackgroundWorker(); ((System.ComponentModel.ISupportInitialize)(this.ssc_main)).BeginInit(); diff --git a/插件库/Lskj.PubBillSpecial/BillModule.cs b/插件库/Lskj.PubBillSpecial/BillModule.cs index 5a70e5f..c1c668c 100644 --- a/插件库/Lskj.PubBillSpecial/BillModule.cs +++ b/插件库/Lskj.PubBillSpecial/BillModule.cs @@ -3851,7 +3851,7 @@ namespace Lskj.PubBillSpecial menu.ApplyBefore += new CommonToolApplyEventHandler(OnMenuApplyBefore); menu.DynamicLinkAfter += new CommonToolAfterDynamicLinkEventHandler(OnDynamicLinkAfter); StaticControl.RightMenuGridView = gcMain.GridView; - StaticControl.RightMenuMyControl = ControlObj; + StaticControl.SetRightMenuMyControl(ControlObj); menu.Apply(menuRow); if (!menu.issuccess) { diff --git a/插件库/Lskj.Util/TaskUtil.cs b/插件库/Lskj.Util/TaskUtil.cs index a101902..fe3cea7 100644 --- a/插件库/Lskj.Util/TaskUtil.cs +++ b/插件库/Lskj.Util/TaskUtil.cs @@ -4,11 +4,63 @@ using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; +using System.Windows.Forms; namespace Lskj.Util { public class TaskSchedulerUtil : TaskScheduler { + /// + /// WinForms 会在后台线程第一次构造控件时自动安装 + /// WindowsFormsSynchronizationContext。预加载任务只能执行数据准备, + /// 不应把线程池线程注册成 WinForms UI 线程;否则 DevExpress manager + /// 会绑定到已经没有消息循环的线程,导致模块打开/关闭后的残留和卡死。 + /// 这里只在线程池任务执行期间使用一个非 WinForms 的同步上下文 + /// 作为占位,不改变调度器的并发度,也不触碰真正的 UI 线程上下文。 + /// 不能直接使用 SynchronizationContext 基类实例:WinForms 的 + /// InstallIfNeeded 会把“基类实例”视为未安装状态并再次替换为 + /// WindowsFormsSynchronizationContext;必须使用独立派生类型。 + /// + private void ExecuteTaskWithoutWinFormsContext(Task task) + { + SynchronizationContext previousContext = + SynchronizationContext.Current; + bool replaceContext = previousContext == null || + previousContext is WindowsFormsSynchronizationContext; + + if (replaceContext) + { + SynchronizationContext.SetSynchronizationContext( + new BackgroundSynchronizationContext()); + } + + try + { + TryExecuteTask(task); + } + finally + { + if (replaceContext) + { + SynchronizationContext.SetSynchronizationContext( + previousContext); + } + } + } + + /// + /// 标记后台线程已明确配置同步上下文,阻止 WinForms 按“空/基类上下文” + /// 自动安装 WindowsFormsSynchronizationContext。继承基类的默认 Post + /// 实现仍将延续任务投递到线程池,不改变普通后台任务语义。 + /// + private sealed class BackgroundSynchronizationContext : SynchronizationContext + { + public override SynchronizationContext CreateCopy() + { + return new BackgroundSynchronizationContext(); + } + } + private readonly LinkedList _tasks = new LinkedList(); //任务队列 private readonly int _maxDegreeOfParallelism; //最大并发线程数 private int _runningTasks = 0; //当前正在运行的线程数 @@ -46,7 +98,7 @@ namespace Lskj.Util { try { - base.TryExecuteTask(task); // 执行任务 + ExecuteTaskWithoutWinFormsContext(task); } finally {