如果你读过 Raymond Chen 的帖子,你可能会像我一样觉得它很烦人。您只是“可能做错了什么”,因为您正在做 Windows 无法完成的事情。
在我的应用程序中,当用户第一次访问选项卡页时,我会创建并布置该页面上的所有控件。这需要相当长的时间 - 一个页面上很容易就有 50 个控件。因此,如果可能的话,我不会在填充选项卡页后丢弃该选项卡页上的控件,并将关闭选项卡页集留给用户。
事实上,有些用户永远不想关闭any标签页集。我为什么要强迫他们?通过我的 UI,他们可以非常快速地导航到他们负责管理的 300 多组事务中的任何一组。他们的机器足够快,并且有足够的内存,使这一切都非常敏感。唯一的问题是 Windows 不支持它。
为什么我使用控件而不是其他 UI 技术?因为他们work。我需要支持焦点事件、选项卡顺序、验证事件、动态布局和数据绑定 - 用户实际上是在内存数据集中管理数十个表中的数千条记录。比如说,使用无窗口控件实现一些东西,我需要做的开发工作量是天文数字。
我只是“做错了”,因为 Windows 对它可以支持的窗口句柄的数量有严格的限制。这个硬性限制是基于一系列十年前关于如何构建计算机用户界面的假设。 “做错事”的不是我。
无论如何,我的解决方案分为两部分。
首先,一个类可以告诉您进程正在使用多少个窗口句柄:
using System;
using System.Runtime.InteropServices;
namespace StreamWrite.Proceedings.Client
{
public class HWndCounter
{
[DllImport("kernel32.dll")]
private static extern IntPtr GetCurrentProcess();
[DllImport("user32.dll")]
private static extern uint GetGuiResources(IntPtr hProcess, uint uiFlags);
private enum ResourceType
{
Gdi = 0,
User = 1
}
public static int GetWindowHandlesForCurrentProcess(IntPtr hWnd)
{
IntPtr processHandle = GetCurrentProcess();
uint gdiObjects = GetGuiResources(processHandle, (uint)ResourceType.Gdi);
uint userObjects = GetGuiResources(processHandle, (uint)ResourceType.User);
return Convert.ToInt32(gdiObjects + userObjects);
}
}
}
其次,我维护最近最少使用的选项卡页对象的缓存。 .NET框架不提供通用的LRU缓存类,所以我构建了一个,你可以得到here http://csharp-lru-cache.googlecode.com如果你需要的话。每次用户访问标签页时,我都会将其添加到 LRU 缓存中。然后我检查一下窗口句柄是否不足。如果是的话,我会丢弃最近最少使用的选项卡页上的控件,并继续这样做,直到再次拥有足够的窗口句柄。