Written by flexitime
PS: 有鉴于几位热心网友的意见,所以还是决定写一下一些技术相关的BLOG,并希望能有网友不吝赐教
DevExpress是一个十分有名的Delphi控件套装,这个套件很多时候都是带有原码的,而我这篇文章正如题目所述的问题,是研究在dxBar中动态添加菜单。其实要完成这件事情并不困难,只是几行代码就说明问题了,但为什么要写这篇文章呢,主要的目的在于说明如何在没有其它资料可查的情况下,通过查源代码找到问题的解决方法。
首先,要使用TdxBar,我们要先在Form上添加一个TdxBarManager的控件,双击这个控件会弹出一个对话框,在ToolBar这一页上点击New..,再输入这个这个Bar的名字(例如 dxbMainMenu),这样就可以生成一个TdxBar了,如果要把这个TdxBar设置成为主菜单的话,那么只需要设置一下IsMainMenu属性为true即可。如果要手工在这个TdxBar实例dxbMainMenu上添加一些菜单项的话,那么可以通过在dxbMainmenu的右键菜单中选择对应的Add SubItem, Add Button等选项去完成。这里不再逐一细表了。
现在关键的问题在于如何在这个TdxBar中通过代码动态添加这些菜单项。
按照一般的思路,我们会有以下几种方案去考虑这个问题
第一.在dxBar中寻找有没有对应的方法,这些方法的名字可能是Add,Insert等等,但十分可惜的事,dxBar中并没有提供这些方法。
第二.我们看一下刚才手工生成的那些对像如 TdxBarSubItem有没有Parent或dxBar的对像,因为有可能会这样的方式来产生相应的菜单:
Bar := TdxBar.Create(nil);
Bar.dxBar := dxbmainMenu; 或 Bar.Parent := dxbmainMenu;
等等,但仍然是找不到合适的对像。
既然,以上常规的方式都找不到正确的方法,那么我们换一个角度去考虑,刚才dxBar其实是由dxBarManager生成出来的。因此,有解决的方法可能会通过dxBarManager才能完成。我们可以在dxBarManager找到一些与Add相关的方法
AddButton
AddSubitem
AddItem
AddToolBar
其中AddToolBar肯定不是我们想要的东西,那么我们通过DevExpress提供的源码(dxBar.pas)查一下这几个方法的实现:
function TdxBarManager.AddButton: TdxBarButton;
begin
Result := TdxBarButton(AddItem(TdxBarButton));
end;
function TdxBarManager.AddItem(AClass: TdxBarItemClass): TdxBarItem;
var
ACategoryIndex: Integer;
begin
Result := AClass.Create(Self);
ACategoryIndex := Categories.IndexOf(dxSBAR_DEFAULTCATEGORYNAME);
if ACategoryIndex = -1 then
ACategoryIndex := 0;
Result.Category := ACategoryIndex;
end;
function TdxBarManager.AddSubItem: TdxBarSubItem;
begin
Result := TdxBarSubItem(AddItem(TdxBarSubItem));
end;
通过代码,可以十分明显地知道,原来AddButton及AddSubItem都是调用AddItem的,而AddItem的作用是通过传入的特定的某些类名来产生相应的对象的,我们可以写以下的代码来试验一下
var
asi : TdxBarSubItem;
begin
asi := self.dxBarManager1.AddSubItem;
asi.Caption := '菜单1';
….
很遗憾,这些代码根本不增加一个菜单子项。
线索似乎又断了,但回想一下,在dxBarManager中提供这个AddItem的方法肯定是有用的,要不然不会多此一举,因此,我们利用搜索的功能看一下那些地方调用了这个方法(最好在Delphi的环境中安装GExpert,利用它的Grep功能来搜),我们可以在同一个源文件中找到这样的内容
function TdxBarItemLinks.AddItem(AItemClass: TdxBarItemClass): TdxBarItemLink;
begin
Result := Add(BarManager.AddItem(AItemClass));
end;
function TdxBarItemLinks.AddButton: TdxBarItemLink;
begin
Result := Add(BarManager.AddButton);
end;
function TdxBarItemLinks.AddSubItem: TdxBarItemLink;
begin
Result := Add(BarManager.AddSubItem);
end;
十分明显,在TdxBarItemLinks中调用了TdxBarMangerger的AddItem方法,但这个TdxBarItemLinks又是何方神圣呢?我们再去查这个TdxBarItemLinks的类,发现很多地方都有这个类的对象成员,其中包括我们想要的TdxBar类。
至此,我们可以测试一下以下的代码了:
var
asi : TdxBarSubItem;
il : TdxBarItemLink;
begin
asi := self.dxBarManager1.AddSubItem;
asi.Caption := '菜单1';
dxbrMainMenu.ItemLinks.Add (asi);
end;
运行看看,这次真的可以在dxbrMainMenu中添加了一个菜单项了,虽然是一个空项,但没有关系,这是个好的开始。
我们再来看,这个新加上去菜单项是在菜单的末尾的,如果我们想把它放在前面的话,那么应该如何办呢?
一般来说在Delphi的习惯都是用Add或Append来进行追加,用Insert来插入,而刚好TdxBarItemLinks也正好符合这种说法,我们来看Insert的实现
function TdxBarItemLinks.Insert(AIndex: Integer): TdxBarItemLink;
begin
Result := TdxBarItemLink(inherited Insert(AIndex));
end;
原来是调用基类的方法,而TdxBarItemLinks的基类是我们十分熟悉的VCL组件TCollection,而它的Insert代码如下,
function TCollection.Insert(Index: Integer): TCollectionItem;
begin
Result := Add;
Result.Index := Index;
end;
看代码就知道原来是调用Add方法,但这个Add方法是不带参数的,与我们刚才提到的方法有所不一样。
我们找回那两个Add方法来看看
function TdxBarItemLinks.Add: TdxBarItemLink;
begin
Result := TdxBarItemLink(inherited Add);
end;
function TdxBarItemLinks.Add(AItem: TdxBarItem): TdxBarItemLink;
begin
BeginUpdate;
try
Result := Add;
Result.Item := AItem;
finally
EndUpdate;
end;
end;
原来有参的Add方法也是调用这个无参的Add方法,而看到有参数的那个Add方法后,是否有些启示呢,我们可以利用TdxBarItemLink.Item来实现某些我们想要的功能,那么代码就变成这样了
var
asi : TdxBarSubItem;
il : TdxBarItemLink;
abb : TdxBarButton;
begin
asi := self.dxBarManager1.AddSubItem;
asi.Caption := '菜单1';
il := dxbrMainMenu.ItemLinks.Insert(0);
il.Item := asi;
这样,就可以把生成出来的菜单项放在前面了。
现在我们来看看如何生成子项了,其实我们已经有了前面的基础,要生成子项也是十分容易的了,可以不用再查源程序了,直接就写出以下的代码
var
asi : TdxBarSubItem;
il, il2 : TdxBarItemLink;
abb : TdxBarButton;
begin
asi := self.dxBarManager1.AddSubItem;
asi.Caption := '菜单1';
il := dxbrMainMenu.ItemLinks.Insert(0);
il.Item := asi;
abb := self.dxBarManager1.AddButton;
abb.Caption := '子项1';
il2 := asi.ItemLinks.Insert(0);
il2.Item := abb;
end;
至此,我们就完成了这个动态添加菜单的例子了。