正在开发一个非.net得数据表格组件,用到.net应用中去……

在家正在开发一个grid  COM组件,因为实在用.net Winform提供的DataGrid不爽,功能太少了,搞什么都得自己写大堆代码,连最基本得行选都得自己写datagrid.select(...) ...烦了;还是封装几个COM组件,反正系统也只需要运行到win2k的工控机上;大致确定了一下功能和名称,叫做GridCellPro组件,支持多种功能,比如合计列,合计行,打印,中文金额,过滤,排序,加亮,列排序,固定列,多种CELL;还有一个就是报表,水晶报表很强大,可和我使用习惯不一样,也许是习惯了borland的贴心设计吧,还是做好表格组件再说吧,希望能改善一下开发效率。

目前编写完框架代码,已经把表格框架搞完了,只是属性定制对话框还有点问题,到网上搜来个文章看了看,还不错,关键得地方都讲到了;顺便也收录进来,今后忘了又看。


原始地址:http://hubdog.csdn.net/Hubdog/ActiveX2.htm
转自哈巴狗的小窝

完善ActiveX控件

    从VCL出发生成一个基本的ActiveX控件是很容易的,但为了让它更容易使用,比如添加属性页编辑器,还需要做一些额外的工作。接下来我们将继续完善ListX控件,这次不是改进它运行时的功能,而是增强它设计时的编辑功能,为它提供一个属性页和下拉式属性编辑器。

属性列表

    如图1.7所示,在Visuabl Basic的窗体上放置一个ListBoxX控件,这时可以注意到在属性察看器中Cursor 和DragCursor 属性,不像通常在Delphi的属性察看器中所看到的ListBox控件的相应属性那样显示crDefault 和其他crDrag 枚举变量,而是显示0和-12的数值。这是因为TCursor 类型在Delphi中是整数类型,所以当ActiveX control wizard转化TListBox 控件时,Cursor属性就按一个整数属性来处理了。这样,在Visual Basic中我们要想设定列表框控件的光标为时钟形状,我们必须设定Cursor 属性为-11, 它等价于定义在Controls单元中的crHourGlass 常数值,但这样显得非常不直观,很容易忘记和混淆。

