自写一个继承自TTrackBar的VCL组件改怎么处理消息?

kingeboy 2007-11-17 12:27:35
我写了一个继承自TTrackBar的组件,因为不知道要处理那些消息或函数,就在类里面定义个处理WM_PAINT的消息映射
BEGIN_MESSAGE_MAP
VCL_MESSAGE_HANDLER(WM_PAINT, TWMPaint*,OnPaint);
END_MESSAGE_MAP(TTrackBar);
然后在OnPaint函数里面处理组件重绘,但是我不知道这样对不对,也不知道如何来处理鼠标拖动时调节TrackBar的刻度.
我看过CCRUN老大写的ListBox的重绘,有专门处理列表项的重绘事件,那TrackBar应该要处理哪些消息事件,不会完全在OnPaint里面画出来吧,所以想问一下各位做过VCL组件的老大,能指点小弟一下,不胜感激。
...全文
363 19 打赏 收藏 转发到动态 举报
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
laowang2 2008-11-12
  • 打赏
  • 举报
回复
留个标记,其实可以映射窗体的HScroll消息来判断滚动条的位置。
kingeboy 2007-11-21
  • 打赏
  • 举报
回复
搞懂为什么状态改变后不重绘Thumb的问题了
原因是当执行Invalidate()后控件会重绘,但此时的lpDraw->dwDrawStage 会变成CDDS_PREPAINT所以就被函数给拒绝了
下面我修改了一下判断状态改变后强制重新绘制Thumb部分与大家分享:

//---------------------------------------------------------------------------
void __fastcall TCrnTrackBar::CrnNotify(TWMNotify &Message)
{
LPNMCUSTOMDRAW lpDraw = (LPNMCUSTOMDRAW)Message.NMHdr;

if(lpDraw->hdr.code == NM_CUSTOMDRAW)
{
try
{
m_pCanvas->Lock();
m_pCanvas->Handle = lpDraw->hdc;
if(!(lpDraw->dwDrawStage & CDDS_ITEM)) //<--当第一次全部重绘完毕后,如果控件的状态没有
Message.Result = CDRF_NOTIFYITEMDRAW;//改变,再次触发重绘事件的时候,值为CDDS_PREPAINT
else
CrnCustomDraw(Message); //所以就不会执行到这里的重绘函数
}
__finally
{
m_pCanvas->Handle = NULL;
m_pCanvas->Unlock();
}
}
}
//------------------------------------改---------------------------------------
void __fastcall TMyTrackBar::CrnNotify(TWMNotify &Message)
{
LPNMCUSTOMDRAW lpDraw = (LPNMCUSTOMDRAW)Message.NMHdr;

if(lpDraw->hdr.code == NM_CUSTOMDRAW)
{
try
{
m_pCanvas->Lock();
m_pCanvas->Handle = lpDraw->hdc;
static bool bMouseState=false;
if(isMouseOn != bMouseState) //判断标志是否已改变;isMouseOn全局变量
{
bMouseState=isMouseOn ; //下面手动改变NMCUSTOMDRAW结构体的内容
lpDraw->dwDrawStage = CDDS_ITEMPREPAINT; //改变绘制状态,可以执行重绘函数
lpDraw->dwItemSpec = TBCD_THUMB; //绘制项为Thumb
lpDraw->uItemState = 0;
GetThumbRect(lpDraw->rc); //获得Thumb的区域,这个函数是我自己加的,下面有实现
}
if(!(lpDraw->dwDrawStage & CDDS_ITEM))
Message.Result = CDRF_NOTIFYITEMDRAW;
else
CrnCustomDraw(Message);
}
__finally
{
m_pCanvas->Handle = NULL;
m_pCanvas->Unlock();
}
}
}
//---------------------------------------------------------------------------
void __fastcall TMyTrackBar::GetThumbRect(RECT &rc)
{
SendMessage(this->Handle,TBM_GETTHUMBRECT,0,(LPARAM)&rc);
}
//---------------------------------------------------------------------------

然后根据isMouseOn的值在重绘函数里面绘制就行了
alloutoflove 2007-11-21
  • 打赏
  • 举报
回复
记不太清了,我当时实现时显示的Thumb和消息获取到的有一定差距,但应该不多...关于重绘那个我印象中是在响应WM_MOUSEMOVE的时候做一下判断,如果需要重绘的画就强制重绘一下...
kingeboy 2007-11-21
  • 打赏
  • 举报
