Jump to content
John Savoury

Modal dialog closes before user input

Recommended Posts

I have a strange problem.  I have created a component as a modal dialog to act as a menu.  (I created it because of focus issues when working with PDFs not allowing for accelerator keys on other components).  The user clicks a button or presses Alt+o (the button's hot key) to open the dialog.  If the user clicks the button everything works as it should, but if the user presses the accelerator key instead, the modal dialog briefly flashes open and then immediately closes before any user interaction with the dialog form.   To complicate matters, if I put a breakpoint on the line DropDownDialog.ShowModal; and then singlestep that line the dialog will remain open and wait for user input.  If I put the breakpoint on the following line instead, the window closes before the code reaches the breakpoint.

 

The menu is a listbox and the code of the Execute function looks like this:

function JDropDown.Execute: Boolean;
var Position: TPoint; Idx: Integer;
begin
     Result := False;
     DropDownDialog := TDropDownDialog.Create(Application);
     try
        DropDownDialog.ListBox1.Items.Assign(FItems);
        Position := Button.ClientToScreen(Point(Button.Left-FOffsetX,Button.Top+FOffsetY));
        DropDownDialog.Left := Position.X;
        DropDownDialog.Top := Position.Y;
        DropDownDialog.ModalResult := mrNone;
        DropDownDialog.ShowModal;
        If mItemIndex <> -1 then
        begin
             FItemSelected := mItemIndex-1;
             DropDownDialog.Free;
        end else
        begin
             FItemSelected := -1;
             DropDownDialog.Free;
        end;
        Result := True;
     except
        DropDownDialog.Free;
     end;
end;

The button on the main form has this code:

procedure TForm1.Button1Click(Sender: TObject);
begin
     If JDropDown1.Execute then
     begin
          ShowMessage(IntToStr(JDropDown1.ItemIndex));
     end;
end;

and the code on the dialog form is

procedure TDropDownDialog.ListBox1KeyUp(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
     ModalResult := mrNone;
     If Key <> 27 then
     begin
          If NotFound(Key) then
          begin
               mItemIndex := -1;
          end else
          begin
               mItemIndex:= ListBox1.ItemIndex+1;
          end;
     end else mItemIndex := -1;
     ModalResult := mrOK;
end;

procedure TDropDownDialog.ListBox1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var N: Integer;
begin
     ModalResult := mrNone;
     mItemIndex := ListBox1.ItemIndex+1;
     ModalResult := mrOK;
end;

The listbox is OwnerDrawFixed.   Can anyone see what I'm doing wrong?  Any help greatly appreciated.

Share this post


Link to post

It's presumably the handling of KeyUp that sets the modal result. If the modal dialog is shown by mouse action, then happens on mouse up. But if shown by keyboard action, it is shown on key down. Then the matching key up is handled by the modal form which immediately closes. 

Share this post


Link to post
     try
        ...
        DropDownDialog.ShowModal;
        If mItemIndex <> -1 then
             FItemSelected := mItemIndex-1;
        else
             FItemSelected := -1;
        Result := True;
     finally
        DropDownDialog.Free;
     end;

Side note

 

Also there's no sense in resetting ModalResult.

And what is mItemIndex? Seems like global var. I'd advise to make it a dialog's property to reduce dependencies.

Edited by Fr0sT.Brutal

Share this post


Link to post

Thanks for the replies.

 

@David Heffernan I hadn't thought about key down, but that gave me the idea to set the Key var to 0 when the dialog first opens.  (I hadn't realised that the keypress was being passed to the dialog from the main form.)   Setting the Key to 0 creates another issue.  If the user clicks the button to open the dialog instead of the keyboard, the keydown event is first triggered if the user then selects an item using the keyboard, but the key is rejected and set to 0.

 

With the change, the dialog still flashes open briefly if I press Alt-o or Shift-o, but if I just press o by itself now the dialog works the way it should (other than the behaviour mentioned above).  My problem now is that when I deploy the dialog component to my proper program (instead of my testing program) I have to use HotKeys, and of course they require Alt or Shift as modifiers and the open/close issue remains.

 

@Fr0sT.Brutal  The dialog's property is FItemSelected.  mItemIndex is indeed a global variable.  I use it to avoid confusion with the actual itemindex because if the user selects item[0] in the listbox the itemindex of 0 will make the dialog think it is mrNone.  (That's why I add 1 to mItemIndex).  I have made your suggested change to my code.

Share this post


Link to post

Why are you doing anything in response to key up? Try using some respectable programs and see which of them perform actions on key up. 

 

Once you have done that, remove TDropDownDialog.ListBox1KeyUp and see how things behave. 

Edited by David Heffernan

Share this post


Link to post

Thanks @David Heffernan   Using keyup was the way I was taught way back in Delphi 1 days!  I will certainly check out what you're saying.  In the meantime I have things working properly.  I added the line

 

If Shift <> [] then Exit;, and that stops the problem with modifiers because I only want the listbox to react to the choice of an item.

Share this post


Link to post
1 minute ago, John Savoury said:

Thanks @David Heffernan   Using keyup was the way I was taught way back in Delphi 1 days!  I will certainly check out what you're saying.  In the meantime I have things working properly.  I added the line

 

If Shift <> [] then Exit;, and that stops the problem with modifiers because I only want the listbox to react to the choice of an item.

This sounds like you are papering over the problem. 

Share this post


Link to post
6 hours ago, John Savoury said:

No, I'm not trying to - I do want to get things right, and that's why I asked for help.

So why continue doing things in key up? 

Share this post


Link to post

As I said in a previous post I am checking out what you said about keydown.  So far from what I've been reading online I have mixed feelings.  Without flaming me, can you please give a more detailed reason for keydown (which I have changed to).  Forgive me for being a bit slow - I am 80 after all!

Share this post


Link to post

Reason is exactly as I described in my first comment. Down and up come in pairs. If one part of your program responds to down and another to up then you can have a down up pair invoking two distinct actions. Down invokes form show modal, up invokes closing that form. 

 

More prosaicly, if there is a common uniform way to do something in the system, your default should always be to follow that common uniform convention. 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×