fix(winforms): release module grid resources
This commit is contained in:
@@ -31,6 +31,9 @@ using Lskj.Business;
|
||||
|
||||
public static class LabPicUrlPreview
|
||||
{
|
||||
private static readonly object AttachedViewsSync = new object();
|
||||
private static readonly List<WeakReference> AttachedViews = new List<WeakReference>();
|
||||
|
||||
// ====== 对外:初始化图片列(创建 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<string, CacheEntry>(StringComparer.OrdinalIgnoreCase);
|
||||
private static readonly Dictionary<string, Task<Image>> _tasks =
|
||||
new Dictionary<string, Task<Image>>(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<Image> task = Task.Factory.StartNew<Image>(delegate
|
||||
{
|
||||
_throttle.WaitOne();
|
||||
@@ -394,15 +454,21 @@ public static class LabPicUrlPreview
|
||||
}, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).ContinueWith<Image>(delegate (Task<Image> 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<string, CacheEntry>(StringComparer.OrdinalIgnoreCase);
|
||||
private static readonly Dictionary<string, Task<Image>> _tasks =
|
||||
new Dictionary<string, Task<Image>>(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<Image> task = Task.Factory.StartNew<Image>(delegate
|
||||
{
|
||||
_throttle.WaitOne();
|
||||
@@ -509,15 +598,21 @@ public static class LabPicUrlPreview
|
||||
}, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).ContinueWith<Image>(delegate (Task<Image> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 共享底层工具 =====
|
||||
|
||||
Reference in New Issue
Block a user