回复
不好意思上面的消息写错了,应该是TBM_GETTHUMBRECT
还想问个问题就是,能否自己定义TrackBar中Thumb的区域大小,我说的不是用发送TBM_SETTHUMBLENGTH消息来改变,因为这样的话你只要改了大小后控件会自己给出一个固定样式长方形区域,说简单点就是想自己来定义区域(比如是一个竖的长方形或横置的长方形或者是正方形)
kingeboy 2007-11-21
  • 打赏
  • 举报
回复
to:alloutoflove兄你好!谢谢你的回复.
我昨天用消息映射可以正常处理鼠标移入/移出事件,也可以在事件里通过SendMessage给控件自身发TBS_GETTHUMBRECT消息来获得TrackBar上Thumb的区域大小,然后可以通过PtInRect()来判断鼠标是否移动到了Thumb的区域中,但现在的问题是我上面也说了,在事件里用RePaint或Invalidate()都不能导致控件重绘,这样就没法在重绘中根据标志改变Thumb的状态了,不知道该怎么搞?
alloutoflove 2007-11-21
  • 打赏
  • 举报
回复
TTrackBar本身不支持OnMouseEnter和OnMouseLeave的, 我记得以前是通过WM_MOUSEHOVER和WM_MOUSELEAVE/WM_MOUSEMOVE以及TrackMouseEvent实现的,具体代码换工作后我现在没有了...

另外还要判断Thumb的区域来决定当前是否鼠标在这个区域里...难是不难, 只是要花时间处理某些细节..
kingeboy 2007-11-21
  • 打赏
  • 举报
回复
to:alloutoflove
这位仁兄处理过OnMouseEnter和OnMouseLeave事件吗?我现在也想处理这两个事件来控制TrackBar的Thumb的状态,但是我在这两个事件里面改变标志后,用RePaint或Invalidate()都不能导致控件重绘,该怎样搞啊?
王集鹄 2007-11-20
  • 打赏
  • 举报
回复
仰慕一下cc
kingeboy 2007-11-20
  • 打赏
  • 举报
回复
太感谢妖哥了,万分感谢,人品相当的好啊,不愧为4星!
想当初在网上找到妖哥的网站后就一直感觉不错,经常回去光顾(大部分时间都是查资料^_^),现在看来是没跟错啊,先收下了,自己研究一下。
alloutoflove 2007-11-20
  • 打赏
  • 举报
回复
TTrackBar自绘倒是不难的, 老妖提供的思路很好啊, 以前试着绘出过WMP11那样的TrackBar...处理Mouse移入移出花了些时间...
ccrun.com 2007-11-20
  • 打赏
  • 举报
回复
这里是运行后的效果图:


另外,我在代码中留了个小joke,如果你只是照抄代码而不去仔细阅读和理解的话,呵呵。。。
ccrun.com 2007-11-20
  • 打赏
  • 举报
回复
刚才都准备睡觉了,忽然想起还有这个帖子木有回复,年轻的时候放鸽子放太多了,到如今深感不安。既然答应了楼主,就一定要做到,呵呵,于是花了点时间做个小Demo,在演示中,我将继承的组件代码混合在测试单元中了,如果你要自己写一个组件,最好将其放到独立的单元中。多余话不说了,翠花,上代码。
以下代码已经在BCB6.0 + Vista的环境中测试通过。

#include <ComCtrls.hpp>

class PACKAGE TCrnTrackBar : public TTrackBar
{
private:
TCanvas *m_pCanvas;
protected:
void __fastcall CrnNotify(TWMNotify &Message);
void __fastcall CrnCustomDraw(TWMNotify &Message);

BEGIN_MESSAGE_MAP
VCL_MESSAGE_HANDLER(CN_NOTIFY, TWMNotify, CrnNotify);
END_MESSAGE_MAP(TWinControl);
public:
__fastcall TCrnTrackBar(TComponent* Owner);
__fastcall ~TCrnTrackBar();
__published:
};


