Files
lserp_cs_6.0/插件库/Lskj.Control/Model/StaticControl.cs
T

583 lines
23 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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>();
}
}