TGridDrawState名字空间冲突问题及解决方法(E2015 Ambiguity between 'TGridDrawState' and 'Gridseh::TGridDrawState')_gxsky的博客-程序员秘密

技术标签: delphi  components  types  编译器  pascal  ide  C++Builder  

作者: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<Grids_3, gdSelected, gdFixed> TGridDrawState;

其中
gdSelected 表示单元格处于被选择状态
gdFocused 表示单元格正获得焦点
gdFixed 表示单元格是固定的(行或列头)

VCL中常用的Grid控件有: TStringGrid, TDrawGrid, TDBGrid,三者都继承自TCustomGrid,类关系如下:
      TCustomGrid
      /        /
 TDrawGrid   TCustomDBGrid
    |            |
TStringGrid  TDBGrid

TCustomGrid提供了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 <xxx>却是.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混用时,如果都有各自的自画单元格事件,都会有这情况发生。举一反三就行了。
为避免成为妖哥眼中之不负责任的转载者,所以原文未作删改(含部分扯远之内容)^_^
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/gxsky/article/details/3958173

智能推荐

CSS_timshinlee的博客-程序员秘密

Selectors属性选择器伪类选择器伪元素选择器混合选择器Selectors选择器的分类:简单选择器:使用元素类型、class、id等进行匹配。属性选择器:使用属性或属性值进行匹配。伪类选择器:使用元素特定状态或元素特定部分进行匹配,例如鼠标滑过、选中状态、首个子元素等等。伪元素选择器:使用相对于某个元素的特定位置进行匹配,例如每段首字母、某个元素...

CSRF攻击与防御(写得非常好)_weixin_34378922的博客-程序员秘密

转载地址:http://www.phpddt.com/reprint/csrf.html CSRF概念:CSRF跨站点请求伪造(Cross—Site Request Forgery),跟XSS攻击一样,存在巨大的危害性,你可以这样来理解: 攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的,...

使用Chrome控制台进行3D模型编辑的尝试_我爱丁丁猫的博客-程序员秘密

前言:3D模型编辑的核心是对顶点位置和纹理颜色的编辑,这个研究的目的在于寻找一种通过编程方式直接对模型进行编辑的方法,这种编辑方法和时下流行的通过鼠标点选、拖拽进行编辑的方法之间的关系,和前端编程中“...

Docker 入门看这一篇就够了,万字详解!_吴师兄学算法的博客-程序员秘密

点击上方“五分钟学算法”,选择“星标”公众号重磅干货,第一时间送达作者:惨绿少年博客园文章地址:cnblogs.com/clsn/p/8410309.html容器简介什么是Linux...

LWJGL入门指南:使用《我的世界》(Minecraft)同款游戏库开发一个超级“简单”的3D射击游戏_eguid_1的博客-程序员秘密_lwjgl

LWJGL系列文章LWJGL入门指南:序章LWJGL入门指南:安装LWJGL或生成maven或gradle依赖与第一行LWJGL代码LWJGL入门指南:一个“超级”简单的3D射击游戏demo前言前面两章我们不仅大体了解了lwjgl的大体结构,还知道如何编写和运行helloword了, 已经成功的迈入了lwjgl入门的第一步,那么本章将使用lwjgl实现一个“简单”的3D射击游戏,废话不多说,让我们直接开始吧。参考资料https://github.com/LWJGL/lwjgl

linux传数据到prometheus,Prometheus 进阶_Lekecui的博客-程序员秘密

在「Prometheus入门」一文中我们对Prometheus基本知识点做了讲解,并演示了如何监控一个Linux服务器。这篇文章我们将讲解如何对几个常见的应用进行监控。监控MySQL服务器Prometheus通过安装在远程机器上的exporter来收集监控数据,这里要用到的是mysqld_exporter。部署的架构图安装mysqld_exporter123$ wget https://githu...

随便推点

我是一个程序员_simhare的博客-程序员秘密

做了四年的程序了,水平还是不咋的。为什么我还是舍不得这个职业,我仍然喜欢她,我还要继续下去,不管明天怎样 

车载设备上分区音效的实现_睡着的海豚的博客-程序员秘密_车机系统分区

最近产品同事在车机上提出了一个分区音效的功能:语音控制车机上的音视频类应用程序,动态调节音频的左右声道以及低音.思路:Android中控制音频的类主要是AudioTrack,在创建AudioTrack类对象时AudioFlinger系统服务会分配一个随机生成的sessionID(默认情况下sessionID是自动增长且唯一的),因此要想在自己的应用程序里控制第三方应用的音效,需要获取到第三方应用程序的pid以及sessionID;pid还比较容易获取,应用程序开始播放音频的时候,有个音频获取焦点的事件;

Android学习之ViewPager_weixin_30663471的博客-程序员秘密

1.定义  ViewPager是android扩展包v4包中的类,这个类可以让用户左右切换当前的view。其中,android.support.v4是谷歌公司为了解决当前版本碎片化的问题,从而提供的一个兼容的包。可以实现向下兼容的问题,让一些高版本的控件能够在低版本中进行使用;通过ViewPager可以实现左右翻页的效果。2.实现思路  1.新建一个主Activity以及一个主Vie...

【回文树 && 求本质不同回文串的和】ACM-ICPC 2018 南京赛区网络预赛 I. Skr_笑对这个世界的志贵的博客-程序员秘密

Step1 Problem: 给你一个字符串 s,求本质不同的回文字符串的加和 mod 1e9+7。 例:s = “1111”, ans = 1111 + 111 + 11 + 1. 数据范围: 1 &amp;lt;= len &amp;lt;= 2e6. 1 &amp;lt;= s[i] &amp;lt;= 9.Step2 Ideas: 前置技能:4348 = ((4*10 + 3)*1...

ASM字节码插桩:QQ空间的热修复解决方案核心技术,安卓程序员的硬通货_阿尔法789的博客-程序员秘密

一、什么是插桩QQ空间曾经发布的《热修复解决方案》中利用 Javaassist库实现向类的构造函数中插入一段代码解决 CLASS_ISPREVERIFIED问题。包括了Instant Run的实现以及参照Instant Run实现的热修复美团Robus等都利用到了插桩技术。插桩就是将一段代码插入或者替换原本的代码。字节码插桩顾名思义就是在我们编写的源码编译成字节码(Class)后,在Andro...

分配问题与Hungarian算法_weixin_34191734的博客-程序员秘密

分配问题与Hungarian算法分配问题 指派问题 匈牙利算法匈牙利方法是一种能够在多项式时间内解决分配问题(assignment problem)的组合优化算法。它由Harold Kuhn 与1955年发展并提出,由于该算法很大程度上依赖于先前两位匈牙利数学家:Denes Konig 和 Jeno Egervary,所以被命名为“匈牙利方法”。1957年James Munkres重新审视...

推荐文章

热门文章

相关标签