首页 > 代码库 > Autocomplete TEdit

Autocomplete TEdit

http://forum.codecall.net/topic/75946-autocomplete-tedit/ 

Overview 

Autocomplete feature really helpful for us speeding up our typing job.

For you who is not familiar with the term autocomplete,

it‘s when you type partial part of a word and then

you will be presented with a list of possible complete words.

You can just select the correct word from the list,

and that partial word will be automatically completed.

In programming, this feature very helpful to

"remember" class names, routine names, and variable name. 

Not only to speed up the typing, autocomplete also very helpful to avoid typo. 

In this tutorial I will show you a technique to implement autocomplete

in your Delphi program in order to provide your users the benefits of autocomplete.

I will implement autocomplete in a descendant of TEdit. I name it TAutocompleteEdit. 

TAutoCompleteEdit

Behaviors

  1. Upon typing some chars, TAutocompleteEdit will check the typed word agains a word list. When no match found, do nothing.
  2. When one or more matches found, TAutocompleteEdit show the matches in a TListBox.We will call this TListBoxWordList.
  3. User can move between TAutocompleteEdit and WordList using down and up arrow.
  4. User select a complete word from WordList by highlighting the word and press Enter key.
  5. After user selected a word, the word will replace whatever content in TAutocompleteEdit.
  6. If user press Escape in TAutocompleteEdit or in WordList, WordList must dissapear.
  7. If TAutocompleteEdit lost focus, and the new focus is not in WordList, WordList must dissapear.
  8. If WordList lost focus, and the new focus is not in TAutocompleteEdit, WordList must dissapear.
  9. If later user type in another character and no match found, WordList must dissapear. 

Key Methods 

From the above behaviors, we decided to have the following methods.

  1. ShowWordList(AWords: TStrings).
    This method is responsible to create WordList TListBox when needed,
    populate it with words contained in AWords, and
    also to patch its events so we can achieve behavior #3, #4, #5, #6, and #8.
  2. HideWordList.
    This method is responsible to hide and clean up WordList.
  3. Change.
    This is where to respond when the content of TAutocompleteEdit changed.
    So this is where we do the checking.
    This method actually already exist in TAutocompleteEdit‘s parent.
    So what we are going to do is override it, and introduce our behavior.
  4. DoExit.
    This method also already exist in TAutocompleteEdit‘s parent.
    We are going to override it and introduce new behavior, in order to achieve behavior #7.
  5. KeyDown(var Key: Word; Shift: TShiftState).
    This method also already exist in TAutocompleteEdit‘s parent.
    We are going to override it to achieve behavior #3 and #6. 

Key Methods Implementations 

1. ShowWordList(AWords: TStrings).

  procedure TAutocompleteEdit.ShowWordList(AWords: TStrings);begin  if FWordList=nil then  begin    FWordList := TListBox.Create(Self);    FWordList.ParentCtl3D := False;    FWordList.Ctl3D := False;    FWordList.Parent := Self.Parent;    FWordList.TabStop := False;    FWordList.OnExit := HandleWordListLostFocus;    FWordList.OnKeyPress := HandleWordListKeyPress;    FWordList.OnKeyDown := HandleWordListKeyDown;  end;   FWordList.Items.Assign(AWords);  if FWordListWidth < 1 then    FWordList.SetBounds(Self.Left, Self.Top + Self.Height, Self.Width, FWordListHeight)  else    FWordList.SetBounds(Self.Left, Self.Top + Self.Height, FWordListWidth, FWordListHeight);   FWordList.Show;end; 

 2. HideWordList

 procedure TAutocompleteEdit.HideWordList;begin  PostMessage(Self.Handle, MSG_HIDEWORDLIST, 0, 0);end;

 

 Note that in the above method we only post a custom message. The custom message handler in turn will call this private method. 

procedure TAutocompleteEdit.HandleHideWordList;begin  FWordList.Free;  FWordList := nil;end;

 

 3. Change.

procedure TAutocompleteEdit.Change;var  S: TStrings;begin  inherited;  if AutocompleteMan.IsRecognized(Self.Text) then  begin    S := TStringList.Create;    try      if AutocompleteMan.IsRecognized(Self.Text, S) then        ShowWordList(S);    finally      S.Free;    end;  end  else    HideWordList;end;

 

 4. DoExit.

procedure TAutocompleteEdit.DoExit;begin  if Assigned(FWordList) and FWordList.Visible and not FWordList.Focused then    HideWordList;  inherited;end;

 

