最近维护一个老的delphi项目,想自定义一个控件,根据用户输入动态过滤数据。因为能力有限,而且近十年没有用过Delphi了,感觉有些力不从心。在这里求助一下,目前控件可以过滤,但是只能输入一个字符。问题应该是输入字符弹出列表后Edit失去了焦点,请各位大侠帮忙解决一下。不胜感谢!TFilterEdit扩展了TButtonEdit,,TButtonEdit扩展了TCustomEdit只是集成了一个Button没有其它扩展,所以这里可以用TCustomEdit替换TButtonEdit。因为要维护的项目是基于对象的,所以定义了一个TBaseItem类, 控件中有一个TBaseItem列表,根据用户输入从列表中过滤数据,显示在下拉窗体中的ListBox里,然后可以通过光标键选择。代码借鉴一些从网上下载的例子。
unit FilterEdit;interfaceuses Classes, Controls, Messages, Windows, Forms, StdCtrls, ButtonEdit;type
  TBaseItem = class
  private
    FInCode: string;
    FDispCode: string;
    FDispName: string;
    FHintCode: string;
  public
    function MatchItem(const value: string): boolean;
    property InCode: string read FInCode write FInCode;
    property DispCode: string read FDispCode write FDispCode;
    property DispName: string read FDispName write FDispName;
    property HintCode: string read FHintCode write FHintCode;
  end;  TDropWindow = class;  TFilterEdit = class(TButtonEdit)
  private
    FDropDown: boolean;
    FDropWindow: TDropWindow;
    FWindowHeight: integer;
    FWindowWidth: integer;
    FItemList: TStrings;
    FFilterItemList: TStrings;
    FSelectedItem: TBaseItem;    procedure SetWindowWidth(const value: integer);
    procedure SetWindowHeight(const value: integer);
    procedure SetWindowPosition(var point : TPoint);
    procedure SetSelectedItem(value: TBaseItem);
    procedure SetItemList(const value: TStrings);
    function GetItemCount: integer;
  protected
//    procedure Change; override;
    procedure DropDown; virtual;
    procedure CloseUp; virtual;
    procedure WMKillFocus(var Msg: TWMKillFocus); message WM_KILLFOCUS;
    procedure WMChar(var Msg: TWMChar); message WM_CHAR;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X: Integer; Y: Integer); override;
    //
    procedure RefreshFilterList(filter: string);
 public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    property SelectedItem: TBaseItem read FSelectedItem write SetSelectedItem;
    property ItemCount: integer read GetItemCount;
    property Items: TStrings read FItemList write SetItemList;
  published
    property DropWindowWidth: integer read FWindowWidth write SetWindowWidth;
    property DropWindowHeight: integer read FWindowHeight write SetWindowHeight;
  end;  TDropWindow = class(TCustomForm)
  private
    FListBox: TListBox;
    procedure WMMouseActivate(var Msg: TMessage); message WM_MOUSEACTIVATE;
    procedure WMActivate(var Msg: TWMActivate); message WM_ACTIVATE;
    function GetForm: TWinControl;
    procedure ListBoxKeyPress(Sender: TObject; var Key: Char);
    procedure SelectData(Sender: TObject);
    procedure RefreshList;
    function GetEdit: TFilterEdit;
  protected
    procedure CreateParams(var Params: TCreateParams); override;
    procedure DoShow; override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    property ListBox: TListBox read FListBox write FListBox;
    property Edit: TFilterEdit read GetEdit;
  end;procedure Register;implementationprocedure Register;
begin
  RegisterComponents('DYF', [TFilterEdit]);
//  RegisterPropertyEditor(TypeInfo(string), TButtonEdit, 'SelectField', TSelectFieldEditer);
end;{ TBaseItem }function TBaseItem.MatchItem(const value: string): boolean;
begin
  Result := (Pos(value, self.FDispCode) > 0) or (Pos(value, self.FHintCode) > 0)
        or (Pos(value, self.FDispName) > 0);
