fix(winforms): release module grid resources
This commit is contained in:
@@ -629,6 +629,45 @@ namespace Lskj.Control
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void ReleaseResourcesForDispose()
|
||||||
|
{
|
||||||
|
DisposeGridResources();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DetachGridViewEventHandlers()
|
||||||
|
{
|
||||||
|
if (gridView == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gridView.CustomDrawRowIndicator -= OnGridViewCustomDrawRowIndicator;
|
||||||
|
gridView.SelectionChanged -= OnGridViewSelectionChanged;
|
||||||
|
gridView.FocusedRowObjectChanged -= OnGridViewFocusedRowObjectChanged;
|
||||||
|
gridView.BeforeLeaveRow -= OnGridView_BeforeLeaveRow;
|
||||||
|
gridView.CustomDrawCell -= OnGridViewCustomDrawCell;
|
||||||
|
gridView.CellValueChanged -= OnGridViewCellValueChanged;
|
||||||
|
gridView.DragObjectDrop -= OnGridViewDragObjectDrop;
|
||||||
|
gridView.CustomDrawGroupRow -= OnGridCustomDrawGroupRow;
|
||||||
|
gridView.KeyDown -= OnGridViewKeyDown;
|
||||||
|
gridView.DoubleClick -= OnGridViewDoubleClick;
|
||||||
|
gridView.Click -= OnGridViewClick;
|
||||||
|
gridView.Click -= OnGridView_Click;
|
||||||
|
gridView.CellMerge -= OnGridViewCellMerge;
|
||||||
|
gridView.CustomSummaryCalculate -= OnGridViewSummaryCalculate;
|
||||||
|
gridView.ShowFilterPopupCheckedListBox -= OnShowFilterPopupCheckedListBox;
|
||||||
|
gridView.DataSourceChanged -= OnGridDataSourceChanged;
|
||||||
|
gridView.RowCellStyle -= GridView_RowCellStyle;
|
||||||
|
gridView.CustomRowFilter -= GridView_CustomRowFilter;
|
||||||
|
gridView.EndGrouping -= GridView_EndGrouping;
|
||||||
|
gridView.ShowingEditor -= GridView_ShowingEditor;
|
||||||
|
gridView.MouseDown -= GridView_MouseDown;
|
||||||
|
gridView.MouseWheel -= GridView_MouseWheel;
|
||||||
|
gridView.PopupMenuShowing -= OnGridViewPopupMenuShowing;
|
||||||
|
gridView.EndSorting -= OnGridViewEndSorting;
|
||||||
|
LabPicUrlPreview.Detach(gridView);
|
||||||
|
}
|
||||||
|
|
||||||
private void DisposeGridResources()
|
private void DisposeGridResources()
|
||||||
{
|
{
|
||||||
if (_gridResourcesDisposed)
|
if (_gridResourcesDisposed)
|
||||||
@@ -673,6 +712,11 @@ namespace Lskj.Control
|
|||||||
ImageList.Clear();
|
ImageList.Clear();
|
||||||
RepeatedVerificationNames.Clear();
|
RepeatedVerificationNames.Clear();
|
||||||
PromptMessage.Clear();
|
PromptMessage.Clear();
|
||||||
|
DetachGridViewEventHandlers();
|
||||||
|
DisposeGridViews();
|
||||||
|
DisposeRepositoryItems();
|
||||||
|
gridViewRightMenu?.ReleaseResourcesForDispose();
|
||||||
|
gridViewRightMenu = null;
|
||||||
ParentControl = null;
|
ParentControl = null;
|
||||||
Model = null;
|
Model = null;
|
||||||
SysModel = null;
|
SysModel = null;
|
||||||
@@ -719,13 +763,126 @@ namespace Lskj.Control
|
|||||||
|
|
||||||
private void DisposeRepositoryItems()
|
private void DisposeRepositoryItems()
|
||||||
{
|
{
|
||||||
RepositoryItem[] repositoryItems = gridControl.RepositoryItems
|
RepositoryItem[] repositoryItems;
|
||||||
.Cast<RepositoryItem>()
|
try
|
||||||
.ToArray();
|
{
|
||||||
gridControl.RepositoryItems.Clear();
|
if (gridControl == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
repositoryItems = gridControl.RepositoryItems
|
||||||
|
.Cast<RepositoryItem>()
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// The inner control may already have released its repository collection.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DisposeRepositoryButtonForms(repositoryItems);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
gridControl.RepositoryItems.Clear();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Continue disposing the captured items.
|
||||||
|
}
|
||||||
foreach (RepositoryItem repositoryItem in repositoryItems)
|
foreach (RepositoryItem repositoryItem in repositoryItems)
|
||||||
{
|
{
|
||||||
repositoryItem.Dispose();
|
try
|
||||||
|
{
|
||||||
|
repositoryItem.Dispose();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Continue releasing the remaining repository items.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DisposeRepositoryButtonForms(IEnumerable<RepositoryItem> repositoryItems)
|
||||||
|
{
|
||||||
|
HashSet<Form> forms = new HashSet<Form>();
|
||||||
|
foreach (RepositoryItemButtonEdit buttonEditor in repositoryItems.OfType<RepositoryItemButtonEdit>())
|
||||||
|
{
|
||||||
|
foreach (EditorButton button in buttonEditor.Buttons)
|
||||||
|
{
|
||||||
|
Form form = button.Tag as Form;
|
||||||
|
if (form == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.Tag = null;
|
||||||
|
forms.Add(form);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (Form form in forms)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
form.Dispose();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// A lookup window must not prevent its owning grid from being released.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DisposeGridViews()
|
||||||
|
{
|
||||||
|
HashSet<BaseView> views = new HashSet<BaseView>();
|
||||||
|
if (gridView != null)
|
||||||
|
{
|
||||||
|
views.Add(gridView);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (gridControl != null)
|
||||||
|
{
|
||||||
|
foreach (BaseView view in gridControl.ViewCollection.Cast<BaseView>().ToArray())
|
||||||
|
{
|
||||||
|
if (view != null)
|
||||||
|
{
|
||||||
|
views.Add(view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// The inner control may already be disposing its view collection.
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (BaseView view in views)
|
||||||
|
{
|
||||||
|
GridView disposableGridView = view as GridView;
|
||||||
|
if (disposableGridView != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
StaticControl.ReleaseGridViewReferences(disposableGridView);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Static cleanup is best-effort; the view must still be disposed.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
view.Dispose();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Continue releasing the remaining views during control disposal.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ using Lskj.Business;
|
|||||||
|
|
||||||
public static class LabPicUrlPreview
|
public static class LabPicUrlPreview
|
||||||
{
|
{
|
||||||
|
private static readonly object AttachedViewsSync = new object();
|
||||||
|
private static readonly List<WeakReference> AttachedViews = new List<WeakReference>();
|
||||||
|
|
||||||
// ====== 对外:初始化图片列(创建 RepositoryItemPictureEdit、设为 Unbound) ======
|
// ====== 对外:初始化图片列(创建 RepositoryItemPictureEdit、设为 Unbound) ======
|
||||||
public static void InitPicColumn(GridControl gridControl, GridColumn gridColumn, GridColumnModel model)
|
public static void InitPicColumn(GridControl gridControl, GridColumn gridColumn, GridColumnModel model)
|
||||||
{
|
{
|
||||||
@@ -61,6 +64,7 @@ public static class LabPicUrlPreview
|
|||||||
if (view == null) return;
|
if (view == null) return;
|
||||||
view.CustomUnboundColumnData -= OnCustomUnboundColumnData;
|
view.CustomUnboundColumnData -= OnCustomUnboundColumnData;
|
||||||
view.CustomUnboundColumnData += OnCustomUnboundColumnData;
|
view.CustomUnboundColumnData += OnCustomUnboundColumnData;
|
||||||
|
TrackAttachedView(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ====== 对外:绑定“双击大图预览”的事件(只需调用一次) ======
|
// ====== 对外:绑定“双击大图预览”的事件(只需调用一次) ======
|
||||||
@@ -69,6 +73,54 @@ public static class LabPicUrlPreview
|
|||||||
if (view == null) return;
|
if (view == null) return;
|
||||||
view.DoubleClick -= OnViewDoubleClick;
|
view.DoubleClick -= OnViewDoubleClick;
|
||||||
view.DoubleClick += OnViewDoubleClick;
|
view.DoubleClick += OnViewDoubleClick;
|
||||||
|
TrackAttachedView(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Detach(ColumnView view)
|
||||||
|
{
|
||||||
|
if (view == null) return;
|
||||||
|
view.CustomUnboundColumnData -= OnCustomUnboundColumnData;
|
||||||
|
view.DoubleClick -= OnViewDoubleClick;
|
||||||
|
if (UntrackAttachedView(view))
|
||||||
|
{
|
||||||
|
ImageCache.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void TrackAttachedView(ColumnView view)
|
||||||
|
{
|
||||||
|
lock (AttachedViewsSync)
|
||||||
|
{
|
||||||
|
for (int i = AttachedViews.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
object target = AttachedViews[i].Target;
|
||||||
|
if (target == null)
|
||||||
|
{
|
||||||
|
AttachedViews.RemoveAt(i);
|
||||||
|
}
|
||||||
|
else if (ReferenceEquals(target, view))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AttachedViews.Add(new WeakReference(view));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool UntrackAttachedView(ColumnView view)
|
||||||
|
{
|
||||||
|
lock (AttachedViewsSync)
|
||||||
|
{
|
||||||
|
for (int i = AttachedViews.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
object target = AttachedViews[i].Target;
|
||||||
|
if (target == null || ReferenceEquals(target, view))
|
||||||
|
{
|
||||||
|
AttachedViews.RemoveAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return AttachedViews.Count == 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===================== 缩略图事件实现 =====================
|
// ===================== 缩略图事件实现 =====================
|
||||||
@@ -292,6 +344,12 @@ public static class LabPicUrlPreview
|
|||||||
// ====== 图片缓存(缩略图 & 原图)—— WebClient + Dictionary + Semaphore,C# 7.3 兼容 ======
|
// ====== 图片缓存(缩略图 & 原图)—— WebClient + Dictionary + Semaphore,C# 7.3 兼容 ======
|
||||||
private static class ImageCache
|
private static class ImageCache
|
||||||
{
|
{
|
||||||
|
public static void Clear()
|
||||||
|
{
|
||||||
|
ThumbCache.Clear();
|
||||||
|
FullCache.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
// —— 公共接口(缩略图) ——
|
// —— 公共接口(缩略图) ——
|
||||||
public static bool TryGetThumb(string url, int targetHeight, out Image img)
|
public static bool TryGetThumb(string url, int targetHeight, out Image img)
|
||||||
{
|
{
|
||||||
@@ -326,6 +384,7 @@ public static class LabPicUrlPreview
|
|||||||
new Dictionary<string, CacheEntry>(StringComparer.OrdinalIgnoreCase);
|
new Dictionary<string, CacheEntry>(StringComparer.OrdinalIgnoreCase);
|
||||||
private static readonly Dictionary<string, Task<Image>> _tasks =
|
private static readonly Dictionary<string, Task<Image>> _tasks =
|
||||||
new Dictionary<string, Task<Image>>(StringComparer.OrdinalIgnoreCase);
|
new Dictionary<string, Task<Image>>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
private static int _generation;
|
||||||
|
|
||||||
private static readonly Semaphore _throttle = new Semaphore(4, 4); // 并发 4
|
private static readonly Semaphore _throttle = new Semaphore(4, 4); // 并发 4
|
||||||
private const int Capacity = 300;
|
private const int Capacity = 300;
|
||||||
@@ -377,6 +436,7 @@ public static class LabPicUrlPreview
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int generation = _generation;
|
||||||
Task<Image> task = Task.Factory.StartNew<Image>(delegate
|
Task<Image> task = Task.Factory.StartNew<Image>(delegate
|
||||||
{
|
{
|
||||||
_throttle.WaitOne();
|
_throttle.WaitOne();
|
||||||
@@ -394,15 +454,21 @@ public static class LabPicUrlPreview
|
|||||||
}, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).ContinueWith<Image>(delegate (Task<Image> t)
|
}, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).ContinueWith<Image>(delegate (Task<Image> t)
|
||||||
{
|
{
|
||||||
Image result = t.Result;
|
Image result = t.Result;
|
||||||
|
bool keepResult = false;
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
_tasks.Remove(key);
|
_tasks.Remove(key);
|
||||||
if (result != null)
|
if (result != null && generation == _generation)
|
||||||
{
|
{
|
||||||
_cache[key] = new CacheEntry { Image = result, LastHitUtc = DateTime.UtcNow };
|
_cache[key] = new CacheEntry { Image = result, LastHitUtc = DateTime.UtcNow };
|
||||||
TrimIfNeededUnsafe();
|
TrimIfNeededUnsafe();
|
||||||
|
keepResult = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!keepResult && result != null)
|
||||||
|
{
|
||||||
|
try { result.Dispose(); } catch { }
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}, TaskScheduler.Default);
|
}, TaskScheduler.Default);
|
||||||
|
|
||||||
@@ -431,6 +497,27 @@ public static class LabPicUrlPreview
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Clear()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_generation++;
|
||||||
|
foreach (CacheEntry entry in _cache.Values)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (entry != null && entry.Image != null)
|
||||||
|
entry.Image.Dispose();
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
_cache.Clear();
|
||||||
|
// Drop task references immediately. In-flight continuations use
|
||||||
|
// the generation check and dispose stale images when they finish.
|
||||||
|
_tasks.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== 原图缓存实现(容量更小) =====
|
// ===== 原图缓存实现(容量更小) =====
|
||||||
@@ -447,6 +534,7 @@ public static class LabPicUrlPreview
|
|||||||
new Dictionary<string, CacheEntry>(StringComparer.OrdinalIgnoreCase);
|
new Dictionary<string, CacheEntry>(StringComparer.OrdinalIgnoreCase);
|
||||||
private static readonly Dictionary<string, Task<Image>> _tasks =
|
private static readonly Dictionary<string, Task<Image>> _tasks =
|
||||||
new Dictionary<string, Task<Image>>(StringComparer.OrdinalIgnoreCase);
|
new Dictionary<string, Task<Image>>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
private static int _generation;
|
||||||
|
|
||||||
private static readonly Semaphore _throttle = new Semaphore(2, 2); // 原图并发更小
|
private static readonly Semaphore _throttle = new Semaphore(2, 2); // 原图并发更小
|
||||||
private const int Capacity = 50;
|
private const int Capacity = 50;
|
||||||
@@ -491,6 +579,7 @@ public static class LabPicUrlPreview
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int generation = _generation;
|
||||||
Task<Image> task = Task.Factory.StartNew<Image>(delegate
|
Task<Image> task = Task.Factory.StartNew<Image>(delegate
|
||||||
{
|
{
|
||||||
_throttle.WaitOne();
|
_throttle.WaitOne();
|
||||||
@@ -509,15 +598,21 @@ public static class LabPicUrlPreview
|
|||||||
}, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).ContinueWith<Image>(delegate (Task<Image> t)
|
}, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).ContinueWith<Image>(delegate (Task<Image> t)
|
||||||
{
|
{
|
||||||
Image result = t.Result;
|
Image result = t.Result;
|
||||||
|
bool keepResult = false;
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
_tasks.Remove(url);
|
_tasks.Remove(url);
|
||||||
if (result != null)
|
if (result != null && generation == _generation)
|
||||||
{
|
{
|
||||||
_cache[url] = new CacheEntry { Image = result, LastHitUtc = DateTime.UtcNow };
|
_cache[url] = new CacheEntry { Image = result, LastHitUtc = DateTime.UtcNow };
|
||||||
TrimIfNeededUnsafe();
|
TrimIfNeededUnsafe();
|
||||||
|
keepResult = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!keepResult && result != null)
|
||||||
|
{
|
||||||
|
try { result.Dispose(); } catch { }
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}, TaskScheduler.Default);
|
}, TaskScheduler.Default);
|
||||||
|
|
||||||
@@ -546,6 +641,27 @@ public static class LabPicUrlPreview
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void Clear()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_generation++;
|
||||||
|
foreach (CacheEntry entry in _cache.Values)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (entry != null && entry.Image != null)
|
||||||
|
entry.Image.Dispose();
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
_cache.Clear();
|
||||||
|
// Drop task references immediately. In-flight continuations use
|
||||||
|
// the generation check and dispose stale images when they finish.
|
||||||
|
_tasks.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== 共享底层工具 =====
|
// ===== 共享底层工具 =====
|
||||||
|
|||||||
@@ -209,6 +209,26 @@ namespace Lskj.Control.Model
|
|||||||
|
|
||||||
this.OnRightCallback = handler;
|
this.OnRightCallback = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal virtual void ReleaseResourcesForDispose()
|
||||||
|
{
|
||||||
|
OnRightCallback = null;
|
||||||
|
OnBeforeHandleParamsCallback = null;
|
||||||
|
if (MenuStrip != null)
|
||||||
|
{
|
||||||
|
MenuStrip.Opening -= MenuStripOpening;
|
||||||
|
MenuStrip.Dispose();
|
||||||
|
MenuStrip = null;
|
||||||
|
}
|
||||||
|
RightMenuItems.Clear();
|
||||||
|
RightMenuBtnEdits.Clear();
|
||||||
|
RightMenuMrpBtnEdits.Clear();
|
||||||
|
BaseGridView = null;
|
||||||
|
ControlObj = null;
|
||||||
|
Model = null;
|
||||||
|
MenuTable = null;
|
||||||
|
AccountItem = null;
|
||||||
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <para>说明:右键菜单执行前调用</para>
|
/// <para>说明:右键菜单执行前调用</para>
|
||||||
/// <para>创建人:龚宇超</para>
|
/// <para>创建人:龚宇超</para>
|
||||||
|
|||||||
@@ -38,6 +38,19 @@ namespace Lskj.Control.Model
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal override void ReleaseResourcesForDispose()
|
||||||
|
{
|
||||||
|
if (GridControlObj != null && GridControlObj.gridControl != null &&
|
||||||
|
ReferenceEquals(GridControlObj.gridControl.ContextMenuStrip, MenuStrip))
|
||||||
|
{
|
||||||
|
GridControlObj.gridControl.ContextMenuStrip = null;
|
||||||
|
}
|
||||||
|
GridControlObj = null;
|
||||||
|
_gridView = null;
|
||||||
|
_control = null;
|
||||||
|
base.ReleaseResourcesForDispose();
|
||||||
|
}
|
||||||
|
|
||||||
protected override void MenuStripOpening(object sender, System.ComponentModel.CancelEventArgs e)
|
protected override void MenuStripOpening(object sender, System.ComponentModel.CancelEventArgs e)
|
||||||
{
|
{
|
||||||
int[] mSelectRows = this._gridView.GetSelectedRows() ?? new int[0];
|
int[] mSelectRows = this._gridView.GetSelectedRows() ?? new int[0];
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ namespace Lskj.Control.Model
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
RemoveGridReferences(gridView);
|
ReleaseGridViewReferences(gridView);
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
@@ -103,12 +103,18 @@ namespace Lskj.Control.Model
|
|||||||
yield return nested;
|
yield return nested;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void RemoveGridReferences(GridView gridView)
|
internal static void ReleaseGridViewReferences(GridView gridView)
|
||||||
{
|
{
|
||||||
|
if (gridView == null)
|
||||||
|
return;
|
||||||
|
|
||||||
GridDragGrid.DisposeRegistrations(GridViewDragGridDic, TargetViewDragGridDic, gridView);
|
GridDragGrid.DisposeRegistrations(GridViewDragGridDic, TargetViewDragGridDic, gridView);
|
||||||
if (ReferenceEquals(SourceDragGridView, gridView) ||
|
if (ReferenceEquals(SourceDragGridView, gridView) ||
|
||||||
(SourceDragGridView != null && SourceDragGridView.GridControl != null && SourceDragGridView.GridControl.IsDisposed))
|
(SourceDragGridView != null && SourceDragGridView.GridControl != null && SourceDragGridView.GridControl.IsDisposed))
|
||||||
SourceDragGridView = null;
|
SourceDragGridView = null;
|
||||||
|
if (ReferenceEquals(_RightMenuGridView, gridView) ||
|
||||||
|
(_RightMenuGridView != null && _RightMenuGridView.GridControl != null && _RightMenuGridView.GridControl.IsDisposed))
|
||||||
|
RightMenuGridView = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void RemoveConditionPanels(string moduleCode)
|
private static void RemoveConditionPanels(string moduleCode)
|
||||||
|
|||||||
@@ -165,6 +165,9 @@ namespace Lskj.Control
|
|||||||
/// 按钮形式展示的右键
|
/// 按钮形式展示的右键
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private List<SimpleButton> RightButtons = new List<SimpleButton>();
|
private List<SimpleButton> RightButtons = new List<SimpleButton>();
|
||||||
|
// Fonts assigned to dynamic controls are not reliably reclaimed by the
|
||||||
|
// DevExpress/WinForms control tree, so keep ownership explicit.
|
||||||
|
private readonly List<Font> _ownedFonts = new List<Font>();
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 附加右键菜单集合
|
/// 附加右键菜单集合
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1298,7 +1301,9 @@ namespace Lskj.Control
|
|||||||
if (FontSize > 0)
|
if (FontSize > 0)
|
||||||
{
|
{
|
||||||
//this.gcMain.GridView.Appearance.Row.Font = new Font("微软雅黑", FontSize);
|
//this.gcMain.GridView.Appearance.Row.Font = new Font("微软雅黑", FontSize);
|
||||||
this.gcMain.GridView.Appearance.Row.Font = new Font(this.gcMain.GridView.Appearance.Row.Font.FontFamily, FontSize);
|
Font rowFont = new Font(this.gcMain.GridView.Appearance.Row.Font.FontFamily, FontSize);
|
||||||
|
this.gcMain.GridView.Appearance.Row.Font = rowFont;
|
||||||
|
_ownedFonts.Add(rowFont);
|
||||||
}
|
}
|
||||||
if (SysModel.RowHeight > 0)
|
if (SysModel.RowHeight > 0)
|
||||||
{
|
{
|
||||||
@@ -3792,6 +3797,27 @@ namespace Lskj.Control
|
|||||||
}
|
}
|
||||||
gcMain.Model = null;
|
gcMain.Model = null;
|
||||||
gcMain.SysModel = null;
|
gcMain.SysModel = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
gcMain.ReleaseResourcesForDispose();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Resource cleanup must not block the module close path.
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!gcMain.IsDisposed)
|
||||||
|
{
|
||||||
|
gcMain.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// A partially disposed grid must not block the module close path.
|
||||||
|
}
|
||||||
|
gcMain = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@@ -3826,8 +3852,9 @@ namespace Lskj.Control
|
|||||||
m_dragRowShadow.Dispose();
|
m_dragRowShadow.Dispose();
|
||||||
m_dragRowShadow = null;
|
m_dragRowShadow = null;
|
||||||
}
|
}
|
||||||
|
DisposeDynamicRightButtons();
|
||||||
|
DisposeOwnedFonts();
|
||||||
ButtonModeRightMenus.Clear();
|
ButtonModeRightMenus.Clear();
|
||||||
RightButtons.Clear();
|
|
||||||
itemCommonList.Clear();
|
itemCommonList.Clear();
|
||||||
LineStatus.Clear();
|
LineStatus.Clear();
|
||||||
_leftGridSearchObj = null;
|
_leftGridSearchObj = null;
|
||||||
@@ -4053,6 +4080,39 @@ namespace Lskj.Control
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DisposeDynamicRightButtons()
|
||||||
|
{
|
||||||
|
foreach (SimpleButton button in RightButtons.ToList())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
button.Click -= OnModuleClick;
|
||||||
|
button.Dispose();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Cleanup must continue for the remaining controls.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RightButtons.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DisposeOwnedFonts()
|
||||||
|
{
|
||||||
|
foreach (Font font in _ownedFonts.ToList())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
font.Dispose();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// A partially disposed font must not stop module cleanup.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ownedFonts.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
private void Txt_SweepCode_KeyDown(object sender, KeyEventArgs e)
|
private void Txt_SweepCode_KeyDown(object sender, KeyEventArgs e)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -5785,7 +5845,9 @@ namespace Lskj.Control
|
|||||||
{
|
{
|
||||||
GridRightMenuModel menuModel = new GridRightMenuModel(item);
|
GridRightMenuModel menuModel = new GridRightMenuModel(item);
|
||||||
SimpleButton button = new SimpleButton();
|
SimpleButton button = new SimpleButton();
|
||||||
button.Font = new Font("宋体", 9);
|
Font buttonFont = new Font("宋体", 9);
|
||||||
|
button.Font = buttonFont;
|
||||||
|
_ownedFonts.Add(buttonFont);
|
||||||
button.Text = menuModel.MenuName;
|
button.Text = menuModel.MenuName;
|
||||||
button.Height = 24;
|
button.Height = 24;
|
||||||
//按钮最小大小
|
//按钮最小大小
|
||||||
|
|||||||
@@ -7433,104 +7433,123 @@ namespace Lskj.PubBill
|
|||||||
/// <param name="e">The <see cref="PaintEventArgs"/> instance containing the event data.</param>
|
/// <param name="e">The <see cref="PaintEventArgs"/> instance containing the event data.</param>
|
||||||
private void OnLabelTitlePaint(object sender, PaintEventArgs e)
|
private void OnLabelTitlePaint(object sender, PaintEventArgs e)
|
||||||
{
|
{
|
||||||
if (this._waterMark != null)
|
if (this._waterMark == null)
|
||||||
{
|
return;
|
||||||
GraphicsText graphicsText = new GraphicsText();
|
|
||||||
graphicsText.Graphics = e.Graphics;
|
|
||||||
|
|
||||||
// 绘制围绕点旋转的文本
|
GraphicsText graphicsText = new GraphicsText();
|
||||||
StringFormat format = new StringFormat();
|
graphicsText.Graphics = e.Graphics;
|
||||||
|
|
||||||
|
// Every paint can allocate native GDI handles. Keep all temporary drawing
|
||||||
|
// objects scoped to this paint so closing a module can release them.
|
||||||
|
using (StringFormat format = new StringFormat())
|
||||||
|
{
|
||||||
format.Alignment = StringAlignment.Center;
|
format.Alignment = StringAlignment.Center;
|
||||||
format.LineAlignment = StringAlignment.Center;
|
format.LineAlignment = StringAlignment.Center;
|
||||||
|
|
||||||
// 绘制单据状态文本
|
|
||||||
if (!string.IsNullOrEmpty(this._waterMark.Text))
|
if (!string.IsNullOrEmpty(this._waterMark.Text))
|
||||||
{
|
{
|
||||||
|
using (Font font = new Font("宋体", 12, FontStyle.Bold))
|
||||||
|
using (SolidBrush brush = new SolidBrush(Color.Red))
|
||||||
if (Business.Impl.LanguageTranslation.Translatable)
|
|
||||||
{
|
{
|
||||||
graphicsText.DrawString(this._waterMark.Text, new Font("宋体", 12, FontStyle.Bold), new SolidBrush(Color.Red), new PointF(50, 21), format, 0f);
|
graphicsText.DrawString(this._waterMark.Text, font, brush, new PointF(50, 21), format,
|
||||||
// 测量文本实际尺寸(含字体和格式影响)
|
Business.Impl.LanguageTranslation.Translatable ? 0f : -15f);
|
||||||
SizeF textSize = e.Graphics.MeasureString(this._waterMark.Text, new Font("宋体", 12, FontStyle.Bold), new SizeF(float.MaxValue, float.MaxValue), format);
|
|
||||||
int rectWidth = (int)textSize.Width + 10;
|
|
||||||
int rectHeight = (int)textSize.Height + 5;
|
|
||||||
// 文本中心点是(50,21),计算边框左上角坐标(确保边框围绕文本居中)
|
|
||||||
int rectX = 50 - rectWidth / 2;
|
|
||||||
int rectY = 21 - rectHeight / 2;
|
|
||||||
|
|
||||||
e.Graphics.DrawRectangle(new Pen(Color.Red, 2f), new Rectangle(rectX, rectY, rectWidth, 21));
|
if (Business.Impl.LanguageTranslation.Translatable)
|
||||||
|
{
|
||||||
|
SizeF textSize = e.Graphics.MeasureString(this._waterMark.Text, font,
|
||||||
|
new SizeF(float.MaxValue, float.MaxValue), format);
|
||||||
|
int rectWidth = (int)textSize.Width + 10;
|
||||||
|
int rectHeight = (int)textSize.Height + 5;
|
||||||
|
int rectX = 50 - rectWidth / 2;
|
||||||
|
int rectY = 21 - rectHeight / 2;
|
||||||
|
using (Pen pen = new Pen(Color.Red, 2f))
|
||||||
|
{
|
||||||
|
e.Graphics.DrawRectangle(pen, new Rectangle(rectX, rectY, rectWidth, 21));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
e.Graphics.TranslateTransform(0, 0);
|
||||||
|
e.Graphics.RotateTransform(-15f);
|
||||||
|
using (Pen pen = new Pen(Color.Red, 2f))
|
||||||
|
{
|
||||||
|
e.Graphics.DrawRectangle(pen, new Rectangle(14, 20, 55, 21));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
graphicsText.DrawString(this._waterMark.Text, new Font("宋体", 12, FontStyle.Bold), new SolidBrush(Color.Red), new PointF(50, 21), format, -15f);
|
|
||||||
|
|
||||||
e.Graphics.TranslateTransform(0, 0);
|
|
||||||
e.Graphics.RotateTransform(-15f);
|
|
||||||
e.Graphics.DrawRectangle(new Pen(Color.Red, 2f), new Rectangle(14, 20, 55, 21));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 绘制打印次数文本
|
|
||||||
e.Graphics.ResetTransform();
|
e.Graphics.ResetTransform();
|
||||||
if (!string.IsNullOrEmpty(this._waterMark.Print))
|
if (!string.IsNullOrEmpty(this._waterMark.Print))
|
||||||
{
|
{
|
||||||
|
int x = this.lbTitle.Width - 70;
|
||||||
|
using (Font font = new Font("宋体", 11, FontStyle.Bold))
|
||||||
if (Business.Impl.LanguageTranslation.Translatable)
|
using (SolidBrush brush = new SolidBrush(Color.Red))
|
||||||
{
|
{
|
||||||
graphicsText.DrawString(this._waterMark.Print, new Font("宋体", 11, FontStyle.Bold), new SolidBrush(Color.Red), new PointF((this.lbTitle.Width - 70), 21), format, 0f);
|
graphicsText.DrawString(this._waterMark.Print, font, brush, new PointF(x, 21), format,
|
||||||
// 测量文本实际尺寸(含字体和格式影响)
|
Business.Impl.LanguageTranslation.Translatable ? 0f : -15f);
|
||||||
SizeF textSize = e.Graphics.MeasureString(this._waterMark.Print, new Font("宋体", 11, FontStyle.Bold), new SizeF(float.MaxValue, float.MaxValue), format);
|
|
||||||
int rectWidth = (int)textSize.Width + 10;
|
|
||||||
int rectHeight = (int)textSize.Height + 5;
|
|
||||||
// 文本中心点是(this.lbTitle.Width - 70,21),计算边框左上角坐标(确保边框围绕文本居中)
|
|
||||||
int rectX = (this.lbTitle.Width - 70) - rectWidth / 2;
|
|
||||||
int rectY = 21 - rectHeight / 2;
|
|
||||||
|
|
||||||
e.Graphics.DrawRectangle(new Pen(Color.Red, 2f), new Rectangle(rectX, rectY, rectWidth, 21));
|
if (Business.Impl.LanguageTranslation.Translatable)
|
||||||
|
{
|
||||||
|
SizeF textSize = e.Graphics.MeasureString(this._waterMark.Print, font,
|
||||||
|
new SizeF(float.MaxValue, float.MaxValue), format);
|
||||||
|
int rectWidth = (int)textSize.Width + 10;
|
||||||
|
int rectHeight = (int)textSize.Height + 5;
|
||||||
|
int rectX = x - rectWidth / 2;
|
||||||
|
int rectY = 21 - rectHeight / 2;
|
||||||
|
using (Pen pen = new Pen(Color.Red, 2f))
|
||||||
|
{
|
||||||
|
e.Graphics.DrawRectangle(pen, new Rectangle(rectX, rectY, rectWidth, 21));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
e.Graphics.TranslateTransform(x, 21);
|
||||||
|
e.Graphics.RotateTransform(-15f);
|
||||||
|
using (Pen pen = new Pen(Color.Red, 2f))
|
||||||
|
{
|
||||||
|
e.Graphics.DrawRectangle(pen, new Rectangle(-35, -14, 70, 21));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
graphicsText.DrawString(this._waterMark.Print, new Font("宋体", 11, FontStyle.Bold), new SolidBrush(Color.Red), new PointF((this.lbTitle.Width - 70), 21), format, -15f);
|
|
||||||
e.Graphics.TranslateTransform(this.lbTitle.Width - 70, 21);
|
|
||||||
e.Graphics.RotateTransform(-15f);
|
|
||||||
e.Graphics.DrawRectangle(new Pen(Color.Red, 2f), new Rectangle(-35, -14, 70, 21));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 绘制收款文本
|
|
||||||
e.Graphics.ResetTransform();
|
e.Graphics.ResetTransform();
|
||||||
if (!string.IsNullOrEmpty(this._waterMark.Collect))
|
if (!string.IsNullOrEmpty(this._waterMark.Collect))
|
||||||
{
|
{
|
||||||
|
int x = this.lbTitle.Width - 170;
|
||||||
|
using (Font font = new Font("宋体", 12, FontStyle.Bold))
|
||||||
if (Business.Impl.LanguageTranslation.Translatable)
|
using (SolidBrush brush = new SolidBrush(Color.Red))
|
||||||
{
|
{
|
||||||
graphicsText.DrawString(this._waterMark.Collect, new Font("宋体", 12, FontStyle.Bold), new SolidBrush(Color.Red), new PointF((this.lbTitle.Width - 170), 21), format, 0f);
|
graphicsText.DrawString(this._waterMark.Collect, font, brush, new PointF(x, 21), format,
|
||||||
// 测量文本实际尺寸(含字体和格式影响)
|
Business.Impl.LanguageTranslation.Translatable ? 0f : -15f);
|
||||||
SizeF textSize = e.Graphics.MeasureString(this._waterMark.Collect, new Font("宋体", 12, FontStyle.Bold), new SizeF(float.MaxValue, float.MaxValue), format);
|
|
||||||
int rectWidth = (int)textSize.Width + 10;
|
|
||||||
int rectHeight = (int)textSize.Height + 5;
|
|
||||||
// 文本中心点是(this.lbTitle.Width - 170,21),计算边框左上角坐标(确保边框围绕文本居中)
|
|
||||||
int rectX = (this.lbTitle.Width - 170) - rectWidth / 2;
|
|
||||||
int rectY = 21 - rectHeight / 2;
|
|
||||||
|
|
||||||
e.Graphics.DrawRectangle(new Pen(Color.Red, 2f), new Rectangle(rectX, rectY, rectWidth, 21));
|
if (Business.Impl.LanguageTranslation.Translatable)
|
||||||
|
{
|
||||||
|
SizeF textSize = e.Graphics.MeasureString(this._waterMark.Collect, font,
|
||||||
|
new SizeF(float.MaxValue, float.MaxValue), format);
|
||||||
|
int rectWidth = (int)textSize.Width + 10;
|
||||||
|
int rectHeight = (int)textSize.Height + 5;
|
||||||
|
int rectX = x - rectWidth / 2;
|
||||||
|
int rectY = 21 - rectHeight / 2;
|
||||||
|
using (Pen pen = new Pen(Color.Red, 2f))
|
||||||
|
{
|
||||||
|
e.Graphics.DrawRectangle(pen, new Rectangle(rectX, rectY, rectWidth, 21));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
e.Graphics.TranslateTransform(this.lbTitle.Width - 100, 21);
|
||||||
|
e.Graphics.RotateTransform(-15f);
|
||||||
|
using (Pen pen = new Pen(Color.Red, 2f))
|
||||||
|
{
|
||||||
|
e.Graphics.DrawRectangle(pen, new Rectangle(-100, -32, 55, 21));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
graphicsText.DrawString(this._waterMark.Collect, new Font("宋体", 12, FontStyle.Bold), new SolidBrush(Color.Red), new PointF((this.lbTitle.Width - 170), 21), format, -15f);
|
|
||||||
|
|
||||||
e.Graphics.TranslateTransform(this.lbTitle.Width - 100, 21);
|
|
||||||
e.Graphics.RotateTransform(-15f);
|
|
||||||
e.Graphics.DrawRectangle(new Pen(Color.Red, 2f), new Rectangle(-100, -32, 55, 21));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
e.Graphics.ResetTransform();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -154,7 +154,11 @@ namespace Lskj.PubModuleDetail
|
|||||||
{
|
{
|
||||||
this.mControl.CloseModule();
|
this.mControl.CloseModule();
|
||||||
string guid = this.Tag + "";
|
string guid = this.Tag + "";
|
||||||
if (string.IsNullOrWhiteSpace(guid)) return;
|
if (string.IsNullOrWhiteSpace(guid))
|
||||||
|
{
|
||||||
|
this.mControl.ReleaseResourcesForDispose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
DllModule m = Manager.ModuleForms[guid];
|
DllModule m = Manager.ModuleForms[guid];
|
||||||
m.IsClose = true;
|
m.IsClose = true;
|
||||||
if (SystemInfo.Instance.EfficientVerification)
|
if (SystemInfo.Instance.EfficientVerification)
|
||||||
@@ -175,6 +179,15 @@ namespace Lskj.PubModuleDetail
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FormClosing fires before the control tree is disposed. Release the
|
||||||
|
// DevExpress grids and their native resources as soon as the close is
|
||||||
|
// confirmed; waiting for FrmMain.Dispose leaves GDI handles alive
|
||||||
|
// between module cycles.
|
||||||
|
if (!e.Cancel)
|
||||||
|
{
|
||||||
|
this.mControl.ReleaseResourcesForDispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user