diff --git a/插件库/Lskj.Control/GridControlEx.cs b/插件库/Lskj.Control/GridControlEx.cs index 0a263c8..4911e2d 100644 --- a/插件库/Lskj.Control/GridControlEx.cs +++ b/插件库/Lskj.Control/GridControlEx.cs @@ -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() { if (_gridResourcesDisposed) @@ -673,6 +712,11 @@ namespace Lskj.Control ImageList.Clear(); RepeatedVerificationNames.Clear(); PromptMessage.Clear(); + DetachGridViewEventHandlers(); + DisposeGridViews(); + DisposeRepositoryItems(); + gridViewRightMenu?.ReleaseResourcesForDispose(); + gridViewRightMenu = null; ParentControl = null; Model = null; SysModel = null; @@ -719,13 +763,126 @@ namespace Lskj.Control private void DisposeRepositoryItems() { - RepositoryItem[] repositoryItems = gridControl.RepositoryItems - .Cast() - .ToArray(); - gridControl.RepositoryItems.Clear(); + RepositoryItem[] repositoryItems; + try + { + if (gridControl == null) + { + return; + } + + repositoryItems = gridControl.RepositoryItems + .Cast() + .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) { - repositoryItem.Dispose(); + try + { + repositoryItem.Dispose(); + } + catch + { + // Continue releasing the remaining repository items. + } + } + } + + private static void DisposeRepositoryButtonForms(IEnumerable repositoryItems) + { + HashSet
forms = new HashSet(); + foreach (RepositoryItemButtonEdit buttonEditor in repositoryItems.OfType()) + { + 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 views = new HashSet(); + if (gridView != null) + { + views.Add(gridView); + } + + try + { + if (gridControl != null) + { + foreach (BaseView view in gridControl.ViewCollection.Cast().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. + } } } diff --git a/插件库/Lskj.Control/Model/LabPicUrlPreview.cs b/插件库/Lskj.Control/Model/LabPicUrlPreview.cs index 2c933ed..98fb5e7 100644 --- a/插件库/Lskj.Control/Model/LabPicUrlPreview.cs +++ b/插件库/Lskj.Control/Model/LabPicUrlPreview.cs @@ -31,6 +31,9 @@ using Lskj.Business; public static class LabPicUrlPreview { + private static readonly object AttachedViewsSync = new object(); + private static readonly List AttachedViews = new List(); + // ====== 对外:初始化图片列(创建 RepositoryItemPictureEdit、设为 Unbound) ====== public static void InitPicColumn(GridControl gridControl, GridColumn gridColumn, GridColumnModel model) { @@ -61,6 +64,7 @@ public static class LabPicUrlPreview if (view == null) return; view.CustomUnboundColumnData -= OnCustomUnboundColumnData; view.CustomUnboundColumnData += OnCustomUnboundColumnData; + TrackAttachedView(view); } // ====== 对外:绑定“双击大图预览”的事件(只需调用一次) ====== @@ -69,6 +73,54 @@ public static class LabPicUrlPreview if (view == null) return; 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 兼容 ====== private static class ImageCache { + public static void Clear() + { + ThumbCache.Clear(); + FullCache.Clear(); + } + // —— 公共接口(缩略图) —— public static bool TryGetThumb(string url, int targetHeight, out Image img) { @@ -326,6 +384,7 @@ public static class LabPicUrlPreview new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary> _tasks = new Dictionary>(StringComparer.OrdinalIgnoreCase); + private static int _generation; private static readonly Semaphore _throttle = new Semaphore(4, 4); // 并发 4 private const int Capacity = 300; @@ -377,6 +436,7 @@ public static class LabPicUrlPreview return; } + int generation = _generation; Task task = Task.Factory.StartNew(delegate { _throttle.WaitOne(); @@ -394,15 +454,21 @@ public static class LabPicUrlPreview }, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).ContinueWith(delegate (Task t) { Image result = t.Result; + bool keepResult = false; lock (_lock) { _tasks.Remove(key); - if (result != null) + if (result != null && generation == _generation) { _cache[key] = new CacheEntry { Image = result, LastHitUtc = DateTime.UtcNow }; TrimIfNeededUnsafe(); + keepResult = true; } } + if (!keepResult && result != null) + { + try { result.Dispose(); } catch { } + } return result; }, 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(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary> _tasks = new Dictionary>(StringComparer.OrdinalIgnoreCase); + private static int _generation; private static readonly Semaphore _throttle = new Semaphore(2, 2); // 原图并发更小 private const int Capacity = 50; @@ -491,6 +579,7 @@ public static class LabPicUrlPreview return; } + int generation = _generation; Task task = Task.Factory.StartNew(delegate { _throttle.WaitOne(); @@ -509,15 +598,21 @@ public static class LabPicUrlPreview }, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).ContinueWith(delegate (Task t) { Image result = t.Result; + bool keepResult = false; lock (_lock) { _tasks.Remove(url); - if (result != null) + if (result != null && generation == _generation) { _cache[url] = new CacheEntry { Image = result, LastHitUtc = DateTime.UtcNow }; TrimIfNeededUnsafe(); + keepResult = true; } } + if (!keepResult && result != null) + { + try { result.Dispose(); } catch { } + } return result; }, 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(); + } + } } // ===== 共享底层工具 ===== diff --git a/插件库/Lskj.Control/Model/MenuStrip/BaseMenuStrip.cs b/插件库/Lskj.Control/Model/MenuStrip/BaseMenuStrip.cs index 9a059e0..a1a232b 100644 --- a/插件库/Lskj.Control/Model/MenuStrip/BaseMenuStrip.cs +++ b/插件库/Lskj.Control/Model/MenuStrip/BaseMenuStrip.cs @@ -209,6 +209,26 @@ namespace Lskj.Control.Model 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; + } /// /// 说明:右键菜单执行前调用 /// 创建人:龚宇超 diff --git a/插件库/Lskj.Control/Model/MenuStrip/GridViewMenuStrip.cs b/插件库/Lskj.Control/Model/MenuStrip/GridViewMenuStrip.cs index d1f6e15..9208070 100644 --- a/插件库/Lskj.Control/Model/MenuStrip/GridViewMenuStrip.cs +++ b/插件库/Lskj.Control/Model/MenuStrip/GridViewMenuStrip.cs @@ -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) { int[] mSelectRows = this._gridView.GetSelectedRows() ?? new int[0]; diff --git a/插件库/Lskj.Control/Model/StaticControl.cs b/插件库/Lskj.Control/Model/StaticControl.cs index 45bb199..412fb15 100644 --- a/插件库/Lskj.Control/Model/StaticControl.cs +++ b/插件库/Lskj.Control/Model/StaticControl.cs @@ -40,7 +40,7 @@ namespace Lskj.Control.Model { try { - RemoveGridReferences(gridView); + ReleaseGridViewReferences(gridView); } catch (Exception exception) { @@ -103,12 +103,18 @@ namespace Lskj.Control.Model yield return nested; } - private static void RemoveGridReferences(GridView gridView) + internal static void ReleaseGridViewReferences(GridView gridView) { + if (gridView == null) + return; + GridDragGrid.DisposeRegistrations(GridViewDragGridDic, TargetViewDragGridDic, gridView); if (ReferenceEquals(SourceDragGridView, gridView) || (SourceDragGridView != null && SourceDragGridView.GridControl != null && SourceDragGridView.GridControl.IsDisposed)) SourceDragGridView = null; + if (ReferenceEquals(_RightMenuGridView, gridView) || + (_RightMenuGridView != null && _RightMenuGridView.GridControl != null && _RightMenuGridView.GridControl.IsDisposed)) + RightMenuGridView = null; } private static void RemoveConditionPanels(string moduleCode) diff --git a/插件库/Lskj.Control/ModuleGridEx.cs b/插件库/Lskj.Control/ModuleGridEx.cs index 91584ad..2d6670d 100644 --- a/插件库/Lskj.Control/ModuleGridEx.cs +++ b/插件库/Lskj.Control/ModuleGridEx.cs @@ -165,6 +165,9 @@ namespace Lskj.Control /// 按钮形式展示的右键 /// private List RightButtons = new List(); + // Fonts assigned to dynamic controls are not reliably reclaimed by the + // DevExpress/WinForms control tree, so keep ownership explicit. + private readonly List _ownedFonts = new List(); /// /// 附加右键菜单集合 /// @@ -1298,7 +1301,9 @@ namespace Lskj.Control if (FontSize > 0) { //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) { @@ -3792,6 +3797,27 @@ namespace Lskj.Control } gcMain.Model = 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 @@ -3826,8 +3852,9 @@ namespace Lskj.Control m_dragRowShadow.Dispose(); m_dragRowShadow = null; } + DisposeDynamicRightButtons(); + DisposeOwnedFonts(); ButtonModeRightMenus.Clear(); - RightButtons.Clear(); itemCommonList.Clear(); LineStatus.Clear(); _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) { try @@ -5785,7 +5845,9 @@ namespace Lskj.Control { GridRightMenuModel menuModel = new GridRightMenuModel(item); 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.Height = 24; //按钮最小大小 diff --git a/插件库/Lskj.PubBill/BillModule.cs b/插件库/Lskj.PubBill/BillModule.cs index cded4fb..9a89144 100644 --- a/插件库/Lskj.PubBill/BillModule.cs +++ b/插件库/Lskj.PubBill/BillModule.cs @@ -7433,104 +7433,123 @@ namespace Lskj.PubBill /// The instance containing the event data. private void OnLabelTitlePaint(object sender, PaintEventArgs e) { - if (this._waterMark != null) - { - GraphicsText graphicsText = new GraphicsText(); - graphicsText.Graphics = e.Graphics; + if (this._waterMark == null) + return; - // 绘制围绕点旋转的文本 - StringFormat format = new StringFormat(); + GraphicsText graphicsText = new GraphicsText(); + 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.LineAlignment = StringAlignment.Center; - // 绘制单据状态文本 if (!string.IsNullOrEmpty(this._waterMark.Text)) { - - - if (Business.Impl.LanguageTranslation.Translatable) + using (Font font = new Font("宋体", 12, FontStyle.Bold)) + using (SolidBrush brush = new SolidBrush(Color.Red)) { - graphicsText.DrawString(this._waterMark.Text, new Font("宋体", 12, FontStyle.Bold), new SolidBrush(Color.Red), new PointF(50, 21), format, 0f); - // 测量文本实际尺寸(含字体和格式影响) - 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; + graphicsText.DrawString(this._waterMark.Text, font, brush, new PointF(50, 21), format, + Business.Impl.LanguageTranslation.Translatable ? 0f : -15f); - 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(); if (!string.IsNullOrEmpty(this._waterMark.Print)) { - - - if (Business.Impl.LanguageTranslation.Translatable) + int x = this.lbTitle.Width - 70; + using (Font font = new Font("宋体", 11, FontStyle.Bold)) + 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); - // 测量文本实际尺寸(含字体和格式影响) - 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; + graphicsText.DrawString(this._waterMark.Print, font, brush, new PointF(x, 21), format, + Business.Impl.LanguageTranslation.Translatable ? 0f : -15f); - 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(); if (!string.IsNullOrEmpty(this._waterMark.Collect)) { - - - if (Business.Impl.LanguageTranslation.Translatable) + int x = this.lbTitle.Width - 170; + using (Font font = new Font("宋体", 12, FontStyle.Bold)) + 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); - // 测量文本实际尺寸(含字体和格式影响) - 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; + graphicsText.DrawString(this._waterMark.Collect, font, brush, new PointF(x, 21), format, + Business.Impl.LanguageTranslation.Translatable ? 0f : -15f); - 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(); } } /// diff --git a/插件库/Lskj.PubModuleDetail/FrmMain.cs b/插件库/Lskj.PubModuleDetail/FrmMain.cs index 810ec37..54d173b 100644 --- a/插件库/Lskj.PubModuleDetail/FrmMain.cs +++ b/插件库/Lskj.PubModuleDetail/FrmMain.cs @@ -154,7 +154,11 @@ namespace Lskj.PubModuleDetail { this.mControl.CloseModule(); string guid = this.Tag + ""; - if (string.IsNullOrWhiteSpace(guid)) return; + if (string.IsNullOrWhiteSpace(guid)) + { + this.mControl.ReleaseResourcesForDispose(); + return; + } DllModule m = Manager.ModuleForms[guid]; m.IsClose = true; 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(); + } } ///