前一段时间,到昆明转了一圈,做了一个小项目,又接了一个稍大一点的项目。呵呵,明年的收入有保障了。
昆明是个好地方,虽然当地人说已经有些冷了,但我觉得还很暖和,空气确实不错,中午不睡午觉也不觉得困。
唯一受不了的是晚上,6点左右已用餐完毕,已经很丰盛的了,但用户到晚上9点左右,又拉我出去喝酒吃肉,老天,到十一二点还在吃!!真受不了。
事情多,不怎么出去转,最后跑到云南第一村去了一下,还不知道叫什么名字,好象叫大什么村的,说与什么华西村同一类型的,果然不错,挺有规模。
明年三月再去,交付新项目的演示版。
OK,言归正传,这次到昆明去,给用户交付,算是一个了结。在这段时间中,完成了用户提出的几个需求,还有一个没有做,留待回来再做,因为上网不方便,无法处理。
完成的需求主要是以下几个:
1. 画图时,加上分区功能,即在工作区画出分区坐标,如下A 17之类的,便于用户看图定位。
实现后,用户可简单地在页面设置功能中完成相应的定制工作,具体的步骤将在后续的使用手册中说明。
2. 画图时,元件的管脚名称可简单地通过右键进行修改。
3. 完善实时服务与数据库服务,以支持可视化方式进行设置。
4. …
未完成的需求说明如下:
若打开多个图之后,将有些文件名称按钮无法显示在工具条上,用户希望在工具条上有个箭头按钮,通过该按钮的点击,可选择那些未显示出来的按钮,就象IE中一样。
这个需求说起来简单,我花了半天功夫,做出来的效果非常的不好看。干脆等回家后再做,上网查查看有没有解决方法。
回家后,查来查去,发现这是一种技术,微软件称之为Chevron!即从 IE 5.0 以后,IE 的工具栏具有了一个新特性:当 IE 窗口缩小,使得工具栏上的按钮不能完全显示时,工具栏右边会出现一个小按钮,点击后出现一下拉列表,显示出被隐藏的按钮。
再深入地查一下,发现有消息进行支持。
再搜下去,希望找到有现成的例子,但满世界的全是Delphi的实现,C++ Builder还没有如此处理。
通过归纳,网上主要有以下几篇文件有用:
1. Delphi 完全时尚手册之 CoolBar 篇---实现 CoolBar 的新特性 Chevron
2. #170:How can I display the chevron button for toolbar?
3. #171:How can I display the chevron for toolbar and open dropdowned popup menu with invisible buttons?
以上几篇文件的地址我没有保存,有意者可上网搜一下,或者问我要即可。
下面就开始试验。
按《Delphi 完全时尚手册之 CoolBar 篇---实现 CoolBar 的新特性 Chevron》中的方法,事情很多,要改VCL中的ComCtrls.pas,再重编译,再实现,忙乎了半天,效果不甚满意。
于是专攻#170与#171,首先按170来做,希望能出现Chevron按钮,然后再处理下拉式菜单。
先把代码录入进去,把下面的代码从Delphi转成C++ Builder
- procedure TForm1.FormCreate(Sender: TObject);
- const
- RBBS_NOGRIPPER = $00000100; // never show the gripper
- RBBS_USECHEVRON = $00000200;
- RBBIM_IDEALSIZE = $00000200;
- var
- i: Integer;
- rbi: TSMRebarBandInfo;
- R: Trect;
- begin
- inherited;
- {if somebuttons in toolbar are invisible, add chevron}
- rbi.cbSize := SizeOf(rbi);
- rbi.fMask := RBBIM_STYLE or RBBIM_IDEALSIZE;
- for i := 1 to yourCoolBar.Bands.Count do
- begin
- if not Assigned(yourCoolBar.Bands[i-1].Control) or
- not (yourCoolBar.Bands[i-1].Control is TToolbar) then continue;
- SendMessage(yourCoolBar.Handle, RB_GETBANDINFO, i-1, LongInt(@rbi));
- rbi.fStyle := rbi.fStyle or RBBS_USECHEVRON;
- rbi.fStyle := rbi.fStyle and not RBBS_NOGRIPPER;
- with TToolbar(yourCoolBar.Bands[i-1].Control) do
- if ButtonCount <> 0 then
- with Buttons[ButtonCount-1] do
- rbi.cxIdeal := Left + Width + 5;
- SendMessage(yourCoolBar.Handle, RB_SETBANDINFO, i-1, LongInt(@rbi));
- end;
- end;
这个倒是不难,在此过程中,我突然发现RBBS_NOGRIPPER、RBBS_USECHEVRON 、RBBIM_IDEALSIZE几个常量在CB中已定义,难道在CB中已有的Chevron的实现?不过,我找了半天,没有找到。还是先自己实现吧,以后再改。
用一个函数吧
- void __fastcall TForm1::ChevronWorkForCoolBar(TObject * Sender)
- {
- TCoolBar * ACoolBar = dynamic_cast<TCoolBar *>(Sender);
- if(!ACoolBar) return;
- REBARBANDINFO m;
- m.cbSize = sizeof(m);
- m.fMask = RBBIM_STYLE | RBBIM_IDEALSIZE;
- for (int i = 0; i < ACoolBar->Bands->Count; ++i)
- {
- TToolBar * toolbar = dynamic_cast<TToolBar *>(ACoolBar->Bands->Items[i]->Control);
- if (!(toolbar)) continue;
- SendMessage(ACoolBar->Handle, RB_GETBANDINFO, i, long(&m));
- m.fStyle |= RBBS_USECHEVRON;
- m.fStyle &= ~RBBS_NOGRIPPER;
- if(toolbar->ButtonCount)
- {
- TToolButton * button = toolbar->Buttons[toolbar->ButtonCount - 1];
- m.cxIdeal = button->Left + button->Width + 5;
- }
- SendMessage(ACoolBar->Handle, RB_SETBANDINFO, i, long(&m));
- }
- }
再下来就该是下拉菜单了,学习171后,发现是要捕获RBN_CHEVRONPUSHED消息,该消息附于WM_NOTIFY之中(当然,此说法不严谨),那就处理WM_NOTIFY消息呗。
- void __fastcall OnChevronNotify(TWMNotify & message);
- BEGIN_MESSAGE_MAP
- VCL_MESSAGE_HANDLER(WM_NOTIFY, TWMNotify, OnChevronNotify);
- END_MESSAGE_MAP(TForm)
在录入消息处理函数的过程中,准备实现以下代码的CB转化
- type
- PNMREBARCHEVRON = ^TNMREBARCHEVRON;
- TNMREBARCHEVRON = packed record
- hdr: TNMHdr;
- uBand: Integer;
- wID: Integer;
- lParam: LPARAM;
- rc: TRect;
- lParamNM: LPARAM;
- end;
正录入的过程中,我突然想到,刚才发现RBBS_NOGRIPPER、RBBS_USECHEVRON 、RBBIM_IDEALSIZE几个常量在CB中已定义,是否在CB中有相应的结构?
暂不管,结果调试了一会,发现可以不用这个结构。按如下方式进行实现即可。
- void __fastcall TForm1::OnChevronNotify(Messages::TWMNotify & message)
- {
- TToolButton *toolButton;
- TMenuItem * mi;
- TPoint P;
- if(message.NMHdr->code == RBN_CHEVRONPUSHED)
- {
- TToolBar * tb = dynamic_cast<TToolBar *>(CoolBar->Bands->Items[0]->Control);
- if(!tb) return;
- TRect r = tb->ClientRect;
- int i, j = -1;
- for(i = PopupMenu1->Items->Count - 1; i >= 0; --i)
- {
- mi = PopupMenu1->Items->Items[i];
- PopupMenu1->Items->Delete(i);
- delete mi;
- }
- for(i = 0; i < tb->ButtonCount && j == -1; ++i)
- {
- TToolButton * toolButton = tb->Buttons[i];
- if(toolButton->Left + toolButton->Width > r.right)
- j = i;
- }
- if(j != -1)
- {
- for(i = j; i < tb->ButtonCount; ++i)
- {
- mi = new TMenuItem(this);
- toolButton = tb->Buttons[i];
- if(toolButton->Style == tbsSeparator) mi->Caption = "-";
- else
- {
- mi->Caption = toolButton->Caption;
- }
- PopupMenu1->Items->Add(mi);
- }
- P = tb->ClientToScreen(Point(tb->Buttons[j]->Left, tb->Buttons[j]->Top + tb->Buttons[j]->Height));
- PopupMenu1->Popup(P.x+2, P.y+2);
- }
- }
- }
在此过程中,还发现一个问题,即Chevron按钮按下时,消息是发给CoolBar的父窗口的,也就是说,如果CoolBar的父窗口对象不为本Form窗口,则上面的OnChevronNotify将不会被执行。
其实,找到问题所在,解决问题也就简单了。下面仅列出解决方法代码,以备后用。
开始:
- __fastcall TForm1::TForm1(TComponent* Owner)
- : TForm(Owner)
- {
- InstanceChildWndProc = MakeObjectInstance(ChildWndProc);
- OldChildWndProc = (void *)(GetWindowLong(Panel1->Handle,GWL_WNDPROC));
- MyOldChildWndProc = (void *)(GetWindowLong(Handle,GWL_WNDPROC));
- SetWindowLong(Panel1->Handle, GWL_WNDPROC, long(InstanceChildWndProc));
- }
- void __fastcall TForm1::FormDestroy(TObject *Sender)
- {
- SetWindowLong(Panel1->Handle, GWL_WNDPROC, long(OldChildWndProc));
- FreeObjectInstance(InstanceChildWndProc);
- }
处理:
- void __fastcall TForm1::ChildWndProc(Messages::TMessage &Message)
- {
- if(Message.Msg == 78)
- Message.Result = CallWindowProc((FARPROC)MyOldChildWndProc, Handle, Message.Msg, Message.WParam, Message.LParam);
- else
- Message.Result = CallWindowProc((FARPROC)OldChildWndProc, Handle, Message.Msg, Message.WParam, Message.LParam);
- }
万事OK。
按我上述过程,应该能实现Chevron功能了,如果不行,再从头检查一下.当然,可以给我发信ChenBinWen@gmail.com,我可以发源码给你.