Delphi深度探索之自动完成外壳扩展

引用自:http://develop.csai.cn/delphi/NO000001.htm

 

自动完成简介

  大家一定都非常熟悉IE浏览器的地址输入编辑框,它提供了自动完成的功能。自动完成(Auto Complete)功能简化了编辑框的输入功能,它可以根据已经输入的部分字符串进行预测和匹配。图2.7的例子,演示了自动完成可以依据输入的“pro”,给出Pascal的保留字中与其相匹配的字符串列表"procedure"、"program"、"property"。

  在利用自动完成功能前,必须要知道自动完成并不是任意版本的Windows都支持,自动完成功能实际上是由Internet Explorer 4.0引入并必须有4.71版及以上的Shell32.dll的支持。很多早期的系统,包括很多安装了IE 4的系统并不支持这项特性,所以使用自动完成功能时必须确保要安装的系统支持这项功能,如果不支持就需要安装IE和集成外壳。

  自动完成功能是通过一组COM接口来实现的。这些接口使我们可以连接自动完成到编辑框,添加编辑框可以枚举的字符串列表并配置其显示属性。

  1. 自动完成的用途

  使用自动完成功能可以增强程序的易用性,它可以节省用户反复输入一些常用字符串的时间,并减少输入错误。它还可以用于部分字符匹配的查找系统,可以用它实现对数据库内容的快速查找。另外,它还支持向浏览器历史、最近运行程序和外壳目录输入等功能中添加自定义的完成列表。

  2. 配置自动完成

  需要知道的是在Delphi里并没有提供自动完成功能相关COM接口的pascal声明单元。由于一般这类接口在微软都是以C语言的头文件形式提供接口,Delphi不可能及时提供最新的相应的Pascal包装单元。所以为了使用这项功能,本文中额外提供了一个shlintf单元翻译了相应的头文件的声明。下表总结了Shlintf的内容:

COM对象 接 口
CLSID_AutoComplete IAutoComplete
CLSID_ACLHistory IAutoComplete2
CLSID_ACListISF IACList
CLSID_ACLMRU IACList2
CLSID_ACLMulti ICurrentWorkingDirectory
  IObjMgr

  自动完成功能最基本的一个接口就是IAutoComplete接口,它的声明如下:

type
IAutoComplete = interface(IUnknown)
[SID_IAutoComplete]
function Init(hwndEdit: HWND; punkACL: IUnknown; pwszRegKeyPath: PWideChar;
pwszQuickComplete: PWideChar): HResult; stdcall;
function Enable(fEnable: Boolean): HResult; stdcall;
end;

  IautoComplete接口有两个方法:Init和Enable。

  Init方法用来把IAutoComple接口以及自动完成列表同编辑框相连接。参数pwszRegKeyPath和pwszQuickComplete使得IAutoComplete接口可以按预定义的格式扩展部分输入的字符串,快捷键为CTRL+ENTER。比如设定pwszQuickComplete为”http://www.%s.cn/”后,当用户输入"csai" 到编辑框,并按下快捷键CTRL+ENTER,编辑框中的文本就会更新为http://www.csai.cn/。

  Enable方法是根据fEnable标识来切换自动完成功能的开关状态的。缺省时是激活的。

  下图显示了IAutoComplete接口是如何自动完成字符串的,当用户在编辑框中输入字符时,一种可能是最能匹配已经输入字符串的候选字符会被反白显示。

  回过头再来看看Init方法的punkACL参数,它是一个必须的参数而且必须指向一个提供字符串的接口。注意不是我们熟悉的Delphi中提供的TStrings对象,而是一个IEnumString字符串接口。IEnumString接口负责生成一个用于自动完成的候选字符串列表。除了IEnumString接口外,punkACL参数还可以是IACList和IACList2等其他接口。

  IACList接口简单地说就是可以对候选字符串分类来改善自动完成的效率,本文中将不加介绍。

  要注意的是Microsoft并没有停止对自动完成功能的改进,在Internet Explorer 5.0中,Microsoft 还提供了IAutoComplete2接口。IAutoComplete2接口进行了进一步的扩展,它使得我们可以设定一系列的选项来设定自动完成的可视化表达以及操作。下面是 IAutoComplete2接口的声明:

type
IAutoComplete2 = interface(IAutoComplete)
[SID_IAutoComplete2]
function SetOptions(dwFlag: DWORD): HResult; stdcall;
function GetOptions(out dwFlag: DWORD):HResult; stdcall;
end;

  IAutoComplete2接口引入了两种新的方法,这两种方法使用同样的标识组合作为参数,下表列出了可用的标识及其意义。