图1.7

    同时,在Delphi中使用TListBox 控件时,除了是显示一个符号名而不是数值外,属性察看器还会显示一个下拉式列表,里面包含了所有可能的光标值,那么我们能不能使我们的ActiveX控件在Visual Basic中有同样的属性列表呢?Nothing is impossible!我们可以重载TActiveXControl类的GetPropertyString、GetPropertyStrings和GetPropertyValue 方法来做到。下面就是修改后的ListBox控件代码:

    unit ListBoxImpl;

    interface

    uses

      Windows, ActiveX, Classes, Controls, Graphics, Menus, Forms, StdCtrls,

      ComServ, StdVCL, AXCtrls, DelphiByDesignXLib_TLB;

    type

      TListBoxX = class( TActiveXControl, IListBoxX )

      private

        { Private declarations }

        FDelphiControl: TListBox;

    //省略…

      protected

        { Protected declarations }

        procedure InitializeControl; override;

        procedure EventSinkChanged(const EventSink: IUnknown); override;

        procedure DefinePropertyPages( DefinePropertyPage: TDefinePropertyPage); override;

        function GetPropertyString( DispID: Integer; var S: string ): Boolean; override;

        function GetPropertyStrings( DispID: Integer; Strings: TStrings ): Boolean; override;

        procedure GetPropertyValue( DispID, Cookie: Integer;  var Value: OleVariant ); override;

        { Methods that support properties }

        . . .

      end;

    implementation

    uses

      TabWidthPpg, AboutListBox, SysUtils;

    function TListBoxX.GetPropertyString( DispID: Integer;var S: string ): Boolean;

    begin

      case DispID of

        5: //  5 =IlistBoxXDisp接口中DragCursor属性的DispID

        begin

          S := CursorToString( Get_DragCursor );

          Result := True;

        end;

        26:   // 26 = IlistBoxXDisp接口中Cursor属性的DispID

        begin

          S := CursorToString( Get_Cursor );

          Result := True;

        end;

        else

          Result := False;

      end;

    end;

    function TListBoxX.GetPropertyStrings( DispID: Integer; Strings: TStrings ): Boolean;

    var

      I: Integer;

      Cookie: Integer;

      TempList: TStringList;

    begin

      case DispID of

        5,    //  5 =IlistBoxXDisp接口中DragCursor属性的DispID

        26:   // 26 = IlistBoxXDisp接口中Cursor属性的DispID

        begin

          TempList := TStringList.Create;

          try

            GetCursorValues( TempList.Append );

            for I := 0 to TempList.Count - 1 do

            begin

              Cookie := StringToCursor( TempList[ I ] );

              Strings.AddObject( TempList[ I ], TObject( Cookie ) );

            end;

          finally

            TempList.Free;

          end;

          Result := True;

        end;

        else

          Result := False;

      end;

    end;

    procedure TListBoxX.GetPropertyValue( DispID, Cookie: Integer;

 var Value: OleVariant );

    begin

      case DispID of

        5,    //  5 =IlistBoxXDisp接口中DragCursor属性的DispID

        26:   // 26 = IlistBoxXDisp接口中Cursor属性的DispID

        begin

          { Cookie 代表被选的项目}

          Value := Cookie;

        end;

      end;

    end;

    {= 省略…}

    initialization

      TActiveXControlFactory.Create( ComServer, TListBoxX, TListBox,

        Class_ListBoxX, 1, '{B19A64E4-644D-11D1-AE4B-444553540000}', 0);

    end.

    这里TListBoxX 控件由于是继承于TActiveXControl,所以在TListBoxX类中可以重载上面的方法。

    GetPropertyString 函数在属性察看器显示一个属性时会被调用,通过重载这个方法,我们可以为一个属性值提供一个字符串表达。它同Delphi的属性编辑器TPropertyEditor类的GetValue方法比较类似。

    GetPropertyString 函数会提供两个参数,第一个是被请求字符串表达的属性的dispid。因为所有控件属性的显示都会调用这个方法,所以必须过滤要处理的属性,这通过用Case语句来检验Dispid就可以了。要想确定每个属性对应的Dispid,可以察看类型库接口单元。类型库单元的代码如下:

    unit DelphiByDesignXLib_TLB;

    { This file contains pascal declarations imported from a type library.

      This file will be written during each import or refresh of the type

      library editor.  Changes to this file will be discarded during the

      refresh process. }

    interface

    uses Windows, ActiveX, Classes, Graphics, OleCtrls, StdVCL;

    const

      LIBID_DelphiByDesignXLib: TGUID =

        '{B19A64DB-644D-11D1-AE4B-444553540000}';

    const

    { Component class GUIDs }

      Class_ListBoxX: TGUID = '{B19A64DE-644D-11D1-AE4B-444553540000}';

    type

    { Forward declarations: Interfaces }

      IListBoxX = interface;

      IListBoxXDisp = dispinterface;

      IListBoxXEvents = dispinterface;

    { Forward declarations: CoClasses }

      ListBoxX = IListBoxX;

    { Forward declarations: Enums }

      TxBorderStyle = TOleEnum;

      TxDragMode = TOleEnum;

      TxImeMode = TOleEnum;

      TxListBoxStyle = TOleEnum;

      TxMouseButton = TOleEnum;

    { Dispatch interface for ListBoxX Control }

      IListBoxX = interface(IDispatch)

        ['{B19A64DC-644D-11D1-AE4B-444553540000}']

        function Get_DragCursor: Smallint; safecall;

        procedure Set_DragCursor(Value: Smallint); safecall;

    . . .

        function Get_Cursor: Smallint; safecall;

        procedure Set_Cursor(Value: Smallint); safecall;

    . . .

        procedure AboutBox; safecall;

        . . .

        property DragCursor: Smallint

          read Get_DragCursor write Set_DragCursor;

        . . .

        property Cursor: Smallint read Get_Cursor write Set_Cursor;

      end;

    { DispInterface declaration for Dual Interface IListBoxX }

      IListBoxXDisp = dispinterface

        ['{B19A64DC-644D-11D1-AE4B-444553540000}']

        property BorderStyle: TxBorderStyle dispid 1;

        property Color: TColor dispid 2;

        property Columns: Integer dispid 3;

        property Ctl3D: WordBool dispid 4;

        property DragCursor: Smallint dispid 5;

        property DragMode: TxDragMode dispid 6;

        property Enabled: WordBool dispid 7;

        property ExtendedSelect: WordBool dispid 8;

        property Font: Font dispid 9;

        property ImeMode: TxImeMode dispid 10;

        property ImeName: WideString dispid 11;

        property IntegralHeight: WordBool dispid 12;

        property ItemHeight: Integer dispid 13;

        property Items: IStrings dispid 14;

        property MultiSelect: WordBool dispid 15;

        property ParentColor: WordBool dispid 16;

        property ParentCtl3D: WordBool dispid 17;

        property Sorted: WordBool dispid 18;

        property Style: TxListBoxStyle dispid 19;

        property TabWidth: Integer dispid 20;

        property Visible: WordBool dispid 21;

        procedure Clear; dispid 22;

        property ItemIndex: Integer dispid 23;

        property SelCount: Integer readonly dispid 24;

        property TopIndex: Integer dispid 25;

        property Cursor: Smallint dispid 26;

        procedure AboutBox; dispid -552;

      end;

    { Events interface for ListBoxX Control }

      IListBoxXEvents = dispinterface

        ['{B19A64DD-644D-11D1-AE4B-444553540000}']

        procedure OnClick; dispid 1;

        procedure OnDblClick; dispid 2;

        procedure OnKeyPress(var Key: Smallint); dispid 3;

        procedure OnColorItem(Index: Integer;

                              var Color: TColor); dispid 4;

      end;

    implementation

    end.

    从上面可以看到,与控件属性对应的Dispid可以在IListBoxXDisp 声明部分找到。

    GetPropertyString 函数的第二个参数是一个字符串变量。这个字符串变量将会被显示为相应的属性值,但要注意的是函数的返回值一定要设为True,返回False 将使属性察看器忽略返回的字符串参数。

    在上面的GetPropertyString 方法中,使用了CursorToString 函数来转化当前属性值为一个字符串,同时Get_Cursor和Get_DragCursor方法被用来获得当前的属性值。

    实现GetPropertyString 方法可以确保对于每个Cursor数值都会有一个字符串来说明它,但是如何显示所有光标的下拉列表呢?这时就必须重载GetPropertyStrings 和GetPropertyValue 方法了。GetPropertyStrings 负责生成一个将要显示在下拉列表中的字符串列表。当用户从列表中选择了一个列表项后,GetPropertyValue 方法就会被调用来返回真正的数值。

    首先让我们来看GetPropertyStrings 函数,这个方法有两个参数:一个是Dispid,一个是字符串列表,Dispid自然还是对应于操作的属性,而字符串列表则是用来容纳将要显示在下拉列表中的字符串,此外,字符串列表还必须为每个列表项保存一个唯一的“cookie”值,当用户选择了一个列表项后,对应的cookie值会传递给GetPropertyValue 方法。

    为了把cookie值同列表中的字符串关联起来,调用Strings.AddObject 方法来添加字符串到列表中:

      try

        GetCursorValues( TempList.Append );

        for I := 0 to TempList.Count - 1 do

        begin

          Cookie := StringToCursor( TempList[ I ] );

          Strings.AddObject( TempList[ I ], TObject( Cookie ) );

        end;

      finally

