TGridDrawState名字空间冲突问题及解决方法(E2015 Ambiguity b...

来源:百度文库 编辑:神马文学网 时间:2024/04/20 20:47:26
TGridDrawState名字空间冲突问题及解决方法(E2015 Ambiguity between 'TGridDrawState' and 'Gridseh::TGridDrawState') 收藏
作者:ccrun(老妖)
相信大家遇到过很多类似这样的问题,用了某些第三方组件后,编译时提示:
E2015 Ambiguity between 'TGridDrawState' and 'Gridseh::TGridDrawState'

E2015 Ambiguity between 'TGridDrawState' and 'Grids::TGridDrawState'
之类的,这是典型的名字空间冲突,ccrun(老妖)在CSDN也帮助解决过多次类似的问题,所以写这篇文档旨在于总结一下,希望能对后来者有帮助。文章是以TGridDrawState来说事,但是道理是相同的,适合VCL中其他的类型是。为了增加文章的字数并且为了显的本文章有力度,多加了一些废话在里面(ccrun是不是很有公务员的潜质呢? - -#)。TGridDrawState是VCL中Grids单元定义的一个集合(Set)类型,常用在表格(Grid)的单元格(Cell)绘制中,原型如下:
enum Grids__3 { gdSelected, gdFocused, gdFixed };
typedef Set TGridDrawState;其中
gdSelected 表示单元格处于被选择状态
gdFocused 表示单元格正获得焦点
gdFixed 表示单元格是固定的(行或列头)VCL中常用的Grid控件有: TStringGrid, TDrawGrid, TDBGrid,三者都继承自TCustomGrid,类关系如下:
      TCustomGrid
      /        \
 TDrawGrid   TCustomDBGrid
    |            |
TStringGrid  TDBGridTCustomGrid提供了OnDrawCell的方法,使得我们可以自画单元格,原型如下:
virtual void __fastcall DrawCell(int ACol, int ARow, const TRect &ARect, Grids::TGridDrawState AState) = 0;TDrawGrid和TStringGrid一直沿用了OnDrawCell这个方法,而TCustomDBGrid却走上另一条路,增加了OnDrawColumnCell和OnDrawDataCell方法,原型如下:
DYNAMIC void __fastcall DrawColumnCell(const Types::TRect &Rect, int DataCol, TColumn* Column, Grids::TGridDrawState State);DYNAMIC void __fastcall DrawDataCell(const Types::TRect &Rect, Db::TField* Field, Grids::TGridDrawState State);
分别对应绘制某列的单元格和单个的单元格。到了TDBGrid这一代的时候,干脆把OnDrawCell方法也隐藏了,嘿嘿,所以TCustomGrid的这两个孙子(TStringGrid和TDBGrid)的自画单元格事件是不太一样,就象表兄弟似的,看着有点相似,却又有很多不同。这三个自画函数的最后一个参数都是TGridDrawState,用来指示当前绘制单元格的状态。前面加Grids::是因为VCL中的TGridDrawState的名字空间(namespace)是Grids。
这本来这是一个和谐的局面,可是大家都知道C++Builder和Delphi都支持在VCL基础上派生出来的第三方组件,这实在是个好功能,相当的好啊。你可以扩展已有的组件,写一个新的功能更强的或者利用第三方现成的组件,加快工程的开发进度,美化界面,增强程序功能等等。不过,在方便的同时,也带来一些负作用:兼容性问题。先不说针对不同的IDE版本分别需要不同的组件包(bpl),就组件内部来讲,由于新组件的不断扩充,类,对象,方法,数据类型,名字空间也在不断的增多,所以有冲突是难免的。有了问题我们就着手解决,下面做个试验:
  首先安装Ehlib v4.14 Full Source版本,本站有下载(广告来的真及时哦):
  http://www.ccrun.com/view.asp?id=149
  新建一个工程,放置一个Ehlib组件中的DBGridEh和一个标准的组件DBGrid在窗体上
  分别双击DBGridEh和DBGrid的OnDrawDataCell事件,在IDE产生的两个函数体中分别加一行注释://,这是为了在测试编译时不至于因为函数体空着而被编译器自动消除了函数声明及定义部分。
  在工程属性中添加一下Ehlib的头文件路径:Project-->Options-->Directories/Conditionals-->Include path中,找到Ehlib的BCB6目录并添加进来。
  然后试着编译,弹出一个对话框,说找不到组件的头文件:GridsEh,你会说,日,不是添加了Include path了吗?表着急的说,IDE在安装Ehlib组件时(源码版),生成的头文件是.hpp的,而在单元文件.h中声明的#include 却是.h的,所以会说找不到头文件了。不过没有关系,点击查找头文件窗口的Broswer按钮,找到Ehlib\BCB6目录下的GridsEh.hpp,点击OK,恩,这一关算是过了,不关马上就又产生Error信息了,大名鼎鼎的E2015错误,也就是文章开头说的那个提示信息:两个类型不明确,因为编译器找到分别属于两个名字空间的相同类型的声明,但是在这里却不知道该用具体的哪一个。晕了吧。
  喝口茶,广告时间:欢迎光临 C++Builder研究 - http://www.ccrun.com/
  这个时候很多朋友就该上网发帖子问或者搜索解决方案了。有的朋友把三方组件的头文件中的有冲突的数据类型前全手工加上了名字空间,有的是把VCL自带的组件头文件中相关的数据类型前加了名字空间。不过ccrun不太建议用这样的方法。我的原则是尽量不修改IDE生成的头文件内容,注意是尽量而不是从来不,有的人喜欢抠字眼。除非是迫不得已。比如你用Pascal写了一个组件,组件响应MouseUp事件,于是添加一行 property OnMouseUp; 但是在书写时却写成了onMouseUp(小写的o),因为在Pascal中不区分大小写,在BCB中可以成功的编译和安装,但是在使用这个组件时就问题来了:编译器编译Pas文件,同时生成相应的.hpp文件,刚才的property OnMouseUp在.hpp中的声明就变成了:__property onMouseUp; 汗!Pascal文件的不区分大小写变成.cpp或.hpp以后就成了灾难了。这是一件很郁闷的事,因为C++Builder不认识.hpp中的onMouseUp,只认识OnMouseUp。这时才不得不手工修改.hpp文件。扯远了扯远了。继续名字空间冲突的解决方案。
// 本文转自 C++Builder研究 - http://www.ccrun.com/article.asp?i=1003&d=15f173
  刚才说的情况,Grid组件的的OnDrawColumnCell处理事件(也就是处理的函数)声明是由IDE自动产生的,如下:
__published: // IDE-managed Components
    TDBGridEh *DBGridEh1;
    TDBGrid *DBGrid1;
    void __fastcall DBGrid1DrawDataCell(TObject *Sender, const TRect &Rect,
        TField *Field, TGridDrawState State);
    void __fastcall DBGridEh1DrawDataCell(TObject *Sender,
        const TRect &Rect, TField *Field, TGridDrawState State);
  可以看到两个函数声明中,都使用了TGridDrawState类型,而事实上,这两个TGridDrawState是不同的,因为属于不同的名字空间,但是IDE没有加上名字空间。既然IDE自动生成的函数声明不好用,那么干脆不用了。我们自己声明和定义Grid组件的的OnDrawDataCell事件处理函数。
先做一下清除工作:选中两个Grid,把OnDrawDataCell事件中的函数名删掉。然后.cpp文件中两个DrawDataCell函数中的注释// 去掉,按一下保存按钮,IDE会自动将这两个空函数的声明和定义删除,接下来:在单元文件的.h中声明:
__published: // IDE-managed Components
    TDBGridEh *DBGridEh1;
    TDBGrid *DBGrid1;
public:
    // 注意这里,把事件声明在了public段内,而不是__published,
    // 就这意味着在设计时选中Grid组件查看其OnDrawDataCell事件时,
    // 这个函数名将不会出现在列表中。不过即使这两个声明在__published段内,
    // 在设计时的事件列表中也看不到,因为函数的参数有变化了
    void __fastcall DBGrid1DrawDataCell(TObject *Sender, const TRect &Rect,
        TField *Field, Grids::TGridDrawState State);
    void __fastcall DBGridEh1DrawDataCell(TObject *Sender,
        const TRect &Rect, TField *Field, Gridseh::TGridDrawState State);
    // 注意上面两个TGridDrawState都添加了名字空间在单元文件的.cpp中定义:
//---------------------------------------------------------------------------
// 63 63 72 75 6E 2E 63 6F 6D
void __fastcall TForm1::DBGrid1DrawDataCell(TObject *Sender, const TRect &Rect,
    TField *Field, Grids::TGridDrawState State)
{
    // DBGridEh的DrawDataCell事件
    //
    // 此处填写具体的绘制DBGrid单元格代码
}
//---------------------------------------------------------------------------
void __fastcall TForm1::DBGridEh1DrawDataCell(TObject *Sender,
    const TRect &Rect, TField *Field, Gridseh::TGridDrawState State)
{
    // DBGridEh的OnDrawColumnCell事件
    //
    // 此处填写具体的绘制DBGridEh单元格代码
}  因为不能在设计时为组件的处理事件选择函数了,所以需要为两个Grid指定OnDrawColumnCell处理事件,不然的话,这两个函数就白写了,不会触发的:
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
    DBGridEh1->OnDrawDataCell = DBGridEh1DrawDataCell;
    DBGrid1->OnDrawDataCell = DBGrid1DrawDataCell;
}  再次编译,终于OK了。
  对本文章所述有其他看法可联系本人:cbfans#163.com或者QQ:165332
再次严重BS不负责任的转载者(点名批评天X网的某某小P孩),转载不留名不说,还篡改作者信息,把玉树临风的妖哥竟改成某某鸟人。- -#,素质,注意素质。还有就是本文章是VCL相关的内容,不要冒失的标题写成在C++中如何如何就贴出去招摇了,太不敬业了。
  其他类似的名字空间冲突都可以按这样的方法解决。比如BusinessSkinForm组件包中的bsSkinStringGrid,bsSkinDrawGrid等,和其他的Grid混用时,如果都有各自的自画单元格事件,都会有这情况发生。举一反三就行了。本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/gxsky/archive/2009/03/05/3958173.aspx