end;{ TFilterEdit }constructor TFilterEdit.Create(AOwner: TComponent);
begin
  inherited;  self.FItemList := TStringList.Create;
  self.FFilterItemList := TStringList.Create;  self.FDropWindow   := TDropWindow.Create(self);
  self.FWindowWidth  := 200;
  self.FWindowHeight := 80;
  self.FDropWindow.Width := 200;
  self.FDropWindow.Height := 80;
  self.FDropDown := false;
end;destructor TFilterEdit.Destroy;
begin
  self.FDropWindow.Free;
  self.FFilterItemList.Clear;
  self.FFilterItemList.Free;
  self.FItemList.Clear;
  self.FItemList.Free;  inherited;
end;procedure TFilterEdit.DropDown;
var
  p: TPoint;
begin
  self.FDropWindow.Color := Color;
  self.FDropWindow.Font.Assign(Font);
  p := Parent.ClientToScreen(Point(self.Left, self.Top + self.Height));
  SetWindowPosition(p);  self.FDropWindow.Top := p.Y;
  self.FDropWindow.Left := p.X;
  self.FDropWindow.Show;
  Invalidate;
//  Windows.SetFocus(self.FDropWindow.Handle);
  self.FDropDown := true;
//  self.SetFocus;
end;procedure TFilterEdit.CloseUp;
begin
  self.FDropWindow.Hide;
  self.FDropDown := false;
end;procedure TFilterEdit.MouseDown(Button: TMouseButton; Shift: TShiftState;
  X, Y: Integer);
begin
  Windows.SetFocus(Handle);
  if self.FDropDown then
    CloseUp;  inherited;
end;procedure TFilterEdit.SetSelectedItem(value: TBaseItem);
begin
  self.FSelectedItem := value;
  if Assigned(value) then
    self.Text := value.DispCode + value.DispName
  else
    self.Text := '';
end;procedure TFilterEdit.SetWindowHeight(const value: integer);
begin
  if self.FWindowHeight <> value then
  begin
    self.FWindowHeight := value;
    self.FDropWindow.Height := value;
  end;
end;procedure TFilterEdit.SetWindowPosition(var point: TPoint);
var
  _Left : integer;
  _Top  : integer;
begin
  _Left   := point.X;
  _Top    := point.Y;
  if (self.FWindowWidth + _Left) > Screen.Width then
    _Left := _Left - (self.FWindowWidth - self.Width);
  if self.FWindowHeight + _Top > Screen.Height then
    _Top  := _Top - self.FWindowHeight - self.Height;  point.X   := _Left;
  point.Y   := _Top;
end;procedure TFilterEdit.SetWindowWidth(const value: integer);
begin
  if self.FWindowWidth <> value then
  begin
    self.FWindowWidth := value;
    self.FDropWindow.Width := value;
  end;
end;procedure TFilterEdit.WMKillFocus(var Msg: TWMKillFocus);
begin
  if (Msg.FocusedWnd <> self.FDropWindow.Handle) and (self.FDropDown) then
  begin
    CloseUp;
  end;
end;procedure TFilterEdit.WMChar(var Msg: TWMChar);
begin
  inherited;  self.FSelectedItem := nil;
  self.RefreshFilterList(self.Text);
  if self.FDropDown then
    self.FDropWindow.RefreshList
  else if (self.FFilterItemList.Count > 0) then
    self.DropDown;
end;function TFilterEdit.GetItemCount: integer;
begin
  Result := 0;
  if Assigned(self.FItemList) then
    Result := self.FItemList.Count;
end;procedure TFilterEdit.SetItemList(const value: TStrings);
begin
  if Assigned(self.FItemList) then
    self.FItemList.Assign(value)
  else
    self.FItemList := value;
end;procedure TFilterEdit.RefreshFilterList(filter: string);
var
  i: integer;
  item: TBaseItem;
begin
  self.FFilterItemList.Clear;
  for i := 0 to self.FItemList.Count-1 do
  begin
    item := TBaseItem(self.FItemList.Objects[i]);
    if item.MatchItem(filter) then
      self.FFilterItemList.AddObject(item.InCode, item);
  end;
end;{ TDropWindow }constructor TDropWindow.Create(AOwner: TComponent);
begin
  inherited CreateNew(AOwner);  self.BorderStyle := bsNone;
  self.FListBox := TListBox.Create(self);
  with self.FListBox do
  begin
    Parent := Self;
    Align := alClient;
    BorderStyle := bsNone;
    Ctl3D := true;
    OnKeyPress  := ListBoxKeyPress;
 //   OnClick  := SelectData;
  end;
  
  self.ControlStyle := ControlStyle + [csNoDesignVisible, csReplicatable];