//---------------------------------------------------------------------------
__fastcall TCrnTrackBar::TCrnTrackBar(TComponent* Owner)
: TTrackBar(Owner)
{
m_pCanvas = new TCanvas;
}
//---------------------------------------------------------------------------
__fastcall TCrnTrackBar::~TCrnTrackBar()
{
delete m_pCanvas;
}
//---------------------------------------------------------------------------
void __fastcall TCrnTrackBar::CrnNotify(TWMNotify &Message)
{
LPNMCUSTOMDRAW lpDraw = (LPNMCUSTOMDRAW)Message.NMHdr;

if(lpDraw->hdr.code == NM_CUSTOMDRAW)
{
try
{
m_pCanvas->Lock();
m_pCanvas->Handle = lpDraw->hdc;
if(!(lpDraw->dwDrawStage & CDDS_ITEM))
Message.Result = CDRF_NOTIFYITEMDRAW;
else
CrnCustomDraw(Message);
}
__finally
{
m_pCanvas->Handle = NULL;
m_pCanvas->Unlock();
}
}
}
//---------------------------------------------------------------------------
void __fastcall TCrnTrackBar::CrnCustomDraw(TWMNotify &Message)
{
LPNMCUSTOMDRAW lpDraw = (LPNMCUSTOMDRAW)Message.NMHdr;

// 63 63 72 75 6E 2E 63 6F 6D
if(!(lpDraw->dwDrawStage & CDDS_PREPAINT))
return;

TRect rct = lpDraw->rc;

if(lpDraw->dwItemSpec == TBCD_THUMB) // 绘制滑块
{
m_pCanvas->Brush->Color = clRed;
m_pCanvas->FillRect(rct);

Frame3D(m_pCanvas, rct, clWhite, clGray, 1);
}
else if(lpDraw->dwItemSpec == TBCD_CHANNEL)
{
// ccrun注:可以自己搞个图片贴上去,效果更好
//m_pCanvas->CopyRect(rct, Form1->Image1->Canvas, Form1->Image1->ClientRect);

m_pCanvas->Brush->Color = clSkyBlue;
m_pCanvas->FillRect(rct);

char cc[] = {0x63, 0x63, 0x72, 0x75, 0x6E, 0x00 }; // a joke - -#
String strCaption = String().sprintf("%s TrackBar", cc);

int nLeftOffset = (rct.Width() - m_pCanvas->TextWidth(strCaption)) / 2;
int nTopOffset = (rct.Height() - m_pCanvas->TextHeight(strCaption)) / 2;

m_pCanvas->Font->Color = clBlue;
m_pCanvas->Font->Name = "宋体";
m_pCanvas->Font->Size = 9;

m_pCanvas->TextRect(rct, rct.Left + nLeftOffset, rct.Top + nTopOffset, strCaption);

DrawEdge(m_pCanvas->Handle, &rct, EDGE_SUNKEN, BF_RECT);
}

// 如果是绘制刻度就交给系统默认的处理
Message.Result = (lpDraw->dwItemSpec == TBCD_TICS)?
CDRF_DODEFAULT: CDRF_SKIPDEFAULT;
}
// 测试一哈
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TCrnTrackBar *ctb = new TCrnTrackBar(this);
ctb->Parent = this;
ctb->Position = 2;

ctb->Left = 100;
ctb->Top = 100;
ctb->Show();
}


kingeboy 2007-11-19
  • 打赏
  • 举报
回复
妖哥真是敬业啊,麻烦了
我刚才试了继承TButton组件就可以不过要重载CreateParams函数然后在函数中加入
TButton::CreateParams(Params);
Params.Style = Params.Style | BS_OWNERDRAW;//<<---BS_OWNERDRAW
就可以处理CN_DRAWITEM事件,但我在继承自TTrackBar的自定义组件中用同样的BS_OWNERDRAW样式后就根本没反应,好像是TTrackBar不支持自绘一样。
ccrun.com 2007-11-18
  • 打赏
  • 举报
回复
今天去外地了,有些累,明天再回复你。
kingeboy 2007-11-18
  • 打赏
  • 举报
回复
kingeboy 2007-11-18
  • 打赏
  • 举报
回复
我在CodeProject上看到个代码可以处理WM_DRAWITEM事件,可以在事件中对DrawItemStruct结构的判断来处理绘出哪一部分,但是我在BC++里面用消息映射处理对应的CN_DRAWITEM事件时候怎么都不会处理到我的函数,真是奇了怪了
cczlp 2007-11-18
  • 打赏
  • 举报
回复
问得太空泛了. 你自己写组件的目的是什么, 既然是继承, 就只改你想改的地方,其它的按原来的就可以了,不至于所有消息都处理. 重绘对应WM_PAINT,拖动对应WM_SCROLL, 实际编程的时侯看情况需要处理哪些消息.
kingeboy 2007-11-18
  • 打赏
  • 举报
回复
没人懂吗?顶起
ydlchina 2007-11-17
  • 打赏
  • 举报
回复
替ccrun顶顶

604

社区成员

发帖
与我相关
我的任务
社区描述
C++ Builder VCL组件使用和开发
社区管理员
  • VCL组件使用和开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