[转贴]Skin技术实现框架(三)

上次说了hook和窗口类的原理,有了hook,我们可以截取所有消息,有了窗口类,我们可以识别窗口类型,不同类型的窗口给予不同处理。这样,我们要在钩子函数里面识别不同的窗口和不同的消息,有大量的分派工作,更要命的是,光区分窗口类还不够,同类型的不同窗口经常需要不同的处理,例如两个button窗口,大小不同,文字不同,是否有鼠标按下不同...... 这些状态有些是可以从button窗口读到的,例如大小和文字,而有些则读不到,比如是否有鼠标按下,对这些读不到的状态,我们必须自己记录,例如在收到WM_LBUTTONDOWN消息时记下按钮被按下了。也就是说,对于每个窗口,我们还需要记录一些与其相对应的数据,以便在收到WM_PAINT消息时做不同处理。把所有这些逻辑写在钩子函数里显然太麻烦了,即使能写出来也没法维护,我们需要一个好的设计。
根据面向对象的思想,我们需要为每种窗口类型写一个类,并为每个窗口生成一个对应类的实例,由这些实例来处理窗口消息,并记录必要的窗口状态数据。这样,处理窗口消息的任务就交给这些对象了,那么,怎么把消息传递给这些对象呢,用钩子函数转发是一种方案,不过我们这里采用了另一种:SubclassWindow ,关于SubclassWindow的原理,就不多讲了,可以参看MSDN,其实就是替换一个窗口过程函数。ATL提供了现成的支持,用起来还是很方便的,替代的窗口过程函数不用全部自己写,而可以用消息映射宏生成。
现在我们用SubclassWindow的方式可以直接把我们的对象链接到窗口的消息链中,这好像有点和钩子函数的功能重复了,因为钩子函数本来就是用来截获消息的。现在SubclassWindow以后,窗口的消息已经可以被截获了,那还要钩子函数干什么呢。
答案是:钩子函数用来执行SubclassWindow操作 。原因有两个,第一,我们要做的是一个skin plugin,我们希望使用者调用一个函数就可以改变整个界面风格,而不是为每个窗口调用SubclassWindow函数;第二,有些窗口的创建根本不是在代码里控制的,例如菜单窗口,除了使用钩子函数,我们甚至不能获得菜单窗口的句柄。所以,我们必须使用钩子函数,但在钩子函数中,我们只处理一个消息:WM_CREATE,在任何一个可识别窗口创建时,生成一个对于的对象实例,并用SubclassWindow挂接这个实例到目标窗口,剩下的事情让这个对象实例去完成。

粗略的设计已经有了,总结一下:
1、为每种可识别的窗口类编写类,实现必要的消息处理和状态保存;
2、用钩子函数截取WM_CREATE消息,并创建对应的类实例;
3、通过SubclassWindow操作把生成的类实例挂接到目标窗口,完成消息处理和状态保存的工作;

posted on 2004-05-18 11:49 vibration 阅读(484) 评论(6)  编辑 收藏

评论

# re: Skin技术实现框架(三) 2004-05-19 18:06 yanxm

在codeproject上就看到这篇文章的e文版。觉得很有创意,在这里先谢过了。希望看到后续文章。

# re: Skin技术实现框架(三) 2004-05-19 20:37 vibration

会继续写的,中文可以写的详细些,E文就只能尽量精简了,可能很多地方都没有讲清楚:P

# re: Skin技术实现框架(三) 2004-05-20 09:51 yanxm

我下载了你的代码,我编译后运行发现绘制按钮的时候在某些情况下你的绘制和按钮本身的绘制有些冲突。比如当你在按钮上按下鼠标左键不放然后移动鼠标到按钮以外的地方,这个时候按钮本身的绘制效果就会显示出来(本想贴幅图片上来,但是不知道怎么贴。)。但是我运行你的TestApp.exe却没有这个问题,不知道你编译这个测试程序的时候是否加了其他编译选项。还有一个关于GroupBox的问题,由于我的程序需要依据条件动态修改GroupBox的标题,当我替换标题后,就是通过调用函数SetWindowText替换标题后,刷新有点问题,就是GroupBox本身的绘制显示出来了。对于PushButton绘制的问题,我冒昧对你的代码做了点修改。思路就是在对Button进行SubclassWindow操作后将Button的Style改成自绘制,这样就不会和Button本身的绘制干扰了,而且我估计只要处理WM_DRAWITEM和WM_MEASUREITEM消息就可以了,不过我没有这么试过。但是CheckBox和RadioButton没有BS_OWNERDRAW风格,所以没法这么做。绘制干扰问题还没有解决。下面贴出我对CMacButton修改过的地方:
1,增加成员变量long m_iCtrlType;
2,
 void Initialize()
 {
  m_iCtrlType=-1;
  m_nState = STATE_NORMAL;
  m_bTracking = false;
  if(BASETYPE_BUTTON==GetBaseType())
  {
   ::SetWindowLong(m_hWnd,GWL_STYLE,::GetWindowLong(m_hWnd,GWL_STYLE)|BS_OWNERDRAW);
  }
 }
3,
 long GetBaseType()
 {
  if(m_iCtrlType!=-1)
   return m_iCtrlType;
  long lStyle = GetWindowLong(GWL_STYLE);
  if ( (lStyle & BS_OWNERDRAW) == BS_OWNERDRAW )//ownerdraw
   return m_iCtrlType=BASETYPE_OWNERDRAW;
  
  if ((lStyle & BS_GROUPBOX)==BS_GROUPBOX)//groupbox
   return m_iCtrlType=BASETYPE_GROUPBOX;
  else if ((lStyle & BS_CHECKBOX)==BS_CHECKBOX
   || (lStyle & BS_AUTOCHECKBOX) == BS_AUTOCHECKBOX  )//checkbox
   return m_iCtrlType=BASETYPE_CHECKBOX;
  else if ((lStyle & BS_AUTORADIOBUTTON)==BS_AUTORADIOBUTTON
   || (lStyle & BS_RADIOBUTTON)==BS_RADIOBUTTON)//radiobox
   return m_iCtrlType=BASETYPE_RADIOBUTTON;
  
  //普通按钮
  return m_iCtrlType=BASETYPE_BUTTON;
 }
以上绿颜色是我增加的部分。

# re: Skin技术实现框架(三) 2004-05-20 13:29 vibration

使用ownerdraw也是一种实现方式,原来的代码没有用这种方法是考虑到宿主系统可能本来有ownerdraw按钮

# re: Skin技术实现框架(三) 2004-09-15 13:02 快乐风

我下载了阁下的代码,怎么编译不了啊?提示错误是:
c:/documents and settings/chrys/桌面/skinx_demo/skinx/stdafx.h(22) : fatal error C1083: Cannot open include file: 'atlapp.h': No such file or directory

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值