end;procedure TDropWindow.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);  with Params do
  begin
    Style := WS_POPUP or WS_BORDER;
    ExStyle := WS_EX_TOOLWINDOW;
    AddBiDiModeExStyle(ExStyle);
    WindowClass.Style := CS_SAVEBITS;
  end;
end;destructor TDropWindow.Destroy;
begin
  self.FListBox.Free;  inherited;
end;procedure TDropWindow.DoShow;
begin
  self.RefreshList;  inherited;
end;function TDropWindow.GetEdit: TFilterEdit;
begin
  Result := TFilterEdit(self.Owner);
end;function TDropWindow.GetForm: TWinControl;
begin
  if (Owner <> nil) and (Owner.Owner <> nil) and (Owner.Owner is TWinControl) then
    Result := Owner.Owner as TWinControl
  else
    Result := nil;
end;procedure TDropWindow.ListBoxKeyPress(Sender: TObject; var Key: Char);
begin
  if (key =#13) and (self.FListBox.Count > 0) and (self.FListBox.ItemIndex > -1) then
  begin
    self.Edit.SelectedItem := TBaseItem(self.FListBox.Items.Objects[self.FListBox.ItemIndex]);
    self.Edit.CloseUp;
  end;end;procedure TDropWindow.RefreshList;
var
  i: integer;
  item: TBaseItem;
begin
  self.FListBox.Items.BeginUpdate;
  self.FListBox.Items.Clear;
  for i := 0 to self.Edit.FFilterItemList.Count-1 do
  begin
    item := TBaseItem(self.Edit.FFilterItemList.Objects[i]);
    self.FListBox.Items.AddObject(item.DispCode + item.DispName, item);
  end;
  self.FListBox.Items.EndUpdate;
  if self.FListBox.Count > 0 then
    self.FListBox.ItemIndex := 0;
end;procedure TDropWindow.SelectData(Sender: TObject);
begin
  if (self.FListBox.Count > 0) and (self.FListBox.ItemIndex > -1) then
  begin
    self.Edit.SelectedItem := TBaseItem(self.FListBox.Items.Objects[self.FListBox.ItemIndex]);
    self.Edit.CloseUp;
  end;                
end;
         
procedure TDropWindow.WMActivate(var Msg: TWMActivate);
var
  OwnerForm : TWinControl;
  MDIOwner : TCustomForm;
begin
  inherited;  OwnerForm := GetForm;
  if (Msg.Active = WA_ACTIVE) and (OwnerForm <> nil) then
  begin
    SendMessage(OwnerForm.Handle, WM_NCACTIVATE, 1, 0);
    if (OwnerForm is TCustomForm) and (TForm(OwnerForm).FormStyle = fsMDIChild) then
    begin
      MDIOwner := Application.MainForm;
      if MDIOwner <> nil then
        SendMessage(MDIOwner.Handle, WM_NCACTIVATE, 1, 0);
    end;
  end;  if Msg.Active = WA_INACTIVE then
  begin
    self.Edit.CloseUp;
    Hide;
  end;
end;procedure TDropWindow.WMMouseActivate(var Msg: TMessage);
begin
  Msg.Result := MA_NOACTIVATE;
end;end.

解决方案 »

  1.   

    ListBox也要不能有焦点的,要处理ListBox的WM_Activate消息,以及不允许获取焦点。
      

  2.   


    ListBox如果没有焦点,怎么通过光标键上下选择项目。ComboBox中的ListBox好像是有焦点的吧
      

  3.   


    ListBox如果没有焦点,怎么通过光标键上下选择项目。ComboBox中的ListBox好像是有焦点的吧
    在Edit中拦截上下键进行处理就行了
      

  4.   


    ListBox如果没有焦点,怎么通过光标键上下选择项目。ComboBox中的ListBox好像是有焦点的吧
    在Edit中拦截上下键进行处理就行了哥们,有时间能不能帮我改一下。多谢