标 识 说 明
ACO_NONE 不使用自动完成
ACO_AUTOSUGGEST 使用下拉列表
ACO_AUTOAPPEND 允许自动附加在init方法中pwszQuickComplete参数所代表的字符串
ACO_SEARCH 在下拉列表最后添加"查找"文本
ACO_FILTERPREFIXES 防止自动完成匹配常用前缀比如"www.", "http://"等
ACO_USETAB TAB键可以用来选择下拉列表项
ACF_UPDOWNKEYDROPSLIST 上下按键可以用来调出下拉列表框
ACO_RTLREADING 按由右到左的顺序读

  就像下图所显示的那样,IAutoComplete2同IAutoComplete的显示方式有一点不同,就是因为IAutoComplete2支持不同的显示模式,这些模式可以通过SetOptions 方法来设定。

  3. 实现自动完成

  自动完成功能是通过COM对象来实现的,下面代码用As操作符来获得IAutoComplete2接口:

FAutoComplete :=
CreateComObject(CLSID_AutoComplete) as IAutoComplete2;

  获得接口后我们就可以实现自动完成功能了,下面代码是一个简单的例子:

unit Main;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics,
Controls, Forms, Dialogs, ShlIntf, ActiveX,
ComObj, StdCtrls, StrTools;
type
TAutoCompleteForm. = class(TForm)
CompletionEdit: TEdit;
CompletionLabel: TLabel;
SourceGroupBox: TGroupBox;
SourceMemo: TMemo;
procedure FormCreate(Sender: TObject);
private
FAutoComplete: IAutoComplete2;
FStrings: IUnknown;
end;
var
AutoCompleteForm. TAutoCompleteForm;
implementation
{$R *.DFM}
{ TAutoCompleteForm. }
procedure TAutoCompleteForm.FormCreate(Sender: TObject);
begin
FAutoComplete := CreateComObject(CLSID_AutoComplete) as IAutoComplete2;
FStrings := TEnumString.Create(SourceMemo.Lines) as IUnknown;

OleCheck(FAutoComplete.SetOptions(ACO_AUTOSUGGESTor ACO_UPDOWNKEYDROPSLIST));

OleCheck(FAutoComplete.Init(CompletionEdit.Handle, FStrings, nil, nil));
end;
end.

  上面的例程首先获得IAutoComplete2接口,如果系统不支持IAutoComplete2接口,as操作符将引发EinvalidCastError异常。接下来的一行代码会创建一个TEnumString类的实例并取得IUnknown接口,这个接口将作为FAutoComplete的Init方法的punkACL参数,TEnumString类是从TInterfacedObject类继承的,实现在StrTools单元中,实现它的目的是使TStrings类同IEnumString接口兼容。然后,设定显示选项使自动完成支持下拉列表和自动建议。最后初始化FAutoComplete并同编辑框相连接,被连接的编辑框就支持自动完成了。

  4.标准自动完成列表

  除了支持标准的编辑框的自动完成,Microsoft还允许通过下表中的一组COM对象来存取标准自动完成列表。

COM对象 说 明
CLSID_History 允许存取历史列表项
CLSID_ACListISF 允许操作外壳命名空间的内容
CLSID_MRU 提供最近运行过的程序列表


   上面列出的COM对象提供了一个IUnknown接口,接口可以作为IAutoComplete接口的Init方法的punkACL参数。每个IUnknown接口都支持IEnumString和IACList接口,同时还支持其他特殊接口。比如,外壳空间的IUnknown接口还公开了ICurrentWorkingDirectory和IPersistFolder接口。使用这些对象可以根据外壳空间的变化动态地改变候选字符串列表。

  ICurrentWorkingDirectory和IPersistFolder接口的区别在于IPersistFolder使用item lists 而不是字符串。Item lists 便于存取虚拟文件夹比如控制面板。

  下面的代码演示了如何使用自动完成功能来设定C:/下的外壳内容为候选字符串列表:

var
WorkingDirectory: ICurrentWorkingDirectory;
begin
FAutoComplete := CreateComObject(CLSID_AutoComplete) as IAutoComplete2;
FStrings := CreateComObject(CLSID_ACListISF);
WorkingDirectory := FStrings as ICurrentWorkingDirectory;
OleCheck(WorkingDirectory.SetDirectory('C:/'));

OleCheck(FAutoComplete.SetOptions(ACO_AUTOSUGGEST or ACO_UPDOWNKEYDROPSLIST));

OleCheck(FAutoComplete.Init(CompletionEdit.Handle, FStrings, nil, nil));
end;

  5. 改进自动完成

  虽然自动完成功能实现起来很简单,但为了更好在Delphi中使VCL框架一致,应该创建对它的VCL封装。另外的改进应该是要提供对其他嵌入编辑框的控件的自动完成功能的实现。如下图,TAutoComplete组件被连接到了一个TtreeView控件上。

  总的来说,自动完成功能是一项非常棒的功能,它可以简化用户同经常输入的文本交互的过程。用好自动完成功能,会使得你的程序显得很专业。

 

你可能感兴趣的:(Delphi深度探索之自动完成外壳扩展)