583 lines
23 KiB
C#
583 lines
23 KiB
C#
using DevExpress.XtraGrid.Views.Grid;
|
||
using DevExpress.XtraGrid.Views.BandedGrid;
|
||
using DevExpress.XtraTab;
|
||
using Lskj.Control.ZKFinger;
|
||
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
|
||
{
|
||
public static class StaticControl
|
||
{
|
||
/// <summary>
|
||
/// 在窗体关闭前捕获模块控件树及其 GridView。WinForms 的 Close/Dispose
|
||
/// 流程可能会先清空 Controls 或重建 GridView;保留这个快照可以让后续
|
||
/// 引用解绑仍然针对原始实例执行。
|
||
/// </summary>
|
||
public sealed class ModuleReferenceSnapshot
|
||
{
|
||
internal readonly List<WinFormsControl> Controls;
|
||
internal readonly List<GridView> GridViews;
|
||
|
||
internal ModuleReferenceSnapshot(
|
||
List<WinFormsControl> controls,
|
||
List<GridView> gridViews)
|
||
{
|
||
Controls = controls ?? new List<WinFormsControl>();
|
||
GridViews = gridViews ?? new List<GridView>();
|
||
}
|
||
}
|
||
|
||
public static ModuleReferenceSnapshot CaptureModuleReferences(
|
||
WinFormsControl moduleRoot,
|
||
XtraTabPage modulePage)
|
||
{
|
||
List<WinFormsControl> moduleControls = new List<WinFormsControl>();
|
||
try
|
||
{
|
||
moduleControls = CollectModuleControls(moduleRoot, modulePage);
|
||
}
|
||
catch (Exception exception)
|
||
{
|
||
TraceCleanupFailure("收集模块控件引用", exception);
|
||
}
|
||
|
||
List<GridView> gridViews = new List<GridView>();
|
||
try
|
||
{
|
||
gridViews = CollectGridViews(moduleControls);
|
||
}
|
||
catch (Exception exception)
|
||
{
|
||
TraceCleanupFailure("收集模块表格引用", exception);
|
||
}
|
||
return new ModuleReferenceSnapshot(moduleControls, gridViews);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Releases static references owned by a closed legacy module. Cleanup is
|
||
/// best-effort so it cannot change the module close result.
|
||
/// </summary>
|
||
public static void ReleaseModuleReferences(WinFormsControl moduleRoot, XtraTabPage modulePage, string moduleCode)
|
||
{
|
||
ReleaseModuleReferences(
|
||
CaptureModuleReferences(moduleRoot, modulePage),
|
||
modulePage,
|
||
moduleCode);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 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.
|
||
/// </summary>
|
||
public static void ReleaseModuleReferences(
|
||
ModuleReferenceSnapshot snapshot,
|
||
XtraTabPage modulePage,
|
||
string moduleCode)
|
||
{
|
||
List<WinFormsControl> moduleControls = snapshot == null || snapshot.Controls == null
|
||
? new List<WinFormsControl>()
|
||
: snapshot.Controls;
|
||
List<GridView> gridViews = snapshot == null || snapshot.GridViews == null
|
||
? new List<GridView>()
|
||
: snapshot.GridViews;
|
||
List<TabControlEx> tabControls = new List<TabControlEx>();
|
||
try
|
||
{
|
||
tabControls = moduleControls.OfType<TabControlEx>().ToList();
|
||
}
|
||
catch (Exception exception)
|
||
{
|
||
TraceCleanupFailure("收集模块引用", exception);
|
||
}
|
||
|
||
try
|
||
{
|
||
// WPF 承载旧 WinForms 页面时,移除页签不一定会同步触发
|
||
// TabControlEx.Disposed。先解除左右联动事件,避免 GridView
|
||
// 的事件委托继续闭包持有已经关闭的 TabControlEx/模块。
|
||
foreach (TabControlEx tabControl in tabControls)
|
||
{
|
||
try
|
||
{
|
||
tabControl?.ReleaseRuntimeReferences();
|
||
}
|
||
catch (Exception exception)
|
||
{
|
||
TraceCleanupFailure("释放多页签运行时引用", exception);
|
||
}
|
||
}
|
||
|
||
// GridControlEx 自身也会把事件处理器、右键菜单和父控件
|
||
// 引用挂在 GridView/菜单对象上。这里只处理关闭前快照中的
|
||
// 控件,不调用 Control.Dispose,也不扫描全局控件集合,因而
|
||
// 不会影响仍然打开的模块或共享的 GridView 生命周期。
|
||
ReleaseGridControlRuntimeReferences(moduleControls);
|
||
|
||
foreach (GridView gridView in gridViews)
|
||
{
|
||
try
|
||
{
|
||
ReleaseGridViewReferences(gridView);
|
||
}
|
||
catch (Exception exception)
|
||
{
|
||
TraceCleanupFailure("释放表格拖拽引用", exception);
|
||
}
|
||
}
|
||
|
||
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),
|
||
"移除动态数据缓存引用");
|
||
}
|
||
}
|
||
|
||
private static void TryCleanup(Action cleanup, string operation)
|
||
{
|
||
try
|
||
{
|
||
cleanup();
|
||
}
|
||
catch (Exception exception)
|
||
{
|
||
TraceCleanupFailure(operation, exception);
|
||
}
|
||
}
|
||
|
||
private static void TraceCleanupFailure(string operation, Exception exception)
|
||
{
|
||
Debug.WriteLine("释放旧模块引用失败[" + operation + "]:" + exception);
|
||
}
|
||
|
||
private static void ReleaseGridControlRuntimeReferences(
|
||
IEnumerable<WinFormsControl> moduleControls)
|
||
{
|
||
if (moduleControls == null)
|
||
return;
|
||
|
||
HashSet<GridControlEx> released =
|
||
new HashSet<GridControlEx>(ReferenceEqualityComparer<GridControlEx>.Instance);
|
||
foreach (GridControlEx gridControl in moduleControls.OfType<GridControlEx>())
|
||
{
|
||
if (gridControl == null || !released.Add(gridControl))
|
||
continue;
|
||
|
||
try
|
||
{
|
||
gridControl.ReleaseResourcesForDispose();
|
||
}
|
||
catch (Exception exception)
|
||
{
|
||
TraceCleanupFailure("释放表格控件运行时引用", exception);
|
||
}
|
||
}
|
||
}
|
||
|
||
private static List<WinFormsControl> CollectModuleControls(
|
||
WinFormsControl moduleRoot,
|
||
XtraTabPage modulePage)
|
||
{
|
||
List<WinFormsControl> result = new List<WinFormsControl>();
|
||
AddModuleControls(result, moduleRoot);
|
||
if (!ReferenceEquals(moduleRoot, modulePage))
|
||
AddModuleControls(result, modulePage);
|
||
return result;
|
||
}
|
||
|
||
private static void AddModuleControls(
|
||
List<WinFormsControl> target,
|
||
WinFormsControl root)
|
||
{
|
||
if (root == null)
|
||
return;
|
||
|
||
foreach (WinFormsControl control in EnumerateControls(root))
|
||
if (!ContainsControl(target, control))
|
||
target.Add(control);
|
||
}
|
||
|
||
private static List<GridView> CollectGridViews(IEnumerable<WinFormsControl> controls)
|
||
{
|
||
List<GridView> result = new List<GridView>();
|
||
if (controls == null)
|
||
return result;
|
||
|
||
foreach (WinFormsControl control in controls)
|
||
{
|
||
DevExpress.XtraGrid.GridControl grid = control as DevExpress.XtraGrid.GridControl;
|
||
if (grid == null)
|
||
continue;
|
||
|
||
try
|
||
{
|
||
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);
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
private static void AddGridView(List<GridView> target, GridView view)
|
||
{
|
||
if (view != null && !ContainsGridView(target, view))
|
||
target.Add(view);
|
||
}
|
||
|
||
private static bool ContainsGridView(
|
||
IEnumerable<GridView> 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<WinFormsControl> EnumerateControls(WinFormsControl root)
|
||
{
|
||
yield return root;
|
||
foreach (WinFormsControl child in root.Controls)
|
||
foreach (WinFormsControl nested in EnumerateControls(child))
|
||
yield return nested;
|
||
}
|
||
|
||
internal static void ReleaseGridViewReferences(GridView gridView)
|
||
{
|
||
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.
|
||
BandedGridDragGrid.DisposeRegistrations(
|
||
StaticBandedControl.BandedGridViewDragGridDic,
|
||
StaticBandedControl.BandedTargetViewDragGridDic,
|
||
gridView as BandedGridView,
|
||
gridView);
|
||
GridDragGrid.ClearSourceReferenceIfUnused(gridView);
|
||
if (ReferenceEquals(_RightMenuGridView, gridView))
|
||
RightMenuGridView = null;
|
||
}
|
||
|
||
private static void RemoveConditionPanels(
|
||
string moduleCode,
|
||
IEnumerable<WinFormsControl> moduleControls)
|
||
{
|
||
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<WinFormsControl> 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<WinFormsControl> 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<WinFormsControl> 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<WinFormsControl> moduleControls,
|
||
List<GridView> gridViews)
|
||
{
|
||
if (_RightMenuGridView != null && ContainsGridView(gridViews, _RightMenuGridView))
|
||
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<string, GridControlEx>();
|
||
}
|
||
|
||
private static void RemoveModuleCacheReferences(
|
||
IEnumerable<WinFormsControl> moduleControls,
|
||
IEnumerable<GridView> gridViews)
|
||
{
|
||
if (moduleControls == null)
|
||
return;
|
||
|
||
HashSet<object> moduleReferences = new HashSet<object>(ReferenceEqualityComparer<object>.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<Dictionary<object, Hashtable>> moduleCaches =
|
||
new HashSet<Dictionary<object, Hashtable>>(ReferenceEqualityComparer<Dictionary<object, Hashtable>>.Instance);
|
||
HashSet<DynamicModel> moduleModels =
|
||
new HashSet<DynamicModel>(ReferenceEqualityComparer<DynamicModel>.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<object, Hashtable> 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;
|
||
}
|
||
|
||
private static void CollectDynamicModels(
|
||
object instance,
|
||
HashSet<Dictionary<object, Hashtable>> caches,
|
||
HashSet<DynamicModel> models)
|
||
{
|
||
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);
|
||
}
|
||
}
|
||
|
||
private static void RemoveModuleCacheEntries(
|
||
Dictionary<object, Hashtable> caches,
|
||
HashSet<object> moduleReferences)
|
||
{
|
||
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<object>().ToList())
|
||
if (moduleReferences.Contains(key))
|
||
caches.Remove(key);
|
||
}
|
||
}
|
||
|
||
private sealed class ReferenceEqualityComparer<T> : IEqualityComparer<T>
|
||
where T : class
|
||
{
|
||
public static readonly ReferenceEqualityComparer<T> Instance =
|
||
new ReferenceEqualityComparer<T>();
|
||
|
||
public bool Equals(T x, T y)
|
||
{
|
||
return ReferenceEquals(x, y);
|
||
}
|
||
|
||
public int GetHashCode(T value)
|
||
{
|
||
return value == null ? 0 : RuntimeHelpers.GetHashCode(value);
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 记录当前U盾权限窗口
|
||
/// </summary>
|
||
public static List<XtraTabPage> DogVerifyModuleForms = new List<XtraTabPage>();
|
||
public static List<IForm> DogVerifyNoPageForms = new List<IForm>();
|
||
/// <summary>
|
||
///模块打开时的条件界面
|
||
/// </summary>
|
||
public static Dictionary<string, ModuleConditionsPanelEx> ConditionsPanelDic = new Dictionary<string, ModuleConditionsPanelEx>();
|
||
/// <summary>
|
||
/// 表格拖拽类,key值为源表
|
||
/// </summary>
|
||
public static Dictionary<GridView, List<GridDragGrid>> GridViewDragGridDic = new Dictionary<GridView, List<GridDragGrid>>();
|
||
/// <summary>
|
||
/// 表格拖拽类,key值为目标表
|
||
/// </summary>
|
||
public static Dictionary<GridView, List<GridDragGrid>> TargetViewDragGridDic = new Dictionary<GridView, List<GridDragGrid>>();
|
||
/// <summary>
|
||
/// 当前拖拽源表格
|
||
/// </summary>
|
||
public static GridView SourceDragGridView;
|
||
/// <summary>
|
||
/// 指纹识别模块
|
||
/// </summary>
|
||
public static FingerHelper fingerHelper;
|
||
///// <summary>
|
||
///// 外部exe进程
|
||
///// </summary>
|
||
//public static Dictionary<ExeType, Process> loadExeProcess = new Dictionary<ExeType, Process>();
|
||
/// <summary>
|
||
/// 打开的Add模块对应的父表格控件(模块编号,GridControlEx)
|
||
/// </summary>
|
||
public static KeyValuePair<string, GridControlEx> AddParentGrid = new KeyValuePair<string, GridControlEx>();
|
||
/// <summary>
|
||
/// 当前右键点击时的表格
|
||
/// </summary>
|
||
public static GridView _RightMenuGridView;
|
||
public static GridView RightMenuGridView
|
||
{
|
||
get
|
||
{
|
||
return StaticControl._RightMenuGridView;
|
||
}
|
||
set
|
||
{
|
||
StaticControl._RightMenuGridView = value;
|
||
StaticControl.Comprefix = "";
|
||
StaticControl.ReturnRowLisy.Clear();
|
||
}
|
||
|
||
}
|
||
/// <summary>
|
||
/// bom附件修改时的关联页签
|
||
/// </summary>
|
||
public static XtraTabPage BomUnionPage;
|
||
/// <summary>
|
||
/// 当前右键点击时的控件对象
|
||
/// </summary>
|
||
public static MyControl RightMenuMyControl;
|
||
/// <summary>
|
||
/// 当前右键点击时的表格的列前缀
|
||
/// </summary>
|
||
public static string Comprefix;
|
||
/// <summary>
|
||
/// 浏览器返回数据
|
||
/// </summary>
|
||
public static List<DataRow> ReturnRowLisy = new List<DataRow>();
|
||
|
||
|
||
}
|
||
}
|