How to simulate drop-down form in Delphi?

前端 未结 2 692
猫巷女王i
猫巷女王i 2021-01-30 18:13

How can i create a \"drop-down\" window using Delphi?

Everything beyond this point is research effort; and is in no way related to the answer.

Research

2条回答
  •  滥情空心
    2021-01-30 18:45

    How can i create a "drop-down" window using Delphi?

    You put together all the bits and pieces you have summarized, there is no one VCL class/function that would produce a drop down form.

    There are a few points to mention in your research though.


    First, you're confusing activation with focus. Focus is not preserved in the calling form when another window pops in front of it, activation is - or it seems that way. Focus is where keyboard input goes, it is obviously on either the popped/dropped window or on a control in it.


    Your problem with controls not showing with AnimateWindow is that, VCL does not create underlying native (OS) controls of TWinControls until it is necessary (non-wincontrols are not a problem). As far as VCL is concerned, creating them is not normally necessary until they will be visible, which is when you set Visible of your form to true (or call Show), which you cannot since then there will be no animation, unless of course you set visible after the animation.

    This is also the missing requirement when you try to refresh your form:

    AnimateWindow(Self.Handle, 200, AW_VER_POSITIVE or AW_SLIDE or AW_ACTIVATE);
    Self.Repaint;
    Self.Update;
    Self.Invalidate;
    

    Notice that in the above quote from the question, none of the calls fail. But there's nothing to paint, the form is not even visible yet.

    Any means of forcing the controls to be created and making them visible will make your animation come alive.

    ...
    if comboBoxAnimation then
    begin
      for i := 0 to ControlCount - 1 do
        if Controls[i] is TWinControl and Controls[i].Visible and
            not TWinControl(Controls[i]).HandleAllocated then begin
          TWinControl(Controls[i]).HandleNeeded;
          SetWindowPos(TWinControl(Controls[i]).Handle, 0, 0, 0, 0, 0,
              SWP_NOSIZE or SWP_NOMOVE or SWP_NOZORDER or SWP_NOACTIVATE or
              SWP_SHOWWINDOW);
        end;
      AnimateWindow(Handle, 200, AW_VER_POSITIVE or AW_SLIDE or AW_ACTIVATE);
      Visible := True; // synch VCL
    end
    else
      ...
    

    This is just an example, showing the form off-screen or any other creative method could work equally well. Here, in this answer, I achieve the same by setting animated form's height to '0' before setting visible to true (I like the approach in this answer better though..).


    Regarding not dropping again when the form is already dropped down, you don't have to post a message to the calling form for that. In fact don't do that, it requires unnecessary cooperation from the calling form. There will ever be only one instance to be dropped down, so you can use a global:

      TfrmPopup = class(TForm)
        ...
        procedure FormDestroy(Sender: TObject);
      private
        FNotificationParentWnd: HWND;
        class var
          FDroppedDown: Boolean;
      protected
        ...
    
    
    procedure TfrmPopup.Show(Owner: TForm; NotificationParentWindow: HWND;
      ...
    
      if not FDroppedDown then begin
          if comboBoxAnimation then begin
    
            // animate as above
    
            Visible := True; // synch with VCL
            FDroppedDown := True;
          end
          else
            inherited Show;
        end;
    end;
    
    procedure TfrmPopup.FormDestroy(Sender: TObject);
    begin
      FDroppedDown := False;
    end;
    

提交回复
热议问题