在论坛也混了蛮长时间了,一直没有发表过什么专题性质的文章。主要是论坛上高手如云,很多学习过程中的问题在论坛上都能找到答案,特别是论坛的精华帖。通过不断学习,我也开始对一些问题形成了些自己的想法。比如最近一段时间碰到一个问题:关于Access中动态添加控件的问题,Access中要给Form动态添加控件之类的,必须切换到窗体的设计模式,即使通过VBA代码也必须这么做。以往碰到这个问题,一般的做法是在窗体中先添加固定数目的控件,然后窗体加载时将其隐藏,当需要动态添加时就将其显示出来,但是这个方法一旦超出当初添加控件的数目时,就没办法解决了,并且控件添加多了对窗体加载速度也有一定影响。另外的话也可以通过一些ActiveX控件来做到这些,不过要找到适合Access且适合自己需求的ActiveX控件并不是件容易的事情,鉴于此我就想怎么才能在窗体上动态添加控件。
其实这个问题纠结了差不多有1年了,当初也想到来自绘这个途径,但是有好几个问题都不懂,所以解决不了。这些问题包括:
1、自绘的话,用什么在窗体上自绘? 肯定不能用Access的控件,线条、框什么的都不能用,因为这些都不能动态添加到窗体上。只有选择通过API来绘图,可以使用的包括GDI、GDI+。但是我那时对GDI和GDI+是一点了解都没有,所以画了很长时间研究VBA中用API、GDI跟GDI+。
2、要用API绘图就要有窗体句柄、要获得设备环境(DC),Access里怎么获取这些了?
可能有些人马上会想到Access窗体有个hwnd属性啊,不就可以了吗?其实这里面还有些曲折,后面我会详细说。这里大家所要了解的是Access的窗体下面还包含了好几个,包括窗体页眉、主体跟窗体页脚,它们都有句柄,要进行绘图的话,你得获取对应的句柄,而不是直接使用Access窗体的hwnd属性。
3、以上2个问题解决了,还只是完成了在窗体上自绘,要怎样才能将这些自绘窗体像控件一样使用到其他窗体上了?
可能大家看完这个问题,对Access有些了解的朋友会马上想到子窗体。但是当时我是想了1个星期才想到用子窗体,因为当初对这个问题我的想法是怎么在Access中做自定义控件,而没有想到怎么将窗体放到窗体里面这个方法。使用子窗体作为类似“控件”容器的承载体,这就解决的自定义控件的“容器”问题。
好了,说完这几个问题,那么我再总结下要读懂本文内容所需要储备的知识,如果你还对以下内容完全不了解的话,我建议你首先百度下或者找找相关的书什么的了解下,当然你也可以继续读下去,因为我会尽力讲的通俗易懂。不过如果你感觉阅读的很吃力的话,那你最好还是补一补相关的内容再来。
1、VBA中如何使用API?
2、GDI是用来干什么的?如何使用GDI?GDI句柄跟设备环境的关系,如何用GDI绘图?
3、Access窗体的构成。
4、Access子窗体是什么?怎么使用子窗体?
5、VBA中的类模块是什么?怎么使用类模块?类模块属性、方法、事件怎么建立?
6、Access窗体与类模块的关系;
7、使用VBA代码怎么调用自定义“控件”?
8、集合在类模块中的使用;
另外我也想说明一下,由于本贴内容可能会比较长,我会分批将所写内容更新进来,由于平时工作比较忙,可能一次更新的内容也不会太多,所以希望大家也不要急躁,慢慢看慢慢消化。另外相应的代码部分也有很多在调试之中,但是大部分主体的代码已经完成,我暂时不把源代码随帖子一起发布,我会将其中的大部分代码写到本贴里面并讲解,希望有兴趣的将贴看下去。
下面我们就开始讲怎么在Access来做一个类似TabControl的“控件”。
首先,我们来看下最终的效果,示例中包含了2个窗体,frmTest是个测试窗体,TabControl就是我们所谓的当作控件来使用的子窗体。另外还有些模块跟类模块,有些模块是无用的,因为我在做这个的时候,借鉴了部分代码,只是没有删除,我在后面会说到有哪些模块跟代码会使用到的,所以这里就不再说明各个模块的作用了。
双击打开frmTest,默认会建立3个框,相当于3个Tab,点击添加按钮,会自动添加Tab,点删除按钮会从最后依次删除Tab,在某个Tab上点击,会弹出一个对话框显示当前Tab的序号。
在动手编写代码前,首先我们得分析下TabControl控件的结构,搞清楚我们需要建立什么样的模块、类模块以及窗体模块。从上面我们已经看到了我们用了一个子窗体作为TabControl的容器,那么TabControl里面还包括了很多Tab,这些Tab会构成一个集合Tabs,所以这个控件的层级关系就是:
TabControl
+---Tabs
+----Tab
之所以要理清楚这个关系,是因为基于这个结构建立我们的“控件”,会大大方便对我们控件的访问。这里的TabControl对应我们的窗体,Tabs的话我们将在TabControl的窗体代码中建立一个私有集合变量mTabBars,而Tab这个东西就需要我们自己来写类模块了。我将这个类模块命名为clsAccTabBar,cls代码是类模块,Acc表示是Access中的,TabBar就是这个类模块的含义。
下面我们来分析下这个类模块的内容,这个类模块所代表的是TabControl中的一个TabBar:
1、与属性相关的:包括TabBar的位置信息(Top、Left、Right、Bottom)、鼠标是否在其上(IsMouseOn)、是否被单击(Selected)、显示文字内容(Text)、标识字符串(Key)。可能大家还会说有与颜色相关的属性,这些我都放在了TabControl里面了,因为这些颜色是所有Tab共用的,而不是某一个Tab专属的,即使是选中色、鼠标移动其上的颜色。
2、与方法相关:Tab重画,这个方法我将它写在了TabControl里面了,当然你如果有兴趣可以为Tab建立一个ReDraw的方法;
3、与事件相关:TabBar被单击事件,TabBar鼠标移动事件,这2个事件的实现有些特殊,按道理应该在Tab类模块里建立这2个事件,但是鼠标的移动跟单击触发都是在TabControl里面,所以这2个事件我都把实现做到了TabControl窗体的事件代码里面了,后面讲述TabControl的时候我会再讲;
从上面的描述来看,我基本上把这个clsAccTabBar类模块只让其用于保存各个Tab相关信息,下面是类模块里面的代码:
1 Option Compare Database 2 3 Private mIndex As Integer 4 Private mKey As String 5 Private mText As String 6 Private mTargetFom As String 7 Private mSelected As Boolean 8 Private mIsMouseOn As Boolean 9 10 Public Property Get Index() As Integer 11 Index = mIndex 12 End Property 13 14 Public Property Let Index(Value As Integer) 15 mIndex = Value 16 End Property 17 18 Public Property Get Key() As String 19 Key = mKey 20 End Property 21 22 Public Property Get Text() As String 23 Text = mText 24 End Property 25 26 Public Property Let Text(Value As String) 27 mText = Value 28 End Property 29 30 Public Property Get TargetFom() As String 31 TargetForm = mtargetform 32 End Property 33 34 Public Property Get Left() As Long 35 Left = mRect.Left 36 End Property 37 38 Public Property Let Left(Value As Long) 39 mRect.Left = Value 40 End Property 41 42 Public Property Get Right() As Long 43 Right = mRect.Right 44 End Property 45 46 Public Property Let Right(Value As Long) 47 mRect.Right = Value 48 End Property 49 50 Public Property Get Top() As Long 51 Top = mRect.Top 52 End Property 53 54 Public Property Let Top(Value As Long) 55 mRect.Top = Value 56 End Property 57 58 Public Property Get Bottom() As Long 59 Bottom = mRect.Bottom 60 End Property 61 62 Public Property Let Bottom(Value As Long) 63 mRect.Bottom = Value 64 End Property 65 66 Public Property Get Width() As Long 67 Width = Abs(mRect.Right - mRect.Left) 68 End Property 69 70 Public Property Get Height() As Long 71 Height = Abs(mRect.Bottom - mRect.Top) 72 End Property 73 74 Public Property Get IsMouseOn() As Boolean 75 IsMouseOn = mIsMouseOn 76 End Property 77 78 Public Property Let IsMouseOn(Value As Boolean) 79 mIsMouseOn = Value 80 End Property 81 82 Public Property Get Selected() As Boolean 83 Selected = mSelected 84 End Property 85 86 Public Property Let Selected(Value As Boolean) 87 mSelected = Value 88 End Property
有些属性我在前面没有提到,而在代码里又有,比如Width、Height,这个是宽度、高度,这个都是根据其他属性值来计算得到的。当然这里我再给大家提一下类模块的属性建立问题。 前面有很多私有变量声明,我这里把它们叫做类模块的字段,它们都是以m开头的,之后我所有的代码都是以m开头来代表类模块中的字段,与这些字段对应的Get/Let属性方法表示对这些字段的读取/写入操作。类模块中建立字段、属性的标准范式就是如此,应该避免使用公用变量。如果你对类模块的属性建立不是很清楚,还请在论坛或百度查阅相关的内容。
原文:http://www.cnblogs.com/alexywt/p/5087175.html