using DevExpress.XtraGrid.Views.Grid; using DevExpress.XtraGrid.Views.BandedGrid; using DevExpress.XtraTab; using Lskj.Control.ZKFinger; using Lskj.Model; using Lskj.Util; using System; using System.Collections.Generic; using System.Data; using System.Runtime.CompilerServices; using System.Windows.Forms; 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 { private static readonly object ModuleRegistrySync = new object(); /// /// Owns cleanup callbacks for one opened module instance. /// public sealed class ModuleResourceScope : IDisposable { private sealed class Registration { internal readonly object Owner; internal readonly Action Cleanup; 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 { RegisterOwnedControl(owner); } finally { _currentModuleScope = previous; } return; } ModuleResourceScope current; if (!ModuleScopeOwners.TryGetValue(owner, out current) || !ReferenceEquals(current, scope)) { return; } owner.ParentChanged -= OnOwnedControlParentChanged; ModuleScopeOwners.Remove(owner); if (!owner.IsDisposed) owner.Dispose(); }); } private static ModuleResourceScope FindParentModuleScope(WinFormsControl owner) { for (WinFormsControl current = owner == null ? null : owner.Parent; current != null; current = current.Parent) { ModuleResourceScope scope; if (ModuleScopeOwners.TryGetValue(current, out scope) && scope != null && !scope.IsDisposed) return scope; } return null; } private static void OnOwnedControlParentChanged(object sender, EventArgs args) { WinFormsControl owner = sender as WinFormsControl; if (owner == null || owner.IsDisposed) return; ModuleResourceScope parentScope = FindParentModuleScope(owner); ModuleResourceScope currentScope; if (parentScope != null && (!ModuleScopeOwners.TryGetValue(owner, out currentScope) || !ReferenceEquals(parentScope, currentScope))) { ModuleResourceScope previous = _currentModuleScope; _currentModuleScope = parentScope; try { RegisterOwnedControl(owner); } finally { _currentModuleScope = previous; } } } public static void RegisterModuleSearchControl( WinFormsControl context, MyControl searchControl) { if (searchControl == null) return; ModuleResourceScope scope = context == null ? GetLiveCurrentModuleScope() : FindModuleScope(context); if (scope == null || string.IsNullOrWhiteSpace(scope.ModuleId)) return; string moduleId = scope.ModuleId; lock (ModuleRegistrySync) { MyControl existing; if (ModuleSearchControls.TryGetValue(moduleId, out existing)) { if (ReferenceEquals(existing, searchControl)) return; if (existing != null && !existing.IsDisposed) return; ModuleSearchControls.Remove(moduleId); } ModuleSearchControls[moduleId] = searchControl; } 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); } } }); } public static MyControl FindModuleSearchControl(string moduleId) { 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; } } public static void RegisterConditionPanel( string key, ModuleConditionsPanelEx panel) { 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(); } }); } public static void RegisterProtectedPage(XtraTabPage page) { 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) { if (gridView == null) return; GridDragGrid.DisposeRegistrations( GridViewDragGridDic, TargetViewDragGridDic, gridView); BandedGridDragGrid.DisposeRegistrations( StaticBandedControl.BandedGridViewDragGridDic, StaticBandedControl.BandedTargetViewDragGridDic, gridView as BandedGridView, gridView); GridDragGrid.ClearSourceReferenceIfUnused(gridView); if (ReferenceEquals(_RightMenuGridView, gridView)) RightMenuGridView = null; } private static void OnRightMenuGridViewDisposed(object sender, EventArgs args) { GridView view = sender as GridView; if (ReferenceEquals(_RightMenuGridView, view)) RightMenuGridView = null; } private static void OnBomUnionPageDisposed(object sender, EventArgs args) { XtraTabPage page = sender as XtraTabPage; if (ReferenceEquals(BomUnionPage, page)) SetBomUnionPage(null); } private static void OnRightMenuMyControlDisposed(object sender, EventArgs args) { MyControl control = sender as MyControl; if (ReferenceEquals(RightMenuMyControl, control)) SetRightMenuMyControl(null); } private static void OnAddParentGridDisposed(object sender, EventArgs args) { GridControlEx grid = sender as GridControlEx; if (ReferenceEquals(AddParentGrid.Value, grid)) SetAddParentGrid(new KeyValuePair()); } /// /// 记录当前U盾权限窗口 /// public static List DogVerifyModuleForms = new List(); public static List DogVerifyNoPageForms = new List(); /// /// 模块打开时的条件界面 /// public static Dictionary ConditionsPanelDic = new Dictionary(); /// /// 表格拖拽类,key值为源表 /// public static Dictionary> GridViewDragGridDic = new Dictionary>(); /// /// 表格拖拽类,key值为目标表 /// public static Dictionary> TargetViewDragGridDic = new Dictionary>(); /// /// 当前拖拽源表格 /// public static GridView SourceDragGridView; /// /// 指纹识别模块 /// public static FingerHelper fingerHelper; 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 { get { return _RightMenuGridView; } set { 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; }); } /// /// 当前右键点击时的表格的列前缀 /// public static string Comprefix; /// /// 浏览器返回数据 /// public static List ReturnRowLisy = new List(); } }