delphi ComboBox在面板内的行为不同

e4eetjau  于 2022-11-04  发布在  其他
关注(0)|答案(1)|浏览(137)

使用StackOverflow中的一些答案,我在 Delphi 中创建了一个可搜索的TComboBox。当你直接将其添加到表单中时,它工作正常,但一旦将其添加到TPanel中,它就坏了,我似乎找不到原因。
直接在表单上:
x1c 0d1x键入t后:

在面板内:


键入t后:

下面是该组件的代码:

unit uSmartCombo;

interface

uses
  Vcl.StdCtrls, Classes, Winapi.Messages, Controls;

type
  TSmartComboBox = class(TComboBox)
  private
    FStoredItems: TStringList;
    procedure FilterItems;
    procedure CNCommand(var AMessage: TWMCommand); message CN_COMMAND;
    procedure RedefineCombo;
    procedure SetStoredItems(const Value: TStringList);
    procedure StoredItemsChange(Sender: TObject);
  protected
    procedure KeyPress(var Key: Char); override;
    procedure CloseUp; override;
    procedure Loaded; override;
    procedure DoExit; override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    property StoredItems: TStringList read FStoredItems write SetStoredItems;
  end;

procedure Register;

implementation

uses
  SysUtils, Winapi.Windows, Vcl.Forms;

procedure Register;
begin
   RegisterComponents('Standard', [TSmartComboBox]);
end;

constructor TSmartComboBox.Create(AOwner: TComponent);
begin
   inherited;
   FStoredItems := TStringList.Create;
   FStoredItems.OnChange := StoredItemsChange;
end;

destructor TSmartComboBox.Destroy;
begin
   FStoredItems.Free;
   inherited;
end;

procedure TSmartComboBox.DoExit;
begin
   inherited;
   RedefineCombo;
end;

procedure TSmartComboBox.Loaded;
var LParent: TWinControl;
    LPoint: TPoint;
begin
   inherited;
   if Items.Count > 0 then
      FStoredItems.Assign(Items);
   AutoComplete := False;
   Style := csDropDownList;

   // The ComboBox doesn't behave properly if the parent is not the form.
   // Workaround to pull it from any parenting
   //if not (Parent is TForm) then
   //begin
   //   LParent := Parent;
   //   while (not (LParent is TForm)) and Assigned(LParent) do
   //      LParent := LParent.Parent;
   //   LPoint := ClientToParent(Point(0,0), LParent);
   //   Parent := LParent;
   //   Left   := LPoint.X;
   //   Top    := LPoint.Y;
   //   BringToFront;
   //end;
end;

procedure TSmartComboBox.RedefineCombo;
var S: String;
begin
   if Style = csDropDown then
   begin
      if ItemIndex <> -1 then
         S := Items[ItemIndex];

      Style := csDropDownList;
      Items.Assign(FStoredItems);

      if S <> '' then
         ItemIndex := Items.IndexOf(S);
   end;
end;

procedure TSmartComboBox.SetStoredItems(const Value: TStringList);
begin
   if Assigned(FStoredItems) then
      FStoredItems.Assign(Value)
   else
      FStoredItems := Value;
end;

procedure TSmartComboBox.StoredItemsChange(Sender: TObject);
begin
   if Assigned(FStoredItems) then
   begin
      RedefineCombo;
      Items.Assign(FStoredItems);
   end;
end;

procedure TSmartComboBox.KeyPress(var Key: Char);
begin
   if CharInSet(Key, ['a'..'z']) and not (Style = csDropDown) then
   begin
      DroppedDown := False;
      Style := csDropDown;
   end;
   inherited;
   if not (Ord(Key) in [13,27]) then
      DroppedDown := True;
end;

procedure TSmartComboBox.CloseUp;
begin
   if Style = csDropDown then
      RedefineCombo;
   inherited;
end;

procedure TSmartComboBox.CNCommand(var AMessage: TWMCommand);
begin
   inherited;
   if (AMessage.Ctl = Handle) and (AMessage.NotifyCode = CBN_EDITUPDATE) then
      FilterItems;
end;

procedure TSmartComboBox.FilterItems;
var I: Integer;
    Selection: TSelection;
begin
   SendMessage(Handle, CB_GETEDITSEL, WPARAM(@Selection.StartPos), LPARAM(@Selection.EndPos));

   Items.BeginUpdate;
   Try
      if Text <> '' then
      begin
         Items.Clear;
         for I := 0 to FStoredItems.Count - 1 do
            if (Pos(Uppercase(Text), Uppercase(FStoredItems[I])) > 0) then
               Items.Add(FStoredItems[I]);
      end
      else
         Items.Assign(FStoredItems);
   Finally
      Items.EndUpdate;
   End;

   SendMessage(Handle, CB_SETEDITSEL, 0, MakeLParam(Selection.StartPos, Selection.EndPos));
end;

end.

任何帮助,在我如何能够继续找出为什么会发生这种情况,将不胜感激!

编辑1:

在做了一些额外的调试之后,我注意到发送到ComboBox的消息与面板中的消息不同。CBN_EDITUPDATE从未被发送,就像注解中提到的@Sherlock70,这使得FilterItems过程从未被触发。
我还注意到,在面板中使用ComboBox后,窗体的行为很奇怪,有时会冻结,甚至没有响应,就像它陷入了循环。
这种不可预测的行为使我放弃了这种方法,我可能会采取另一种方法来创建“可搜索的ComboBox”。
如果有人想弄清楚这个问题,甚至可能使用这个组件,就把这个问题留着。

ulydmbyx

ulydmbyx1#

我希望这能帮助一些人在未来甚至在7个月的问题。设置组合框的样式将销毁该组合框的窗口句柄并创建一个新的。这意味着窗口将释放您的控件的窗口句柄并创建一个新的。
您在搜索时设置了Combobx样式,这是错误的。请尝试从代码中删除Style :=并再次测试,对于窗体上的Combobox和面板或其他TWinControl上的Combobox,将得到相同的结果。
如您在下列程式码中所见,设定Style将会呼叫RecreateWnd。

procedure TCustomComboBox.SetStyle(Value: TComboBoxStyle);
begin
  if FStyle <> Value then
  begin
    FStyle := Value;
    if Value = csSimple then
      ControlStyle := ControlStyle - [csFixedHeight] else
      ControlStyle := ControlStyle + [csFixedHeight];
    RecreateWnd;
  end;
end;

RecreateWnd将调用DestroyHandle()

procedure TWinControl.CMRecreateWnd(var Message: TMessage);
var
  WasFocused: Boolean;
begin
  WasFocused := Focused;
  DestroyHandle;
  UpdateControlState;
  if WasFocused and (FHandle <> 0) then Windows.SetFocus(FHandle);
end;

然后,DestroyHandle将调用DestroyWnd(),后者将调用DestroyWindowHandle()。

相关问题