简体   繁体   中英

How to create a modeless dialog box and close it when the user clicks ok?

The user insists on a pop up box for each 'significant event" in the application. Adding a line to a memo or listbox is not aceptable.

I cannout use standard modal boxes bceuase the PC is often unnatended and my applciation will be left waiting for the user to click "OK" before it continues.

Can I just dynamically crate a form with a memo component and an OK button and pass some text to display in the memo?

I tried that and I got an access violation when OK was clicked.

Questions:

  • is there any need to launch a thread to display the form, since it is being shown modelessly?
  • what should I pass as the constructor parameter (prent)? Nil, since the form will destory itself?
  • when the user clicks OK what should I call? Close() or Free() ? Either of these gives an access violation approx 1/2 seconds after clicking OK, but if I do nothing in the function the code runs fine (with a memory leak, of course)

Google is not so helpful as the form should destroy itself, while examples have its creator destroying it.


In main form:

     theDialogForm := TDialogFormForm.Create(Nil);
     theDialogForm.ShowTheForm('Database error '+#13+#10+''+#13+#10+
                E.ClassName+#13+#10+
                E.Message);

and the dialog form ...

unit fDialogForm;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TDialogFormForm = class(TForm)
    Memo1: TMemo;
    OkButton: TButton;
    procedure OkButtonClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
      procedure ShowTheForm(const theMessage : String);
  end;

implementation

{$R *.dfm}

// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
procedure TDialogFormForm.FormCreate(Sender: TObject);
begin
   Visible := False;
end;

// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
procedure TDialogFormForm.OkButtonClick(Sender: TObject);
begin
//   Close();
   Free();
end;

// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
procedure TDialogFormForm.ShowTheForm(const theMessage : String);
begin
   Memo1.Text := theMessage;
   Show();
end;

end.

call stack

main thread ($630):
005164e3 +013 StoreRoom.exe Vcl.Controls          TControl.WMLButtonUp
00515b30 +2d4 StoreRoom.exe Vcl.Controls          TControl.WndProc
0051a47b +5b3 StoreRoom.exe Vcl.Controls          TWinControl.WndProc
00537bf0 +06c StoreRoom.exe Vcl.StdCtrls          TButtonControl.WndProc
00519ad0 +02c StoreRoom.exe Vcl.Controls          TWinControl.MainWndProc
0048dea4 +014 StoreRoom.exe System.Classes        StdWndProc
76677885 +00a USER32.dll                          DispatchMessageW
005b7c63 +0f3 StoreRoom.exe Vcl.Forms             TApplication.ProcessMessage
005b7ca6 +00a StoreRoom.exe Vcl.Forms             TApplication.HandleMessage
005b7fd9 +0c9 StoreRoom.exe Vcl.Forms             TApplication.Run
00823616 +17a StoreRoom.exe StoreRoom      56 +24 initialization
75c43398 +010 kernel32.dll                        BaseThreadInitThunk

To close a form, you can either

  • close it by calling Close and set action to caFree in the OnClose Event
  • call Release , which is a special flavour of Free, especially for forms.

The problem with calling Free , is that the form is freed immediately, while you are still handling the click event on the close button. The form and therefore the button is freed while in that process, causing the access violation. Release internally sends a message to the form, causing it to close after the click is handled.

  1. You do not need a thread to display the form. But, since the form should remain visible, I'd suggest you set it's FormStyle to fsStayOnTop
  2. Since you are destroying the form, you can pass nil to Create()
  3. To destroy your form, call Close and in your form's OnClose event, set Action to caFree .

您为什么不轻松自如地制作一个看起来像对话框的不可见面板,当发生重大事件时该面板变为可见,而当用户单击“确定”时该对话框不可见?

is there any need to launch a thread to display the form, since it is being shown modelessly?

VCL is not multi-threaded. You should always run forms in the main thread. You maybe may call Win32 API ShowMessageBox in extra thread, dunno, but only for a single message then. No way to accumulate events into TMemo or grid.

what should I pass as the constructor parameter (prent)?

I'd pass Application, but it is not big deal.

when the user clicks OK what should I call? Close() or Free() ?

Neither, there is no point to create/delete the form. It only has sense when you can have many forms of the same class. This time, just make Delphi auto-create and auto-dispose it when the program starts/ends.

You only should show it or hide it. No point in creating and disposing manually.


Now, about thread - why should you block your application at all? Make that window AlwaysOnTop, then use TApplication.OnActivate event to check when user switches to it and to bring that window to front and to the center of screen/mainform. So that user would see it.

Or you may try 3rd-party notifications tools like Snarl with options for messages to never expire.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM