VC中锁定ListView的栏目头宽度

2016-02-19 21:42 16 1 收藏

下面图老师小编要跟大家分享VC中锁定ListView的栏目头宽度,简单的过程中其实暗藏玄机,还是要细心学习,喜欢还请记得收藏哦!

【 tulaoshi.com - 编程语言 】

    世界之大,真是无其不有。Windows 应用程序的GUI标准明确规定了 ListView 栏目头(Column Header)的宽度必须是可调整的,这本来是专门为用户考虑而设计的控制特性,可是偏偏就有用户拒绝这样的特性。作为技术人员,用户的需求是很难拒绝的。 尽管这明显是一种“非典型性需求”。本文将通过一个实例来示范如何实现 ListView Column Header 宽度的锁定。

  ListView 及其 Column Header 实际上都是 Windows 通用控件(Comctl32.dll) 的一部分。所以查一查 MSDN 中与“Header Control”相关的控件资料不难发现,栏目头的锁定与否与几个 Windows 的通知消息密切相关,这几个消息分别是 HDN_TRACK、HDN_BEGINTRACK 和 HDN_ENDTRACKA。其中 HDN_BEGINTRACK 是本文要特别关照的一个。当用户在栏目头上拖拽鼠标时,如果位置正好在改变宽度的分割条上,则栏目头控件会向其父窗口发送一个 HDN_BEGINTRACK 通知消息。为了实现栏目头宽度的锁定,就必须搞掂这个通知消息。不能将它传递到父窗口,但是,这个消息与 Windows 中形形色色的其它通知消息一样,有两个版本:一个版本是 HDN_BEGINTRACKW,专门用于宽字符和 Unicode 字符集;另一个版本是 HDN_BEGINTRACKA,专门用于 ANSI 字符集。这两个版本的使用方法可以从公共控件的头文件 commctrl.h 中获取:

// From commctrl.h
#ifdef UNICODE
#define HDN_BEGINTRACK HDN_BEGINTRACKW
#else
#define HDN_BEGINTRACK HDN_BEGINTRACKA
#endif  

  所以在实现对消息的 HDN_BEGINTRACK 处理时,实际上是根据 UNICODE 的取值实现对 HDN_BEGINTRACKA 或 HDN_BEGINTRACKW 的处理。那么 Header Control 到底是发送的哪一个消息呢?在这里必须明白:Header Control 是 Windows 通用控件的一部分,它的实现都在 comctl32.dll 动态链接库中。由于这个 DLL 已经被编译成可执行代码,因此在工程中修改 UNICODE 的设置将无济于事。如何知道栏目头控件发送哪一个版本的通知消息呢?是 A 版本还是 W 版本?

  为了找到答案,我们必须求助一个经常被遗忘的消息 WM_NOTIFYFORMAT。一般控件第一次被创建时,都要向父窗口一个消息询问父窗口需要哪个版本的通知消息。然后父窗口返回 NFR_ANSI 或 NFR_UNICODE。如果父窗口不处理 WM_NOTIFYFORMAT,那么这个消息将根据父窗口或对话框本身的首选项被传递到 Windows 的 DefWindowProc 消息处理例程进行默认处理。默认为 UNICODE。因此,要知道通知消息的版本,必须处理 ListCtrl 的 WM_NOTIFYFORMAT。为了确认父窗口的返回值,你可以做一个试验便明白了。

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com/bianchengyuyan/)

  如果你不想处理 WM_NOTIFYFORMAT 消息,那么完全可以通过双双实现 HDN_BEGINTRACKA 和 HDN_BEGINTRACKW 通知消息的处理来简化问题的解决方案,同时这种方法也更可靠和通用。此时代码将同时支持 ANSI 和 Unicode。本文附带的例子程序示范了这种方法的实现。如图一所示:


  图一 锁定栏目头宽度

  实现代码很简单,Header 控件发送 HDN_XXX 到父窗口(ListCtrl),在 MFC 中可以利用消息反射来处理 Header 控件的通知消息。因为“可锁定栏目头”特性本身更趋向于 Header 控件的属性,而不是 ListCtrl 的属性。如果你不用 MFC ,那么就得处理 ListCtrl 中的通知消息。例子程序使用了消息反射机制,在 Header 控件的消息映射使用 ON_NOTIFY_REFLECT,也就是该写虚拟成员函数 OnChildNotify:

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com/bianchengyuyan/)

BOOL CLockableHeader::OnChildNotify(UINT msg, WPARAM wp, LPARAM lp, LRESULT* pRes)
{
   NMHDR& nmh = *(NMHDR*)lp;
   if (nmh.code==HDN_BEGINTRACKW || nmg.code==HDN_BEGINTRACKA)
   return *pRes=TRUE;
   ......
}
  因为 OnChildNotify 是虚函数,所以没有必要具备消息映射入口。只要实现此函数即可。在任何应用中,Header 发送的消息非此即彼,不会两者都发送。不管怎样,所发送的通知消息在到达父窗口之前都会被吃掉。也就是说,消息处理总是返回 TRUE,是否锁定栏目头的宽度通过一个标志来控制:应用程序通过 Lock 来修改标志的值。

  如果锁定了头宽度,那么同时也必须禁用改变宽度的光标,这样用户界面才会有一致性,要实现这一点也很简单:

BOOL CLockableHeader::OnSetCursor( CWnd* pWnd, UINT nHit, UINT msg)
{
   return m_bLocked ? TRUE : CHeaderCtrl::OnSetCursor(pWnd, nHit, msg);
}   
  如果栏目头被锁定,则 OnSetCursor 返回 TRUE,此时光标不会被重新设置,否则由 Header 控件的进行默认处理。锁定宽度后,当鼠标移到栏目头上时,Windows 显示标准的箭头光标,而不是带左右箭头光标。http://bianceng.cn(编程入门)

  从 CHeaderCtrl 派生类出来的类的使用方法与处理对话框控制一样,通过在父窗口的 OnCreate 的处理例程中进行子类化。实现细节请参考例子源代码:

// CMyView is derived from CListView
int CMyView::OnCreate(LPCREATESTRUCT lpcs)
{
  VERIFY(CListView::OnCreate(lpcs)==0);
  return m_header.SubclassDlgItem(0,this) ? 0 : -1;
}
  由于 Header 控制的资源 ID = 0,所以上面的代码是行得通的。为了有一个友好的用户界面,例子程序创建了一个命令菜单和界面更新处理例程。如图一所示。

来源:https://www.tulaoshi.com/n/20160219/1626888.html

延伸阅读
Insert/New class/ class Type/选择Generic class class information/Name中输入类的名字 1 如果继承某个类的话,在Base class/Derived class 中选择基类 2如果已经存在目录下,但没有添加到工程可以选择project/Add To Project/Files添加文件即可 3在左侧的类名单击右键可以添加成员函数(Add Member Function)和成员变量(Add Member Vari...
摘要:指针,在VC++中是很常见的,这里我们并不打算去详细讲解在C++中那样的指针用法(我们会有另外的文章去详细讨论),这里主要讲一下VC++中常见的对指针获取的方法,包括:工具条、状态条、控件和窗口的指针。      获取工具条的指针      在缺省状态下,有一个默认的工具条AFX_IDW_TOOLBAR,我...
大多数程序设计的爱好者选择并使用Delphi来编写软件,都是被其中丰富而功能强大的VCL控件所吸引。Delphi自带的数据感知(Data-Aware)控件,更是成为开发MIS软件的程序员之首选。在那么多数据感知控件中,TDBGrid由于其使用方便、显示信息量大成为最引人注目的一员,大量的国内外软件中都出现了它的身影。或许是由于使用的人多了,对于它的期...
消息是指什么? 消息系统对于一个win32程序来说十分重要,它是一个程序运行的动力源泉。一个消息,是系统定义的一个32位的值,他唯一的定义了一个事件,向Windows发出一个通知,告诉应用程序某个事情发生了。例如,单击鼠标、改变窗口尺寸、按下键盘上的一个键都会使Windows发送一个消息给应用程序。 消息本身是作为一个记录传递...
#include #include #include #pragma comment(lib,"vfw32.lib") HWND ghWndCap ; //捕获窗的句柄 CAPDRIVERCAPS gCapDriverCaps ; //视频驱动器的能力 CAPSTATUS gCapStatus ; //捕获窗的状态 char szCaptureFile[] = "MYCAP.AVI"; char gachBuffer[20]; LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM); LRESULT CALLBACK Status...

经验教程

898

收藏

46
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部