fix(legacy): finalize hosted module lifecycle cleanup

This commit is contained in:
SugarChes
2026-08-29 18:03:44 +08:00
parent e27d445844
commit 176952d52c
5 changed files with 806 additions and 88 deletions
+125 -4
View File
@@ -160,6 +160,19 @@ namespace Lskj.Control
/// </summary>
private readonly HashSet<GridControlEx> _rightTabRefreshRegisteredGrids = new HashSet<GridControlEx>();
/// <summary>
/// 右侧联动事件委托。必须保存委托实例,Dispose 时才能从 GridView 正确解绑。
/// 匿名 lambda 如果不保存实例,会把 TabControlEx 通过事件闭包继续挂在已关闭的表格上。
/// </summary>
private readonly Dictionary<GridControlEx, DevExpress.XtraGrid.Views.Base.FocusedRowObjectChangedEventHandler> _rightTabRefreshHandlers =
new Dictionary<GridControlEx, DevExpress.XtraGrid.Views.Base.FocusedRowObjectChangedEventHandler>();
/// <summary>
/// 保存注册事件时的 GridView 实例。GridControlEx 在 Dispose 过程中可能
/// 清空或替换 GridView 属性;解绑时必须使用当初注册事件的同一个实例,
/// 不能再次通过 GridControlEx.GridView 取值。
/// </summary>
private readonly Dictionary<GridControlEx, GridView> _rightTabRefreshHandlerViews =
new Dictionary<GridControlEx, GridView>();
/// <summary>
/// 左右结构中的右侧页签集合。
/// 只有这些页签会根据数据行数禁用 ModuleGridEx 底部操作栏。
/// </summary>
@@ -435,9 +448,14 @@ namespace Lskj.Control
if (leftGrid == null || rightTab == null) return;
_rightTabsByLeftGrid[leftGrid] = rightTab;
if (!_rightTabRefreshRegisteredGrids.Contains(leftGrid))
GridView leftView = leftGrid.GridView;
if (!_rightTabRefreshRegisteredGrids.Contains(leftGrid) && leftView != null)
{
leftGrid.GridView.FocusedRowObjectChanged += (s, e) => RefreshRightTabAfterLeftBind(leftGrid);
DevExpress.XtraGrid.Views.Base.FocusedRowObjectChangedEventHandler handler =
(s, e) => RefreshRightTabAfterLeftBind(leftGrid);
leftView.FocusedRowObjectChanged += handler;
_rightTabRefreshHandlers[leftGrid] = handler;
_rightTabRefreshHandlerViews[leftGrid] = leftView;
_rightTabRefreshRegisteredGrids.Add(leftGrid);
}
RefreshRightTab(rightTab, leftGrid);// 首次加载
@@ -480,18 +498,30 @@ namespace Lskj.Control
{
if (ParentGridEx != null && ParentGridEx.CustomGroupBandEx != null)
{
ParentGridEx.CustomGroupBandEx.BandedView.LeftCoordChanged -= OnBandedViewLeftCoordChanged;
ParentGridEx.CustomGroupBandEx.BandedView.ColumnWidthChanged -= OnBandedViewColumnWidthChanged;
ParentGridEx.OnBuildGroupedColumnCallBack -= OnBandedViewColumnWidthChanged;
ParentGridEx.CustomGroupBandEx.BandedView.LeftCoordChanged += OnBandedViewLeftCoordChanged;
ParentGridEx.CustomGroupBandEx.BandedView.ColumnWidthChanged += OnBandedViewColumnWidthChanged;
ParentGridEx.OnBuildGroupedColumnCallBack += OnBandedViewColumnWidthChanged;
}
if (ParentGridEx != null && ParentGridEx.CustomGroupTreeBandEx != null)
{
ParentGridEx.CustomGroupTreeBandEx.TreeListObj.LeftCoordChanged -= OnBandedViewLeftCoordChanged;
ParentGridEx.CustomGroupTreeBandEx.TreeListObj.ColumnWidthChanged -= OnBandedViewColumnWidthChanged;
ParentGridEx.OnBuildGroupedColumnCallBack -= OnBandedViewColumnWidthChanged;
ParentGridEx.CustomGroupTreeBandEx.TreeListObj.LeftCoordChanged += OnBandedViewLeftCoordChanged; ;
ParentGridEx.CustomGroupTreeBandEx.TreeListObj.ColumnWidthChanged += OnBandedViewColumnWidthChanged;
ParentGridEx.OnBuildGroupedColumnCallBack += OnBandedViewColumnWidthChanged;
}
this._gridDetail = gridDetail;
if (!isAddPages) this.xtc_container.TabPages.Clear();
if (!isAddPages)
{
// 重建明细页签前先解除旧左右联动关系。仅清空 TabPages 不会移除
// GridView 上的事件,因此旧页签仍可能通过事件闭包存活。
DetachRightTabRefreshHandlers();
this.xtc_container.TabPages.Clear();
}
// 1️⃣ 预分组:Rightdockid > 0 的明细
var rightGroups = gridDetail
@@ -549,7 +579,12 @@ namespace Lskj.Control
rightTab.ShowTabHeader = DevExpress.Utils.DefaultBoolean.False;
GridControlEx leftControl = new GridControlEx();
// Only reuse an existing grid from the left detail. The
// previous unconditional `new GridControlEx()` created an
// orphan control whenever leftCtrl was a non-grid control;
// it was never added to the visual tree or disposed, and
// its GridView event handlers kept the whole wrapper alive.
GridControlEx leftControl = null;
if (leftCtrl is GridControlEx left1)
{
leftControl = left1;
@@ -646,6 +681,7 @@ namespace Lskj.Control
this._gridDetail = gridDetail;
if (!isAddPages)
{
DetachRightTabRefreshHandlers();
this.xtc_container.TabPages.Clear();
}
string DetaiName = string.Empty;
@@ -1865,6 +1901,7 @@ namespace Lskj.Control
public void InitTabPages(List<GridDetailModel> gridDetail, string parentValue)
{
this._gridDetail = gridDetail;
DetachRightTabRefreshHandlers();
this.xtc_container.TabPages.Clear();
bool ProcessExists = (Lskj.Data.Caches.CacheSYSConfig.Instance().JudgeProcessExists() && SystemInfo.Instance.isExecload);
@@ -2685,8 +2722,92 @@ namespace Lskj.Control
/// <param name="e"></param>
private void OnTabControlExDisposed(object sender, EventArgs e)
{
ReleaseRuntimeReferences();
}
/// <summary>
/// 在模块关闭但控件尚未进入 WinForms Dispose 流程时,主动解除
/// TabControlEx 持有的运行时引用。这里只解除事件和清空本控件的
/// 映射,不销毁共享的 GridControl/GridView。
/// </summary>
internal void ReleaseRuntimeReferences()
{
DetachRightTabRefreshHandlers();
DetachParentGridHandlers();
PageControlsDic?.Clear();
DetailTabPagesDic?.Clear();
_gridDetail?.Clear();
_gridIntoDetail?.Clear();
ParentGridEx = null;
MrpParentGridEx = null;
MrpDetailGridEx = null;
ParentControlEx = null;
ModuleGridObj = null;
ReplaceControlEx = null;
}
/// <summary>
/// 解除左右联动刷新事件。事件委托由字典保存,保证能够与注册时使用同一个实例。
/// </summary>
private void DetachRightTabRefreshHandlers()
{
foreach (KeyValuePair<GridControlEx, DevExpress.XtraGrid.Views.Base.FocusedRowObjectChangedEventHandler> item in
_rightTabRefreshHandlers.ToArray())
{
try
{
GridView registeredView;
if (_rightTabRefreshHandlerViews.TryGetValue(item.Key, out registeredView) &&
registeredView != null && item.Value != null)
{
registeredView.FocusedRowObjectChanged -= item.Value;
}
}
catch
{
// 表格可能已先于 TabControlEx 进入 Dispose,解绑失败不应阻断剩余清理。
}
}
_rightTabRefreshHandlers.Clear();
_rightTabRefreshHandlerViews.Clear();
_rightTabsByLeftGrid.Clear();
_rightTabRefreshRegisteredGrids.Clear();
_rightSideTabPages.Clear();
}
/// <summary>
/// 解除父表聚合视图的滚动/列宽事件,避免父表事件链反向持有当前 TabControlEx。
/// </summary>
private void DetachParentGridHandlers()
{
GridControlEx parentGrid = ParentGridEx;
try
{
if (parentGrid != null && parentGrid.CustomGroupBandEx != null)
{
if (parentGrid.CustomGroupBandEx.BandedView != null)
{
parentGrid.CustomGroupBandEx.BandedView.LeftCoordChanged -= OnBandedViewLeftCoordChanged;
parentGrid.CustomGroupBandEx.BandedView.ColumnWidthChanged -= OnBandedViewColumnWidthChanged;
}
parentGrid.OnBuildGroupedColumnCallBack -= OnBandedViewColumnWidthChanged;
}
if (parentGrid != null && parentGrid.CustomGroupTreeBandEx != null)
{
if (parentGrid.CustomGroupTreeBandEx.TreeListObj != null)
{
parentGrid.CustomGroupTreeBandEx.TreeListObj.LeftCoordChanged -= OnBandedViewLeftCoordChanged;
parentGrid.CustomGroupTreeBandEx.TreeListObj.ColumnWidthChanged -= OnBandedViewColumnWidthChanged;
}
parentGrid.OnBuildGroupedColumnCallBack -= OnBandedViewColumnWidthChanged;
}
}
catch
{
// 父表可能已经释放,事件清理应保持 best-effort。
}
}
/// <summary>
/// 水平滚动条改变时