请问如何自定义属性编辑器?例如:TFileName 类型的属性,编辑时自动弹出 OpenDialog, 这是怎么做的?如果我要在选择 TFileName 属性的值时让 OpenDialog 中的类型为 *.wav 那要怎么做呢?

解决方案 »

  1.   

    让 OpenDialog 中的类型为 *.wav
    设置 OpenDialog  属性
    OpenDialog1.Filter:='*.wav|*.wav';
      

  2.   

    《Delphi开发人员指南》自定义组件一章中有介绍。
      

  3.   

    dht2003(海) 偶没有那本书,能不能介绍一下原理。RegisterPropertyEditor 应该在哪里调用呢?
      

  4.   

    来我论坛吧,
    http://210.83.119.69/bbs
    有这部书下载
      

  5.   

    偶已经基本上搞清了,但是有个单元:Proxys 找不到!
      

  6.   

    shadowfish(小鱼) :
    如果你会的话,请指教。不会的话,也请不要说风凉话。我是诚心来求教想解决问题。
      

  7.   

    在你的Package的Contains中加上designide.dcp如果你是Delphi6,就看看帮助的What's new
      

  8.   

    Contains中只允许 .pas 和 .dcu 啊。我加在了 Requires 里面,可是还是不行。我用的是 delphi7。
    IsProxyClass 这个函数到底是什么意思呀? 就是这一个函数!
      

  9.   

    哦说错了,是在Requires里面D6/D7我都这么干,Proxies单元就包含在designide里面
      

  10.   

    alphax兄弟, 能加我 qq 吗??1600527
      

  11.   

    我真的希望交个高手做朋友,那么有什么问题也好有个讨论。我的 email 是 [email protected]
      

  12.   

    我现在的问题其实很简单:我就是想在我的控件里加个 TWaveFile(string[255]) 类型的属性,
    在编辑的时候就打开 opendialog, 并且只允许选择 wave 文件。能给个例子吗? 多谢多谢!麻烦你了!
      

  13.   

    Proxies找不到,在以前也困扰了我很长时间,下面把帮助我的文字列出来。
    自从Delphi6出来以后,一个经常被提到的问题是Proxies.pas文件从源文件中消失了。
      
      这个改变是大趋势的一个部分。Borland在Delphi 5中没有装载DsgnIntf.dcu,这显然是要强迫迎合Delphi和C++Builder的许可协议。运行时代码在很多控件中常被不经意地用到。在某些方面Borland鼓励:如果你运用新的控件向导,你将发现这个向导只创建了一个单元,它把控件运行时的框架代码和注册函数放在同一个单元中。  在Delphi6中Borland更进一步,不仅用DesignIntf替换了DsgnIntf,而且属性编辑器也被放进了DesignEditors,DesignMenus,DesignWindows和其它的一些设计文件里。特别是DesignEditors使用了其它的一个名叫Proxies的IDE文件。(Proxies代码放在DesignIDE.bpl文件中。)不用说这些改变将会产生编译时的错误。  如果你的运行时代码已经与设计时代码分开了,那么就很容易修改。打开设计时包,然后选择一个目录,点击Add按钮。填写designide.dcp和点击确定。重新编译你的包,这时错误已经没有了。  如果你的设计时代码和运行时代码已经混合在一起了,那应该怎么解决呢?在Delphi里DesignIDE.bpl不是一个可以再分发的包。因此,即使是只是设计时包使用了组件的运行时代码或是只是控件dcu用了都将产生问题。  99.99%的情况事实上很容易解决。你的运行时代码事实上没有使用设计时代码;问题是没有合适的分开。  设计时包应该包括:  1、所有的注册声明。
      2、所有的属性编辑器。
      3、所有的组件编辑器。
      4、将需要DesignIDE和每一个保存组件自己的运行时包。  运行时包应该包括:  1、组件自己。
      2、任何编辑器也许会用到的组件可能自己在运行时调用的窗体。  维一有点混惑的地方是:属或组件是否使用了一个窗体。假如这个窗体在运行时对于组件是可用的,那么它应该包含在运行时包里。如果它只是在设计时可使用,那它就应该包含在设计时包里。一个常见的错误是误认这个窗体本身是一个编辑器,但事实上它不是。而是组件编辑器调用了这个窗体,它是设计时编辑器。  你应该养成一个把组件分开成两个包的习惯,即使是你只在程序中静态地进行链接,因为混合运行时和设计时代码将使你的代码膨胀。你的设计时代码在运行时不会被执行,但是链接器不会知道,所以把它也一起链接进去了。(这就是为什么DsgnIntf要设法链接进去的原因。)  让我们看一个简单的例子,了解如何把设计时代码从运行时代码中分离出去:{ TMixedComponent }
    TMixedComponent = class(TComponent)
      private
        FFileName: String;
      published
        property  FileName : String read FFileName write FFileName;
        { Published declarations }
      end;
      { TMixedFileNameProperty }
      TMixedFileNameProperty = class(TPropertyEditor)
        function    AllEqual: boolean; override;
        procedure   Edit; override;
        function    GetAttributes: TPropertyAttributes; override;
        function    GetValue: string; override;
        procedure   SetValue (const  Value: string); override;
      end;
    procedure  Register;implementationprocedure  Register;
    begin
      RegisterPropertyEditor(TypeInfo(string), TMixedComponent, ’FileName’, TMixedFileNameProperty);
      RegisterComponents(’Samples’, [TMixedComponent]);
    end;function  TMixedFileNameProperty.AllEqual: boolean;
    var
      FirstVal: string;
      i: Integer;
    begin
      FirstVal := GetStrValue;
      Result := True;
      i := 1;
      while  Result and  (i < PropCount) do
      begin
        Result := Result and  (GetStrValueAt(i) = FirstVal);
        Inc(i);
      end;
    end;procedure  TMixedFileNameProperty.Edit;
    var
      Dlg: TOpenDialog;
    begin
      Dlg := TOpenDialog.Create(Application);
      try
        with  Dlg do
        begin
          Title := ’File for ’ + TComponent(GetComponent(0)).Name;
          FileName:= Value;
          if  Execute then  Value := FileName;
        end;
      finally
        FreeAndNil(Dlg);
      end
    end;function  TMixedFileNameProperty.GetAttributes: TPropertyAttributes;
    begin
      Result := [paDialog]
    end;function  TMixedFileNameProperty.GetValue: string;
    begin
      Result := GetStrValue;
    end;procedure  TMixedFileNameProperty.SetValue(const  Value: string);
    begin
      SetStrValue(Value);
    end;end.  把设计时代码从运行时代码中分离出去的最简单方法是,把所有需要DesignIntf 和DesignEditors 代码放入它们自己的单元中去,那个单元将要添加使用组件单元的声明。组件自己不需要知道那个自己运作的IDE编辑器。首先,把DesignIntf 和 DesignEditors 单元从组件单元的Uses部分删除掉,然后让编译/链接器告诉你哪些类需要移到它们自己的单元中去:[Error] MixedComponent.pas(23): Undeclared identifier: ’TPropertyEditor’ 
    [Error] MixedComponent.pas(23): Class type required 
    [Error] MixedComponent.pas(24): Method ’AllEqual’ not found in base class 
    [Error] MixedComponent.pas(25): Method ’Edit’ not found in base class 
    [Error] MixedComponent.pas(26): Undeclared identifier: ’TPropertyAttributes’ 
    [Error] MixedComponent.pas(27): Method ’GetValue’ not found in base class 
    [Error] MixedComponent.pas(28): Method ’SetValue’ not found in base class 
    [Error] MixedComponent.pas(35): Undeclared identifier: ’RegisterPropertyEditor’ 
    [Error] MixedComponent.pas(35): Undeclared identifier: ’TMixedFile’ 
    [Error] MixedComponent.pas(46): Undeclared identifier: ’GetStrValue’ 
    [Error] MixedComponent.pas(49): Undeclared identifier: ’PropCount’ 
    [Error] MixedComponent.pas(51): Undeclared identifier: ’GetStrValueAt’ 
    [Error] MixedComponent.pas(51): Operator not applicable to this operand type 
    [Error] MixedComponent.pas(64): Undeclared identifier: ’GetComponent’ 
    [Error] MixedComponent.pas(65): Undeclared identifier: ’Value’ 
    [Error] MixedComponent.pas(75): Undeclared identifier: ’paDialog’ 
    [Error] MixedComponent.pas(80): Undeclared identifier: ’GetStrValue’ 
    [Error] MixedComponent.pas(85): Undeclared identifier: ’SetStrValue’ 
    [Fatal Error] JOComponents.dpk(33): Could not compile used unit ’MixedComponent.pas’   下一步是创建一个新的单元存放这些代码。可以命名为类似MixedComponentReg的名子。把Register函数也移到那个单元中去。下面我们可以从错误信息中得知哪些需要移走。第一个错误信息是[Error] MixedComponent.pas(23): Undeclared identifier: ’TPropertyEditor’,这个信息指出了一个继承自那个设计时单元的类。这是个很清楚的指示,它指明了它是设计时代码和这个类要被移到一个新创建的单元。  到此,运行时包将会被成功编译(如果还不行,继续把设计时代码从单元中移去,直到没有错误产生)。现在组件在你的应用程序运行时已不再需要Proxies.pas和其它设计时单元了。这个运行时组件非常简单,如下:unit MixedComponent;interfaceuses
      Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;type
      { TMixedComponent }
      TMixedComponent = class(TComponent)
      private
        FFileName: String;
      published
        property FileName : String read FFileName write FFileName;
        { Published declarations }
      end;implementationend.  这最后一步就是把你的组件和它的属性编辑器编译到一个设计时包中,然后安装到IDE中去。  通过File | New | Other 选择 package创建一个新的包。调出包选项,选择design-time only,给Description 项填一个描述文字。选择Requires folder,点击Add按钮。在Requires Package 编辑对话框填写Designide.dcp,点击OK。同样,为你的组件运行时包添加dcp。在这种情况下,它放在了JOComponents.dpk,因此JOComponents.dcp被添加到requires项。在requires里有:JOComponents, designide 和 rtl。最后,选择包含目录,添加MixedComponentReg.pas 到包里。  到现在我们已经基本完成了!打开MixedComponentReg.pas 添加一对单元到uses部分,这要看你的组件或属性编辑器是否要在声明中使用这个组件(一些复杂的编辑器有时需要知道这个组件的存在)。如果是这样,把它加到Interface的uses部分。否则,加到implementation的uses部分。DesignIntf和DesignEditors就放到Interface的uses里。SysUtils, Forms, Dialogs, 和 Classes 在内部的属性编辑器的不同地方使用,因此就放到implementation部分。最后的MixedComponentReg应该象下面这样:unit MixedComponentReg;interfaceuses DesignIntf, DesignEditors;type
      { TMixedFileNameProperty }
      TMixedFileNameProperty = class(TPropertyEditor)
        function  AllEqual: boolean; override;
        procedure Edit; override;
        function  GetAttributes: TPropertyAttributes; override;
        function  GetValue: string; override;
        procedure SetValue (const Value: string); override;
      end;procedure Register;implementationuses MixedComponent, SysUtils, Form
      

  14.   

    非常感谢  wanggongqin 和 alphax
      

  15.   

    unit CustomRule;interfaceuses
      Windows, SysUtils, Classes, Controls, Types, Graphics, Messages, Dialogs,
      TypFuncs, mmSystem, RiffFile;type
      TCustomDrawEvent = procedure(Sender: TObject; RuleCanvas: TCanvas) of object;
      TMessageEvent = procedure(Sender: TObject; var msg: TMessage) of object;  TCustomRule = class(TCustomControl)
         .
         .
         .
        end;
      
      TWaveFile = string[255];
      TWaveRule = class(TCustomRule)
      private
        FFileName:   TWaveFile;
        .
        .
        .
      end;implementation
    ....
    end.unit TypVCLRegist;interfaceuses
    CustomRule, DesignEditors, DesignIntf, Dialogs;type
      TWaveFileProperty = class(TStringProperty)
      protected
        procedure GetDialogOptions(Dialog: TOpenDialog); virtual;
      public
        procedure Edit; override;
        function GetAttributes: TPropertyAttributes; override;
      end;procedure Register;implementation
    uses
    SysUtils, Forms, Classes;procedure Register;
    begin
      RegisterComponents('TYPVCL', [TCustomRule]);
      RegisterComponents('TYPVCL', [TWaveRule]);  RegisterPropertyEditor(TypeInfo(string), TWaveRule, 'FileName',
      TWaveFileProperty);
    end;
    { TWaveFileProperty }procedure TWaveFileProperty.GetDialogOptions(Dialog: TOpenDialog);
    begin
      Dialog.Filter := '音频文件(*.wav)|*.wav';
      Dialog.Options := Dialog.Options + [ofFileMustExist];
    end;procedure TWaveFileProperty.Edit;
    var
      OpenDialog: TOpenDialog;
    begin
      OpenDialog := TOpenDialog.Create(nil);
      GetDialogOptions(OpenDialog);
      if OpenDialog.Execute then
        SetValue(OpenDialog.FileName);
      OpenDialog.Free;
    end;function TWaveFileProperty.GetAttributes: TPropertyAttributes;
    begin
      Result := [paRevertable, paDialog, paMultiSelect];
    end;end.做完以后好象并没有把 TWaveFileProperty 与 FileName 关联上呀!
    这是怎么回事?(还是一个string编辑器)
      

  16.   

    我还有个疑问~从 wanggongqin(功勤) 的例子看,在注册的时候
    RegisterPropertyEditor(TypeInfo(string), TMixedComponent, ’FileName’, 
    TMixedFileNameProperty); 这里很明显,是把 TMixedFileNameProperty 与 TMixedComponent 组件的 FileName 属性关联起来(绑定)。但是请注意下面一种情形:任意创建一个控件,只要其中某个属性的类型是 TFileName, 那么编辑期间 Delphi 会自动打开OpenDialog对它进行编辑。由此可见,delphi是将 TFileNameProperty 属性与 TFileName 类型绑定,而不是只与某个控件的某个属性绑定(例子中的)。 请问为何会有此差别? 如何实现属性编辑器与类型的绑定呢?
      

  17.   

    你的FileName类型是TWaveFile,而不是String类型,所以注册时应该
    RegisterPropertyEditor(TypeInfo(TWaveFile), TWaveRule, 'FileName',TWaveFileProperty);调用RegisterPropertyEditor过程来注册属性编辑器。该过程接受四个参数:  ● 要编辑的属性的类型信息的指针。这总是通过调用调用TypeInfo函数得到,如TypeInfo ( TMyComponent )  ● 编辑器应用的部件类型,如果该参数为nil则编辑器应用于所给的类型的所有属性  ● 属性名,该参数只有在前一参数描述了部件的情况下才可用  ● 使用该属性编辑器的属性的类型  下面引用了注册标准部件的过程:procedure Register;beginRegisterPropertyEditor (TypeInfo(TComponent), nil, TComponentProperty,RegisterPropertyEditor(TypeInfo(TComponentName), TComponent,'Name', (ComponentNamePropety);RegisterPropertyEditor (TypeInfo(TMenuItem), TMenu, '', TMenuItemProperty);end;  这三句表达式使用RegisterPropertyEditor三种不同的用法:  ● 第一种最典型它注册了用于所有TComponent类型属性的属性编辑器TComponentProperty。通常,当为某种类型属性注册属性编辑器时,它就能应用于所有这种类型的属性,因此,第二和第三个参数为nil。  ● 第二个表达式注册特定类型的属性编辑器它为特定部件的特定属性注册属性编辑器,在这种情况下,编辑器用于所有部件的Name属性。  ● 第三个表达式介于第一个和第二个表达式之间它为部件TMenu的TMenuItem类型的所有属性注册了属性编辑器。
      

  18.   

    wanggongqin(功勤) 兄,太感谢了!