fix(legacy): finalize hosted module lifecycle cleanup
This commit is contained in:
@@ -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>
|
||||
/// 水平滚动条改变时
|
||||
|
||||
Reference in New Issue
Block a user