TitleTips 编辑、列表框和组合框控件的支持






4.83/5 (19投票s)
一篇关于支持编辑、列表框和组合框控件 TitleTips 的混合类的文章
更新
尽管这是一个小巧但实用的实用程序类,仅用几行代码即可支持编辑、列表框和组合框控件的 TitleTip,但由于 ATL/WTL 的依赖性,其使用受到了限制。最重要的是,它在非 WIN32/x86 系统(例如 x64)上会出现问题,因为我在原始代码中使用的自定义 ATL 风格的 thunk(MessageHook.hpp)不兼容 NX/DEP。因此,它可能会在 64 位 Windows 系统中引发异常。
x64 支持和 NX/DEP 兼容性
我完全重写了实例子类化代码,以弃用旧的自定义 thunk,现在它从 ATL 中窃取 thunk 定义。因此,如果针对 ATL8 或更高版本进行编译,它将兼容 NX/DEP,并能支持 ATL 支持的各种平台(x86/x64/IA64/alpha...)。虽然它也可以针对 ATL3 或 ATL7 进行编译,但这些二进制文件将遭受与如今的旧版 ATL 二进制文件相同的所有问题,并且不兼容 NX/DEP。
窃取 ATL thunk 定义并不意味着在二进制级别上依赖 ATL,而仅在源代码级别上。两个 ATL 头文件,atlbase.h 和 atlwin.h,将被包含在项目中。如果在包含 MessageHook.hpp 时尚未完成,则会包含它们。
C API 风格 DLL 版本移植
通过将类封装到 DLL 中并导出 C 风格的 API 函数,它有潜力被多种编程语言使用,而不仅仅是 C++。它是通过句柄/体(handle/body)模式实现的。此处附带的 DLL 二进制文件是针对 ATL8 编译的,因此兼容 NX/DEP。或者,您可以使用附加演示项目文件中的名为“TipTMan”的 DLL 项目来为您的目标平台创建自己的 DLL。请注意,在 C++ 中派生自 CTitleTipHandlerT<T>
的方法仍然有效且运行良好。
C API 示例
使用 DLL 函数非常简单,如下所示
#include "TTipMan.h"
# pragma message("Auto link to TTipMan.lib")
# pragma comment(lib, "TTipMan.lib")
BOOL CTestDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Create TitleTip handler and hook into dialog window
HANDLE hTTMan = CreateTitleTipHandler( GetSafeHwnd() );
// Add TitleTip support for edit control (m_Edit)
// Optionally set yellow as foreground and black as background color of the tip
AddTitleTipEdit( hTTMan, m_Edit.GetSafeHwnd(), RGB(255, 255, 0), RGB(0, 0, 0) );
// ...
}
已添加 CreateTitleTipHandler()
和 DestroyTitleTipHandler()
来实现 DLL 版本中的句柄/体模式,并且所有现有函数都已修改为接受 CreateTitleTipHandler()
返回的附加 HANDLE
。
从 DLL 导出的 C 函数
#define COLOR_DEFAULT (DWORD)0xffffffff
/**
* Create a TitleTip manager and subclass the specified parent window which
* will receive TTN_xxx notification from target controls to support TitleTips
*
* \param hwnd [in] HWND Specify the handle to the parent window of controls
*
* \return HANDLE Return the handle for the TitleTip manager to manage TitleTips
**/
HANDLE WINAPI CreateTitleTipHandler(HWND hwnd);
/**
* Destroy the TitleTip manager and perform all related clean-ups
* including unsubclassing.
* Not like ordinary handles, it is not compulsory to call this function as it will
* always
* be destroyed and cleaned up when the subclassed parent window receives the final
* message(WM_NCDESTROY)
*
* \param handle [in] HANDLE Specify the handle to the TitleTip manager to be
* destroyed
*
* \return Return void
**/
void WINAPI DestroyTitleTipHandler(HANDLE handle);
BOOL WINAPI AddTitleTipEdit(HANDLE handle, HWND hwndEdit,
COLORREF crTipTextColor = COLOR_DEFAULT,
COLORREF crTipBkColor = COLOR_DEFAULT);
void WINAPI DelTitleTipEdit(HANDLE handle, HWND hwndEdit);
BOOL WINAPI AddTitleTipListBox(HANDLE handle, HWND hwndListBox,
COLORREF crTipTextColor = COLOR_DEFAULT,
COLORREF crTipBkColor = COLOR_DEFAULT,
COLORREF crTipTextColorSelection = COLOR_DEFAULT,
COLORREF crTipBkColorSelection = COLOR_DEFAULT);
void WINAPI DelTitleTipListBox(HANDLE handle, HWND hwndListBox);
BOOL WINAPI AddTitleTipComboBox(HANDLE handle, HWND hwndComboBox,
COLORREF crTipTextColor = COLOR_DEFAULT,
COLORREF crTipBkColor = COLOR_DEFAULT,
COLORREF crTipTextColorSelection = COLOR_DEFAULT,
COLORREF crTipBkColorSelection = COLOR_DEFAULT);
void WINAPI DelTitleTipComboBox(HANDLE handle, HWND hwndComboBox);
HWND WINAPI GetHwndToolTip(HANDLE handle);
LPTSTR WINAPI GetToolTipBuf(HANDLE handle);
UINT WINAPI GetToolTipBufLen(HANDLE handle);
void WINAPI ResetToolTipBuf(HANDLE handle);
LPTSTR WINAPI ReallocToolTipBuf(HANDLE handle, UINT nLen);
-- 更新结束 --

