2012-02-06 8 views
5

Zwykły styl Windows ComboBox (csDropDown lub csDropDownList) otworzy jego listę rozwijaną tuż poniżej lub, jeśli nie ma wolnego miejsca poniżej, nad kombinacją. Czy mogę kontrolować pozycję tej listy (przynajmniej przez współrzędną Y)?Czy mogę programowo ustawić pozycję listy rozwijanej ComboBox?

+6

Zastanawiasz się: dlaczego? Co w domyślnym zachowaniu nie jest do twoich upodobań? –

+0

@MarjanVenema Nasz projektant chce wprowadzić pewne ulepszenia użyteczności dla właściciela-remisu combobox – Andrew

Odpowiedz

10

zamieszczaniu przykład kodu, który pokaże rozwijaną listę animację poprawnie i zmusi pokazujący listy rozwijanej powyżej ComboBox1. Ten kod podklas ComboBox hwndList:

TForm1 = class(TForm) 
    ComboBox1: TComboBox; 
    procedure FormCreate(Sender: TObject); 
    procedure FormDestroy(Sender: TObject); 
private 
    FComboBoxListDropDown: Boolean; 
    FComboBoxListWnd: HWND; 
    FOldComboBoxListWndProc, FNewComboBoxListWndProc: Pointer; 
    procedure ComboBoxListWndProc(var Message: TMessage); 
end; 

.... 

procedure TForm1.FormCreate(Sender: TObject); 
var 
    Info: TComboBoxInfo; 
begin 
    ZeroMemory(@Info, SizeOf(Info)); 
    Info.cbSize := SizeOf(Info); 
    GetComboBoxInfo(ComboBox1.Handle, Info); 
    FComboBoxListWnd := Info.hwndList; 
    FNewComboBoxListWndProc := MakeObjectInstance(ComboBoxListWndProc); 
    FOldComboBoxListWndProc := Pointer(GetWindowLong(FComboBoxListWnd, GWL_WNDPROC)); 
    SetWindowLong(FComboBoxListWnd, GWL_WNDPROC, Integer(FNewComboBoxListWndProc)); 
end; 

procedure TForm1.FormDestroy(Sender: TObject); 
begin 
    SetWindowLong(FComboBoxListWnd, GWL_WNDPROC, Integer(FOldComboBoxListWndProc)); 
    FreeObjectInstance(FNewComboBoxListWndProc); 
end; 

procedure TForm1.ComboBoxListWndProc(var Message: TMessage); 
var 
    R: TRect; 
    DY: Integer; 
begin 
    if (Message.Msg = WM_MOVE) and not FComboBoxListDropDown then 
    begin 
    FComboBoxListDropDown := True; 
    try 
     GetWindowRect(FComboBoxListWnd, R); 
     DY := (R.Bottom - R.Top) + ComboBox1.Height + 1; 
     // set new Y position for drop-down list: always above ComboBox1 
     SetWindowPos(FComboBoxListWnd, 0, R.Left, R.Top - DY , 0, 0, 
     SWP_NOOWNERZORDER or SWP_NOZORDER or SWP_NOSIZE or SWP_NOSENDCHANGING); 
    finally 
     FComboBoxListDropDown := False; 
    end; 
    end; 
    Message.Result := CallWindowProc(FOldComboBoxListWndProc, 
    FComboBoxListWnd, Message.Msg, Message.WParam, Message.LParam); 
end; 

Uwagi:

  1. I całkowicie zgadzam się z Dawidem, a inni, że jest to zły pomysł, aby zmienić to domyślne zachowanie specyficznego dla TComboBox. OP jeszcze nie odpowiedział na pytanie, dlaczego chciał takie zachowanie.
  2. Powyższy kod został przetestowany z D5/XP.
+0

Testowany pomyślnie, dziękuję! – Andrew

4

Możesz to zrobić, używając GetComboBoxInfo, aby uzyskać uchwyt do okna użytego na liście, a następnie przenieść to okno. W ten sposób:

type 
    TMyForm = class(TForm) 
    ComboBox1: TComboBox; 
    procedure ComboBox1DropDown(Sender: TObject); 
    protected 
    procedure WMMoveListWindow(var Message: TMessage); message WM_MOVELISTWINDOW; 
    end; 

.... 

procedure TMyForm.ComboBox1DropDown(Sender: TObject); 
begin 
    PostMessage(Handle, WM_MOVELISTWINDOW, 0, 0); 
end; 

procedure TMyForm.WMMoveListWindow(var Message: TMessage); 
var 
    cbi: TComboBoxInfo; 
    Rect: TRect; 
    NewTop: Integer; 
begin 
    cbi.cbSize := SizeOf(cbi); 
    GetComboBoxInfo(ComboBox1.Handle, cbi); 
    GetWindowRect(cbi.hwndList, Rect); 
    NewTop := ClientToScreen(Point(0, ComboBox1.Top-Rect.Height)).Y; 
    MoveWindow(cbi.hwndList, Rect.Left, NewTop, Rect.Width, Rect.Height, True); 
end; 

Zignorowałem kwestię sprawdzania błędów, aby zachować prosty kod.

Należy jednak pamiętać, że wygląda to dość okropnie, ponieważ animacja rozwijana jest nadal wyświetlana. Być może znajdziesz sposób, aby to wyłączyć.

Po prostu nie trzeba robić nic takiego, ponieważ system Windows już to robi. Przeciągnij formularz na dół ekranu i upuść kombi. Następnie zobaczysz listę wyświetloną nad kombinacją. Tak:

enter image description here

+3

Testowany w XP z D5. ten kod nie działa dla mnie. 'cbi.hwndList' nie jest przenoszony. natychmiast się otwiera i zamyka. – kobik

+1

@kobik Kolejny powód, żeby tego nie robić. Oczekuję, że problem dotyczy raczej XP niż D5. Prawdopodobnie musisz zmienić zachowanie dla różnych wersji systemu operacyjnego. Nigdy dobrego planu. –

+2

Zgadzam się w 100%. można to prawdopodobnie zrobić przez zahaczenie do 'GWL_WNDPROC' i obsługę' WM_SIZE', ale zachowanie jest tak nieoczekiwane, że całkowicie zrzuciłem ten pomysł. tylko komentarz boczny, myślę, że używając 'GetComboBoxInfo' jest lepszy niż CB_GETCOMBOBOXINFO (patrz komentarz dotyczący awarii na msdn). – kobik

Powiązane problemy