5. KeyDown(var Key: Word; Shift: TShiftState). 

 procedure TAutocompleteEdit.KeyDown(var Key: Word; Shift: TShiftState);begin  if Key=VK_ESCAPE then    HideWordList  else if (Key=VK_DOWN) and Assigned(FWordList) and FWordList.Visible then  begin    FCaretPos := Self.SelStart;    FWordList.SetFocus;    if FWordList.ItemIndex < 0 then      FWordList.ItemIndex := 0;  end  else    inherited;end;

 

Here is the complete source code of TAutocompleteEdit: 

TEdit with Autocomplete.zip   1.84MB   603 downloads.

Feel free to use it or improve it for any kind of use. 

Cheers!

  1 unit AutocompleteEdit;  2   3 interface  4   5 uses  6   Windows  7   , Classes  8   , Vcl.StdCtrls  9   , SysUtils 10   , StrUtils 11   , Messages 12   ; 13  14 const 15   MSG_HIDEWORDLIST = WM_USER + 222; 16  17 type 18   TAutocompleteEdit=class(TEdit) 19   private 20     FWordList: TListBox; 21     FCaretPos: Integer; 22     FWordListHeight: Integer; 23     FWordListWidth: Integer; 24  25     procedure HandleWordListLostFocus(ASender: TObject); 26     procedure HandleWordListSelectItem(ASender: TObject); 27     procedure HandleWordListKeyPress(Sender: TObject; var Key: Char); 28     procedure HandleWordListKeyDown(ASender: TObject; var Key: Word; Shift: TShiftState); 29     procedure HandleHideWordList(var AMsg); overload; message MSG_HIDEWORDLIST; 30     procedure HandleHideWordList; overload; 31     procedure SetWordListHeight(const Value: Integer); 32     procedure SetWordListWidth(const Value: Integer); 33  34     procedure RegainFocus; 35   protected 36     procedure ShowWordList(AWords: TStrings); 37     procedure HideWordList; 38     procedure Change; override; 39     procedure KeyDown(var Key: Word; Shift: TShiftState); override; 40     procedure DoExit; override; 41   public 42     constructor Create(AOwner: TComponent); override; 43   published 44     property WordListHeight: Integer read FWordListHeight write SetWordListHeight; 45     property WordListWidth: Integer read FWordListWidth write SetWordListWidth; 46   end; 47  48   TAutocompleteMan=class 49   private 50     FWords: TStrings; 51   public 52     constructor Create; 53     destructor Destroy; override; 54  55     function IsRecognized(AWord: string): Boolean; overload; 56     function IsRecognized(AWord: string; AWordList: TStrings): Boolean; overload; 57  58     procedure LoadFromFile(const AFilename: string); 59     procedure AddWord(const AWord: string); 60  61     property Words: TStrings read FWords; 62   end; 63  64   procedure Register; 65  66 var 67   AutocompleteMan: TAutocompleteMan; 68  69  70 implementation 71  72 procedure Register; 73 begin 74   RegisterComponents(CodeCall, [TAutocompleteEdit]); 75 end; 76  77 { TAutocompleteMan } 78  79 procedure TAutocompleteMan.AddWord(const AWord: string); 80 begin 81   FWords.Add(UpperCase(AWord) + = + AWord); 82 end; 83  84 constructor TAutocompleteMan.Create; 85 begin 86   FWords := TStringList.Create; 87   TStringList(FWords).Duplicates := dupIgnore; 88 end; 89  90 destructor TAutocompleteMan.Destroy; 91 begin 92   FWords.Free; 93   inherited; 94 end; 95  96 function TAutocompleteMan.IsRecognized(AWord: string): Boolean; 97 var 98   i: Integer; 99 begin100   Result := False;101   AWord := UpperCase(AWord);102   for i := 0 to FWords.Count-1 do103   begin104     Result := System.Pos(AWord, FWords.Names[i]) > 0;105     if Result then106       Break;107   end;108 end;109 110 function TAutocompleteMan.IsRecognized(AWord: string;111   AWordList: TStrings): Boolean;112 var113   i: Integer;114 begin115   Result := False;116   AWord := UpperCase(AWord);117   AWordList.Clear;118   for i := 0 to FWords.Count-1 do119   begin120     if System.Pos(AWord, FWords.Names[i]) > 0 then121     begin122       Result := True;123       AWordList.Add(FWords.ValueFromIndex[i]);124     end;125   end;126 end;127 128 procedure TAutocompleteMan.LoadFromFile(const AFilename: string);129 var130   i: Integer;131   F: TStrings;132 begin133   F := TStringList.Create;134   try135     F.LoadFromFile(AFilename);136     for i := 0 to F.Count-1 do137       AddWord(F[i]);138   finally139     F.Free;140   end;141 end;142 143 { TAutocompleteEdit }144 145 procedure TAutocompleteEdit.Change;146 var147   S: TStrings;148 begin149   inherited;150   if AutocompleteMan.IsRecognized(Self.Text) then151   begin152     S := TStringList.Create;153     try154       if AutocompleteMan.IsRecognized(Self.Text, S) then155         ShowWordList(S);156     finally157       S.Free;158     end;159   end160   else161     HideWordList;162 end;163 164 procedure TAutocompleteEdit.HandleHideWordList(var AMsg);165 begin166   HandleHideWordList;167 end;168 169 constructor TAutocompleteEdit.Create(AOwner: TComponent);170 begin171   inherited;172   FWordListHeight := 60;173 end;174 175 procedure TAutocompleteEdit.DoExit;176 begin177   if Assigned(FWordList) and FWordList.Visible and not FWordList.Focused then178     HideWordList;179   inherited;180 end;181 182 procedure TAutocompleteEdit.HandleHideWordList;183 begin184   FWordList.Free;185   FWordList := nil;186 end;187 188 procedure TAutocompleteEdit.HandleWordListKeyDown(ASender: TObject;189   var Key: Word; Shift: TShiftState);190 begin191   if (Key=VK_UP) and (FWordList.ItemIndex=0) then192     RegainFocus;193 end;194 195 procedure TAutocompleteEdit.HandleWordListKeyPress(Sender: TObject;196   var Key: Char);197 begin198   case Key of199     #13: begin200            Key := #0;201            Self.Text := FWordList.Items[FWordList.ItemIndex];202            Self.SetFocus;203            Self.SelStart := Length(Self.Text);204            Self.SelLength := 0;205            HideWordList;206          end;207     #27: begin208            RegainFocus;209            HideWordList;210          end;211     else begin212       RegainFocus;213     end;214   end;215 end;216 217 procedure TAutocompleteEdit.HandleWordListLostFocus(ASender: TObject);218 begin219   if not Self.Focused then220     HideWordList;221 end;222 223 procedure TAutocompleteEdit.HandleWordListSelectItem(ASender: TObject);224 begin225   Self.Text := FWordList.Items[FWordList.ItemIndex];226   HideWordList;227 end;228 229 procedure TAutocompleteEdit.HideWordList;230 begin231   PostMessage(Self.Handle, MSG_HIDEWORDLIST, 0, 0);232 end;233 234 procedure TAutocompleteEdit.KeyDown(var Key: Word; Shift: TShiftState);235 begin236   if Key=VK_ESCAPE then237     HideWordList238   else if (Key=VK_DOWN) and Assigned(FWordList) and FWordList.Visible then239   begin240     FCaretPos := Self.SelStart;241     FWordList.SetFocus;242     if FWordList.ItemIndex < 0 then243       FWordList.ItemIndex := 0;244   end245   else246     inherited;247 end;248 249 procedure TAutocompleteEdit.RegainFocus;250 begin251   Self.SetFocus;252   Self.SelStart := FCaretPos;253   Self.SelLength := 0;254 end;255 256 procedure TAutocompleteEdit.SetWordListHeight(const Value: Integer);257 begin258   if FWordListHeight <> Value then259   begin260     FWordListHeight := Value;261     if Assigned(FWordList) then262       FWordList.Height := FWordListHeight;263   end;264 end;265 266 procedure TAutocompleteEdit.SetWordListWidth(const Value: Integer);267 begin268   if FWordListWidth <> Value then269   begin270     FWordListWidth := Value;271     if Assigned(FWordList) then272     begin273       if FWordListWidth < 1 then274         FWordList.Width := Self.Width275       else276         FWordList.Width := FWordListWidth;277     end;278   end;279 end;280 281 procedure TAutocompleteEdit.ShowWordList(AWords: TStrings);282 begin283   if FWordList=nil then284   begin285     FWordList := TListBox.Create(Self);286     FWordList.ParentCtl3D := False;287     FWordList.Ctl3D := False;288     FWordList.Parent := Self.Parent;289     FWordList.TabStop := False;290     FWordList.OnExit := HandleWordListLostFocus;291     FWordList.OnKeyPress := HandleWordListKeyPress;292     FWordList.OnKeyDown := HandleWordListKeyDown;293   end;294 295   FWordList.Items.Assign(AWords);296   if FWordListWidth < 1 then297     FWordList.SetBounds(Self.Left, Self.Top + Self.Height, Self.Width, FWordListHeight)298   else299     FWordList.SetBounds(Self.Left, Self.Top + Self.Height, FWordListWidth, FWordListHeight);300 301   FWordList.Show;302 end;303 304 initialization305   AutocompleteMan := TAutocompleteMan.Create;306 307 finalization308   AutocompleteMan.Free;309 end.

 

Autocomplete TEdit