图1.8

    把要显示的字符串作为第一个参数,而第二个参数为Cookie值来调用。Cookie值可以取任意值,这里使用的是Cursor本身对应的整数值。注意AddObject方法的第二个参数需要是TObject类型的参数,所以这里进行了一次类型映射。

    GetPropertyValue 方法则有三个参数:第一个参数是属性对应的Dispid,第二个参数是对应于被选列表项的Cookie值,而第三个是用来返回属性值的参数。这个方法的处理非常简单,对于我们这个例子来说,只要把Cookie值返回就可以了,因为它就是实际的Cursor对应的值。

    实现这些方法后,我们要做的就是重新注册控件了,图1.8显示了新界面更加友好的属性下拉列表。

实现属性页支持

    仔细看一下Visual Basic中的ListBoxX控件的属性察看器,就会发现Items 属性没有显示在其中,但ListBoxX控件是有Items 属性的,其实这是因为ActiveX Control Wizard把它由published属性声明为public类型的属性了,这使得我们只能在运行时操作Items 属性,而无法在设计时编辑Items 属性。因为Visual Basic没有为Items类型的属性提供缺省的属性编辑器,所以ActiveX Control Wizard就没有published Items属性。

图1.9

    但可以通过自定义的属性页编辑器来提供对Items属性设计时的编辑支持。属性页总能在各种支持ActiveX控件程序中看到,它提供了方便的属性编辑功能。Delphi 提供了一些现成的预定义的属性页支持,可以把它同ActiveX控件相关联。每个属性页有一个对应的Class ID,它声明在AxCtrls单元中,图1.9列出了每个ID和对应的属性页的功能:

    这些预定义的属性页被设计成可以用于任意的ActiveX控件。每个属性页都使用运行时类型信息 (RTTI)来确定控件中哪个属性可以使用相应的属性页进行编辑,并会把每个属性名添加到一个下拉编辑框中,这样的话,一个属性页就可以编辑所有相同类型的属性了。图1.10显示了用来编辑ListBoxX控件的Items属性的字符串属性页:

    要想把预定义的属性页同ActiveX控件相关联,只要在控件的DefinePropertyPages 方法中添加对DefinePropertyPage方法的调用就可以了,下面代码把字符串和字体属性页同ListBoxX控件进行了关联:

