自定义过滤输入控件,高手们请提供帮助!

dyf2001 2017-10-27 04:56:08
最近维护一个老的delphi项目,想自定义一个控件,根据用户输入动态过滤数据。因为能力有限,而且近十年没有用过Delphi了,感觉有些力不从心。在这里求助一下,目前控件可以过滤,但是只能输入一个字符。问题应该是输入字符弹出列表后Edit失去了焦点,请各位大侠帮忙解决一下。不胜感谢!

TFilterEdit扩展了TButtonEdit,,TButtonEdit扩展了TCustomEdit只是集成了一个Button没有其它扩展,所以这里可以用TCustomEdit替换TButtonEdit。因为要维护的项目是基于对象的,所以定义了一个TBaseItem类, 控件中有一个TBaseItem列表,根据用户输入从列表中过滤数据,显示在下拉窗体中的ListBox里,然后可以通过光标键选择。

代码借鉴一些从网上下载的例子。


unit FilterEdit;

interface

uses 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;

implementation

procedure 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.
...全文
250 5 打赏 收藏 转发到动态 举报
写回复
用AI写文章
5 条回复
切换为时间正序
请发表友善的回复…
发表回复
dyf2001 2017-11-09
  • 打赏
  • 举报
回复
引用 4 楼 suiyunonghen 的回复:
[quote=引用 2 楼 dyf2001 的回复:]
引用
ListBox也要不能有焦点的,要处理ListBox的WM_Activate消息,以及不允许获取焦点。
ListBox如果没有焦点,怎么通过光标键上下选择项目。ComboBox中的ListBox好像是有焦点的吧
在Edit中拦截上下键进行处理就行了[/quote] 哥们,有时间能不能帮我改一下。多谢
不得闲 2017-11-01
  • 打赏
  • 举报
回复
引用 2 楼 dyf2001 的回复:
引用
ListBox也要不能有焦点的,要处理ListBox的WM_Activate消息,以及不允许获取焦点。
ListBox如果没有焦点,怎么通过光标键上下选择项目。ComboBox中的ListBox好像是有焦点的吧
在Edit中拦截上下键进行处理就行了
dyf2001 2017-11-01
  • 打赏
  • 举报
回复
高手们快来啊
dyf2001 2017-10-30
  • 打赏
  • 举报
回复
引用
ListBox也要不能有焦点的,要处理ListBox的WM_Activate消息,以及不允许获取焦点。
ListBox如果没有焦点,怎么通过光标键上下选择项目。ComboBox中的ListBox好像是有焦点的吧
不得闲 2017-10-30
  • 打赏
  • 举报
回复
ListBox也要不能有焦点的,要处理ListBox的WM_Activate消息,以及不允许获取焦点。

5,518

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 开发及应用
社区管理员
  • VCL组件开发及应用社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