引言
尽管列表视图控件和树视图控件具有易于使用的内置 TitleTips(工具提示)支持,但其他最常用的三个控件(编辑、列表框和组合框控件)却没有任何类似的内置支持。您需要使用工具提示控件来创建自己的支持。“TitleTips 是用于拉长控件中被截断项的提示,以便您可以看到整个项。例如,Visual Studio 在其项目工作区中有 TitleTips。如果项目工作区中的类名太长而无法完全显示,则会出现一个 TitleTip,显示完整的文本。这样就可以在不滚动滚动条也不需要拉宽控件的情况下使用项目工作区。”有关工具提示和 TitleTips 的基本信息和详细信息,请参阅 MSJ 中的本文档。
当我在对话框中编写一个支持编辑控件和列表框控件 TitleTips 的小程序时,我突然想到,或许可以创建一个通用的混合类来支持这些控件的 TitleTips。实际上,这并不像我最初想的那么简单,因为 MSDN 或其他地方并没有很好地记录 ToolTip 控件的*精细*调整行为,更糟糕的是,它的行为会根据通用控件的版本略有不同。唉~ .0
背景
在实现这个混合类时,我大量使用了我自己编写的实用类 CMessageHook
。CMessageHook
是我用来子类化窗口消息过程的一个小巧但实用的类。它使用与 ATL 相同的汇编 thunk 技术,但纯粹是 WIN32 的。因此,它可以在任何 WIN32 平台(仅限 Intel CPU)上使用。我自己编写这个类是为了了解 ATL 和 thunk 技术的来龙去脉,并在我的几个项目中连续使用它。我宁愿说我从其他人类似的汇编 thunk 实现中复制粘贴了好的特性到我的类里 :)
现在回到 TitleTip 问题:我本可以实现一个跟踪工具提示控件(TTF_TRACK
标志)。然后我需要实现自己的自定义计时器和 WM_MOUSEMOVE
消息处理程序,在将提示定位到正确位置后激活/停用提示(TTM_TRACKACTIVATE
)等等。在 CodeProject 上,Hans Dietrich 有一个非常好的例子。然而,我选择坚持使用传统的 TTN_GETDISPINFO
(在旧时是 TTN_NEEDTEXT
)和 TTN_SHOW
通知处理方法,仅仅是因为我习惯了这种方法。因此,我可以利用工具提示控件的内置功能,如计时器(TTM_SETDELAYTIME
)、自动子类化(TTF_SUBCLASS
)等。这就是为什么我认为它和编写常规工具提示一样简单。不幸的是,我错了,我将在下面解释我在实现过程中遇到的问题。
实现说明
1. 使用 TTF_TRANSPARENT
标志
作为 TitleTips 的性质,当提示显示时,它将完全覆盖工具窗口。换句话说,在提示出现的那一刻,工具窗口将停止接收鼠标消息。根据这个规则,它会向提示发送一个 TTM_POP
消息。反过来,提示会消失,这使得工具窗口可以重新接收鼠标消息。这会导致提示一次又一次地显示。
我们将看到提示疯狂闪烁。TTF_TRANSPARENT
标志用于解决这个问题,当在添加工具信息(TOOLINFO.uFlags
)时指定它,它会使提示将某些鼠标消息重定向到工具窗口,以便工具窗口可以看到鼠标消息。即使工具窗口被提示窗口完全覆盖,这也能使提示在屏幕上保持显示而不会闪烁。
现在您可以在 Win2K 或 Win98 机器上尝试一下。提示会很好地显示并保持不闪烁。您可以点击提示上的鼠标左键,它就会消失。因此,您可以执行常规任务(选择一段文本、选择一个列表项等等)。
好的,现在您可以尝试在 WinXP 上使用通用控件版本 6。故事是一样的,直到您尝试点击提示。不幸的是,即使您点击、双击或三击提示,它仍会显示在屏幕上。在 WinXP 上不消失非常恼人,所以我决定改变这种行为,使其在所有平台上保持一致。
class CToolTipCtrlHook : public CMessageHook
{
public:
// c'tor
CToolTipCtrlHook(BOOL bAutoDelete) : CMessageHook(bAutoDelete) { }
protected:
// implementations
protected:
virtual BOOL ProcessWindowMessage(UINT uMsg, WPARAM wParam,
LPARAM lParam, LRESULT &lResult)
{
BOOL bHandled = FALSE;
switch(uMsg)
{
case WM_LBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_XBUTTONDOWN:
{
/////////////////////////////////////////////////////
// added since TTF_TRANSPARENT flag doesn't work well
// especially in WinXP Common Control 6
/////////////////////////////////////////////////////
TOOLINFO ti;
ti.cbSize = sizeof(TOOLINFO);
// Get current tool info (TOOLINFO.uId is tool window's handle)
// GetHwnd() return the subclassed ToolTip control's handle
::SendMessage(GetHwnd(), TTM_GETCURRENTTOOL, 0, (LPARAM)&ti);
// pop (hide) current tip
::SendMessage(GetHwnd(), TTM_POP, 0, 0);
// convert the current cursor position from tip window
// coordinate to tool window coordinate
POINT ptTarget = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
::ClientToScreen(GetHwnd(), &ptTarget);
::ScreenToClient((HWND)ti.uId, &ptTarget);
// re-direct the mouse message to tool window
lResult = ::SendMessage((HWND)ti.uId, uMsg, wParam,
MAKELPARAM(ptTarget.x, ptTarget.y));
bHandled = TRUE;
}
break;
}
return bHandled;
}
};
2. 处理 TTN_GETDISPINFO
通知
TitleTips 与常规提示不同之处在于,它们仅在工具窗口上显示的文本长度大于其显示区域(主要是控件窗口的客户矩形)导致部分文本被截断时才出现。如果工具窗口可以显示完整的文本而没有任何截断,则不应显示 TitleTip。在收到 TTN_SHOW
的那一刻,已经太晚了,无法确定“显示”还是“不显示”提示,提示至少会显示在屏幕上的某个地方一次,即使您尝试在 TTN_SHOW
通知处理程序中立即将其弹出(它会闪烁)。
在 TTN_GETDISPINFO
通知处理程序中,需要将 NMTTDISPINFO.lpszText
设置为指向用于在提示中显示文本的私有缓冲区的指针。NMTTDISPINFO.szText
的长度限制为小于 80 char
。因此,它不适合我们的目的。所以,如果我们不想在屏幕上显示提示,我们可以在 TTN_GETDISPINFO
通知处理程序中将 NMTTDISPINFO.lpszText
设置为 NULL
并返回 -- 在文本被截断的情况下。
这听起来很简单,但这种简单的方法有一个隐藏的陷阱。假设我们的对话框中只有一个编辑控件,并且我们只为我们拥有的这个编辑控件安装了一个提示。如果编辑控件的文本被截断,当我们把鼠标光标移到编辑控件上或移开时,我们可以看到提示按预期弹出。然而,在文本未被截断的情况下,我们的 TTN_GETDISPINOF
通知处理程序会将 NMTTDISPINFO.lpszText
设置为 NULL
并返回。结果,我们将看不到屏幕上的任何提示。
让我们在编辑控件中输入更多字符,以便文本再次被截断。将光标移到编辑控件上,您会惊讶地发现,即使文本被截断(基本上不再发出 TTN_GETDISPINFO
通知),也不再弹出任何提示了。如果我们有多个控件并安装了多个提示,那么当满足某个条件时,我们可以将鼠标光标从一个工具窗口移到另一个工具窗口。这将导致再次发出 TTN_GETDISPINFO
通知。但是,您仍然会在某些情况下看到提示没有按预期弹出。
我花了相当多的时间来寻找这个问题的解决方案,但讽刺的是,当我最终找到它时,发现它非常简单。如果您在 TTN_GETDISPINFO
通知处理程序中将 NMTTDISPINFO.lpszText
设置为 NULL
,则需要向工具提示控件窗口*发布*一个 TTM_POP
消息来显式弹出提示。我强调了*发布*这个词,因为如果在 TTN_GETDISPINFO
通知处理程序中使用 ::SendMessage()
而不是 ::PostMessage()
发送消息,在 WinXP 通用控件版本 6 中将无法正常工作。
LRESULT OnToolTipNotifyGetDispInfo(int nID,
LPNMHDR lpnmhdr, BOOL& bHandled)
{
bHandled = TRUE;
LPNMTTDISPINFO lpnmtdi= reinterpret_cast<LPNMTTDISPINFO>(lpnmhdr);
HWND hwndTool = reinterpret_cast<HWND>(lpnmtdi->hdr.idFrom);
// to determine if the text is being clipped
...
if(m_bTextClipped)
{
// to show TitleTip
// to copy the text to display into my private buffer
...
lpnmhdr.lpszText = pszMyPrivateBufer;
}
else
{
// not to show TitleTip
lpnmhdr.lpszText = NULL;
::PostMessage(m_hwndToolTip, TTM_POP, 0, 0L);
}
return 0;
}
3. 共享工具提示控件和私有存储以存放提示文本
我们不仅可以拥有任意数量的工具提示控件窗口,还可以为相应的各个工具提示控件拥有任意数量的私有存储空间。但是请再想想。在任何给定时刻,我们真的会使用多个工具提示控件吗?不。只有鼠标光标所在的工具窗口的提示才会被激活。那么,为什么我们不只使用一个工具提示控件窗口和私有存储来支持所有 TitleTip,甚至给您机会在任何需要的地方共享它们呢?让我们节省一些资源!无论存在多少个工具窗口,在我编写的混合类中,全局只会创建一个工具提示控件窗口和用于存储提示文本的私有存储。我使用了通用的单例技术来实现这一点。
使用 CTitleTipHandler<T>
模板类的以下公共成员函数来共享同一对话框中的工具提示控件窗口和私有存储,该对话框派生自 CTitleTipHandler<T>
模板类(混合类)。
// return ToolTip control
CToolTipCtrl &GetToolTipCtrl();
// return the pointer to the buffer (private
// storage) for tip text
LPTSTR GetToolTipBuf();
// return the size of buffer in TCHARs
int GetToolTipBufLen() const;
// zero'ing the buffer
void ResetToolTipBuf();
// delete old buffer and reallocate new buffer of size 'nLen' in heap memory
LPTSTR ReallocToolTipBuf(int nLen);
或者,使用以下对应的 CSSTip
单例类的公共类成员函数来在任何地方共享工具提示控件窗口和私有存储。
using codeproject::CSSTip;
// return ToolTip control
static CToolTipCtrl &CSSTip::ToolTipCtrl();
// return the pointer to the buffer (private storage) for tip text
static LPTSTR CSSTip::GetToolTipBuf();
// return the size of buffer in TCHARs
static int CSSTip::GetToolTipBufLen() const;
// zero'ing the buffer
static void CSSTip::ResetToolTipBuf();
// delete old buffer and reallocate new buffer of
// size 'nLen' in heap memory
static LPTSTR CSSTip::ReallocToolTipBuf(int nLen);
4. 嵌套实例子类化问题
正如我之前提到的,我大量使用了我自己编写的实用类 CMessageHook
来子类化窗口过程。任何将从我的混合类中获得 TitleTip 支持的控件,在将其工具信息添加到工具提示控件时都会被子类化。如果您使用的是附加到 ATL::CWindowImpl<T>
派生类的控件窗口,该控件也会被 ATL 内部实现所子类化。当涉及两个或更多实例子类化时,它们的子类和取消子类的顺序变得非常重要。子类必须按照与它们执行的顺序相反的顺序移除。否则,您就会搞砸。
通常,一个实例子类化类的实现者会将其纳入其实现考虑范围,并在其实现中加入最小的安全措施。例如,当子类/取消子类顺序混乱时,CMessageHook
不会删除自身,而是继续寄生在它子类化的窗口上(它会在稍后收到 WM_NCDESTROY
消息时自动删除自身)。然而,混乱子类和取消子类的顺序无论如何都是一个非常糟糕的主意,所以请记住并尽量以某种方式避免它。
class CListBoxImpl : public CWindowImpl<CListBoxImpl, CListBox>
{
public:
BEGIN_MSG_MAP(CListBoxImpl)
END_MSG_MAP()
// NOP
};
using codeproject::CTitleTipHandler;
class CMainDlg : public CDialogImpl<CMainDlg>,
public CTitleTipHandler<CMainDlg>
{
public:
enum { IDD = IDD_MAINDLG };
BEGIN_MSG_MAP(CMainDlg)
CHAIN_MSG_MAP(CTitleTipHandler<CMainDlg>)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
...
END_MSG_MAP()
protected:
CListBoxImpl c_lbList;
public:
LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/,
LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
...
HWND hwndList = NULL;
{ // instance subclassing level 1 BEGIN
c_lbList.SubclassWindow(GetDlgItem(IDC_LIST));
hwndList = c_lbList.UnsubclassWindow();
} // instance subclassing level 1 END
{ // instance subclassing level 1 BEGIN
c_lbList.SubclassWindow(hwndList);
c_lbList.AddString(_T(
"0. Long String - TitleTip for EDIT, LISTBOX & COMBOBOX controls"));
c_lbList.AddString(_T("1. Short String - Hello"));
c_lbList.AddString(_T("2. Short String - Hello"));
{ // instance subclassing level 2 BEGIN
AddTitleTipListBox(c_lbList);
DelTitleTipListBox(c_lbList);
} // instance subclassing level 2 END
hwndList = c_lbList.UnsubclassWindow();
} // instance subclassing level 1 END
{ // instance subclassing level 1 BEGIN
AddTitleTipListBox(hwndList);
{ // instance subclassing level 2 BEGIN
c_lbList.SubclassWindow(hwndList);
c_lbList.AddString(_T(
"3. Long String - TitleTip for EDIT, LISTBOX & COMBOBOX controls"));
c_lbList.AddString(_T(
"4. Long String - TitleTip for EDIT, LISTBOX & COMBOBOX controls"));
}
}
...
return TRUE;
}
...
}
Using the Code
使用起来非常简单。首先要做的是将 CTitleTipHandler<T>
添加为您的对话框类的基类(混合),然后将消息映射链到 CTitleTipHandler<T>
。在您的对话框初始化代码(OnInitDialog()
)中使用 AddTitleTipXXX(HWND hwndTool)
函数或 DelTitleTipXXX(HWND hwndTool)
函数分别添加或移除特定控件(hwndTool
)的 TitleTips 支持。
using codeproject::CTitleTipHandler;
class CMainDlg : public CDialogImpl<CMainDlg>,
public CTitleTipHandler<CMainDlg>
{
public:
enum { IDD = IDD_MAINDLG };
BEGIN_MSG_MAP(CMainDlg)
// chain to message map in CTitleTipHandler<CMainDlg>
CHAIN_MSG_MAP(CTitleTipHandler<CMainDlg>)
MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
...
END_MSG_MAP()
protected:
CEdit c_edEdit;
CListBox c_lbList;
CComboBox c_cbCombo;
public:
LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/,
LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
...
c_edEdit.Attach(GetDlgItem(IDC_EDIT1));
c_lbListBox.Attach(GetDlgItem(IDC_LIST1));
c_cbComboBox.Attach(GetDlgItem(IDC_COMBO1));
// TitleTip support for IDC_EDIT1 Edit control
// Tip color (blue text and white background)
AddTitleTipEdit(c_edEdit.m_hWnd, RGB(0, 0, 255), RGB(255, 255, 255));
// TitleTip support for IDC_LIST1 ListBox control
// Tip color (black text / white background),
// selected item (red text / black background)
AddTitleTipListBox(c_lbList.m_hWnd, RGB(0, 0, 0),
RGB(255, 255, 255), RGB(255, 0, 0), RGB(0, 0, 0));
// TitleTip support for IDC_COMBO1 ComboBox control
// Tip color (COLOR_WINDOWTEXT / COLOR_WINDOW),
// selected item (COLOR_HIGHLIGHTTEXT / COLOR_HIGHLIGHT)
AddTitleTipComboBox(c_cbCombo.m_hWnd);
...
return TRUE;
}
...
}
公共函数
BOOL AddTitleTipEdit(HWND hwndEdit, COLORREF crTipTextColor = COLORREF_DEFAULT, COLORREF crTipBkColor = COLORREF_DEFAULT);
- 为指定的编辑控件(
hwndEdit
)添加 TitleTip 支持。 - 默认颜色 - 提示文本:
::GetSysColor(COLOR_WINDOWTEXT)
,提示背景:::GetSysColor(COLOR_WINDOW)
。 - 成功则返回
TRUE
。
void DelTitleTipEdit(HWND hwndEdit);
- 移除指定编辑控件(
hwndEdit
)的 TitleTip 支持。
BOOL AddTitleTipListBox(HWND hwndListBox, COLORREF crTipTextColor = COLORREF_DEFAULT, COLORREF crTipBkColor = COLORREF_DEFAULT, COLORREF crTipTextColorSelection = COLOR_DEFAULT, COLORREF crTipBkColorSelection = COLOR_DEFAULT);
- 为指定的列表框控件(
hwndListBox
)添加 TitleTip 支持。 - 默认颜色 - 提示文本:
::GetSysColor(COLOR_WINDOWTEXT)
,提示背景:::GetSysColor(COLOR_WINDOW)
,选中的提示文本:::GetSysColor(COLOR_HIGHLIGHTTEXT)
,选中的提示背景:::GetSysColor(COLOR_HIGHLIGHT)
。 - 成功则返回
TRUE
。
void DelTitleTipListBox(HWND hwndListBox);
- 移除指定列表框控件(
hwndListBox
)的 TitleTip 支持。
BOOL AddTitleTipComboBox(HWND hwndComboBox, COLORREF crTipTextColor = COLORREF_DEFAULT, COLORREF crTipBkColor = COLORREF_DEFAULT, COLORREF crTipTextColorSelection = COLOR_DEFAULT, COLORREF crTipBkColorSelection = COLOR_DEFAULT);
- 为指定的组合框控件(
hwndComboBox
)添加 TitleTip 支持。 - 默认颜色 - 提示文本:
::GetSysColor(COLOR_WINDOWTEXT)
,提示背景:::GetSysColor(COLOR_WINDOW)
,选中的提示文本:::GetSysColor(COLOR_HIGHLIGHTTEXT)
,选中的提示背景:::GetSysColor(COLOR_HIGHLIGHT)
。 - 成功则返回
TRUE
。
void DelTitleTipComboBox(HWND hwndComboBox);
- 移除指定组合框控件(
hwndComboBox
)的 TitleTip 支持。
CToolTipCtrl &GetToolTipCtrl();
- 返回工具提示控件。
LPTSTR GetToolTipBuf();
- 返回指向提示文本(私有存储)缓冲区的指针。
int GetToolTipBufLen() const
- 返回缓冲区的大小(以
TCHAR
为单位)。
void ResetToolTipBuf();
- 将缓冲区清零。
LPTSTR ReallocToolTipBuf(int nLen);
- 删除旧缓冲区并重新分配一个大小为
nLen
的新缓冲区到堆内存中。
要求
已安装 Internet Explorer 5 或更高版本 - 可使用 TTM_ADJUSTRECT
消息。
历史
版本 1.10 - 2008 年 2 月 14 日
- 修改了构造函数,以接受用于 DLL 版本移植的
AutoDelete
标志。 - Bug 修复:
EM_GETLINE
- 复制的行不包含空字符。 - Bug 修复:
TOOLINFO
结构的大小可能因通用控件的版本而异。对于 2000 年和未安装通用控件 6.0 的 XP,正确大小为 44 字节;对于安装了通用控件 6.0 的 XP,为 48 字节。
版本 1.01 - 2004 年 10 月 26 日
- 已优化以提高响应速度(仅查找映射一次)。
- 修复了所有发现的资源泄漏。
- 修复了一些(与编辑控件相关的) Bug。
版本 1.0 - 2004 年 10 月 8 日
- 首次在 CodeProject 上发布。