图1.10

    { TListBoxX }

    procedure TListBoxX.DefinePropertyPages(

      DefinePropertyPage: TDefinePropertyPage );

    begin

      { 把预定义的属性页同控件关联 }

      DefinePropertyPage( Class_DStringPropPage );

      DefinePropertyPage( Class_DFontPropPage );

      //省略…

    end;

用户定制的属性页

    除了使用预定义的属性页外,还可以创建自定义的属性页。选菜单File|New,切换到ActiveX页,然后选择Property Page项,Delphi将生成一个属性页的窗体文件和单元文件。下面就是生成的TabWidthPpg属性页的代码:

    unit TabWidthPpg;

    interface

    uses

      SysUtils, Windows, Messages, Classes, Graphics, Controls, StdCtrls, ExtCtrls, Forms,

      ComServ,  ComObj,  StdVcl,  AxCtrls,

      ComCtrls;

    type

      TPpgTabWidth = class(TPropertyPage)

        GrpPreview: TGroupBox;

        GrpTabWidth: TGroupBox;

        LstPreview: TListBox;

        ChkUseTabs: TCheckBox;

        TrkTabWidth: TTrackBar;

        procedure ChkUseTabsClick(Sender: TObject);

        procedure TrkTabWidthChange(Sender: TObject);

      private

        { Private declarations }

      protected

        procedure UpdatePropertyPage; override;

        procedure UpdateObject; override;

      public

        { Public declarations }

      end;

    const

      Class_PpgTabWidth: TGUID =

      '{8BE91420-9070-11D1-AE4B-44455354616F}';

    implementation

    {$R *.DFM}

    procedure TPpgTabWidth.UpdatePropertyPage;

    var

      I: Integer;

    begin

      { 使用OleObject更新对象}

      { 从对象中复制字符串到预览列表框 }

      for I := 0 to OleObject.Items.Count - 1 do

        LstPreview.Items.Add(OleObject.Items[I]);

      ChkUseTabs.Checked := OleObject.TabWidth > 0;

      TrkTabWidth.Position := OleObject.TabWidth div 4;

      LstPreview.TabWidth := OleObject.TabWidth;

    end;

    procedure TPpgTabWidth.UpdateObject;

    begin

      { 使用OleObject更新控件TabWidth属性}

      OleObject.TabWidth := LstPreview.TabWidth;

    end;

    procedure TPpgTabWidth.ChkUseTabsClick(Sender: TObject);

    begin

      TrkTabWidth.Enabled := ChkUseTabs.Checked;

      if ChkUseTabs.Checked then

        LstPreview.TabWidth := TrkTabWidth.Position * 4

      else

        LstPreview.TabWidth := 0;

    end;

    procedure TPpgTabWidth.TrkTabWidthChange(Sender: TObject);

    begin

      Modified;

      LstPreview.TabWidth := TrkTabWidth.Position * 4;

    end;

    initialization

      TActiveXPropertyPageFactory.Create(

        ComServer, TPpgTabWidth, Class_PpgTabWidth);

    end.

图1.11

    注意上面TPpgTabWidth 类是从TPropertyPage继承来的,它的上层父类是TCustomForm。因此我们可以像普通的Delphi窗体一样定制属性页。这个属性页允许用户可视化的调整ActiveX控件的TabWidth 属性。图1.11就是属性页示意图。

    属性页显示时应该能够根据存储在ActiveX控件中的数据更新相应属性页页面的显示,这一切都可以在UpdatePropertyPage方法中来加以实现。上面代码中的TPpgTabWidth. UpdatePropertyPage 方法就是先从ActiveX控件中获得字符串,接着设定ListPreview 列表框,然后是checkbox状态和TabWidth 属性被初始化。一旦激活属性页后,必须能够根据用户输入更新ActiveX控件的属性,这是通过实现UpdateObject 方法来完成的。上面代码中TPpgTabWidth.UpdateObject 方法就是简单的根据预览列表的TabWidth设定更新ActiveX控件的。要注意的是方法中用到的OleObject 就代表ActiveX控件,因此可以使用它来设定数据。

    同预定义的属性页一样,我们也要把自定义的属性页同ActiveX控件相关联。同前面一样,通过在DefinePropertyPages 方法中添加对DefinePropertyPage的调用就可以了,这回参数就是新建立的属性页的GUID ,下面就是实现代码:

       { 把用户定制的属性页同控件相关联 }

       DefinePropertyPage( Class_PpgTabWidth );

分发ActiveX控件

    显然分发包中必须包括编译生成的*.ocx 文件,如果ActiveX项目中用到了其他运行时包,我们还需要分发相应的文件。

    如果创建ActiveX控件时选择生成设计时的许可文件,还需要分发相应的*.lic文件。

    另外,如果创建的ActiveX项目用到了IStrings 接口或预定义的字体、颜色、字符串或图像属性页的话,我们还必须分发标准的VCL类型库,它包括StdVcl32.dll和StdVcl32.tlb类型库文件。这两个文件是被Delphi安装到了Windows的系统目录下(比如,C:\WinNT\System32)。凡是使用了预定义的属性页的控件必须分发StdVcl32.dll,然而如果只使用了IStrings接口的话,可以只分发StdVcl32.tlb类型库。

注册ActiveX的工具Turbo Register Server

    要想使ActiveX控件生效,必须更新注册表信息,这可以使用Turbo Register Server (TRegSvr)程序来完成,这个程序是Borland作为一个演示程序提供的。例子源代码位于Demos\ActiveX\TRegSvr目录下。

    TRegSvr 是一个简单的命令行程序,它支持一些开关来控制ActiveX控件的注册,除了要注册的文件名外不加任何参数的话就会注册指定文件。加了-u开关的话就会注销ActiveX控件,-t开关是用来表明ActiveX控件的类型库也应该被注册,如果要注册的文件名为*.tlb,则这个开关可以不设定。-q开关表示TRegSvr不显示任何输出信息,这使得它比较适用于嵌入于安装程序中。

    下面是使用TRegSvr程序注册TListBoxX 控件的例子示意:

    TRegSvr -q XXX.ocx

    TRegSvr -q StdVcl32.dll

posted @ 2005-04-19 23:28  suifei  阅读(1723)  评论(0编辑  收藏  举报