I want to position a MessageBox in a particular position with respect to the active cell in a string grid and this is no problem using MessageDlgPos()
except that I want to prevent the box running off the right or bottom of the screen when the active cell is close to the right or bottom. What I need for this is a way of getting the dimensions of the box but I cannot see a simple way of getting these. Anyone know how without creating my own box?
-
There isn't a way to do that before the dialog is shown, as the width is adjusted dynamically according to the length of the content. – Ken White Jun 28 '21 at 16:42
-
@Ron Jay: Are you using the task dialog or the legacy dialog? – Andreas Rejbrand Jun 28 '21 at 17:04
-
I guess it is the task dialog – Ron Jay Jun 28 '21 at 22:22
-
2It's much simpler to use your own form for displaying the information. Then you are in full control of its dimensions and position. – dummzeuch Jun 29 '21 at 07:36
3 Answers
The MessageDlg...()
functions do not support what you are asking for. The dimensions of the dialog are not known until the dialog is being displayed, and you have no way to access the dialog window directly to query/re-position it, except maybe with a WH_CBT
hook from SetWindowsHookEx()
.
That being said...
On Windows Vista+ with Vcl.Dialogs.UseLatestCommonDialogs=true
and Visual Styles enabled, the MessageDlg...()
functions call the Win32 TaskDialogIndirect()
API to display a message box. You have no control over that dialog's dimensions, so you would have to wait for that dialog to issue a TDN_DIALOG_CONSTRUCTED
notification to then query its actual dimensions before it is displayed, so you can then adjust its position as needed. However, the MessageDlg...()
functions do not provide access to any of TaskDialogIndirect()
's notifications (TCustomTaskDialog
, which is used internally, does have an OnDialogConstructed
event, amongst other events). So, if you wanted to reposition this dialog, you would have to call TaskDialogIndirect()
yourself with a custom callback function (or, use the VCL's TTaskDialog
wrapper).
On pre-Vista, or with UseLatestCommonDialogs=false
or Visual Styles disabled, the MessageDlg...()
functions display a custom VCL TForm
via Vcl.Dialogs.CreateMessageDialog()
instead, which you can call directly, and then pretty much query, manipulate, and show the returned TForm
however you want. Just be sure to Free()
it when you are done using it.

- 555,201
- 31
- 458
- 770
-
Thanks Remy, I shall think about which approach to take but I am committed to other work for the next day or two. – Ron Jay Jun 28 '21 at 22:28
-
Your suggestion of using CreateMessageDialog works perfectly and is very simple to implement. Just create a dialog Form, check its dimensions, set its position and then ShowModal after which process the return value and free the form. Even though using WIndows 10 and Rad Studio 10.4, I didn't need to set UseLatestCommonDialogs=false or Visual Styles disabled. Many thanks for pointing me in this direction. – Ron Jay Jun 30 '21 at 10:57
You could use an actual TTaskDialog. You can create you own version of it, add a TaskDialogConstructed procedure and get the dimension in the TaskDialogConstructed procedure. Something along the lines of the following.
type
TTaskDialog = class(Vcl.Dialogs.TTaskDialog)
protected
procedure TaskDialogConstructed(Sender: TObject);
end;
procedure TTaskDialog.TaskDialogConstructed(Sender: TObject);
var
TaskDialog: TTaskDialog;
R: TRect;
begin
TaskDialog := Sender as TTaskDialog;
Win32Check(GetWindowRect(TaskDialog.Handle, R));
{... Do whatever with R ...}
end;
function ExecuteTaskDialog(AOwner: TComponent; ATitle, AText: string; ACommonButtons: TTaskDialogCommonButtons = [tcbOK]): integer;
var
TaskDialog: TTaskDialog;
begin
TaskDialog := TTaskDialog.Create(AOwner);
with TaskDialog do
begin
Caption := Application.Title;
Title := ATitle;
Text := AText;
MainIcon := tdiNone;
Flags := Flags + [tfUseHiconMain];
CommonButtons := ACommonButtons;
CustomMainIcon.LoadFromResourceName(HInstance, 'MAINICON');
OnDialogConstructed := TaskDialogConstructed;
Execute;
Result := ModalResult;
Free;
end;
end;

- 145
- 8
-
If you are going to derive a new class, then you should override the virtual `DoOnDialogContructed()` method that is inherited from `TCustomTaskDialog`, rather than assigning a handler to the public `OnDialogConstructed` event – Remy Lebeau Jun 30 '21 at 14:10
Create the MessageDlg yourself. Add an OnActivate or OnShow event. In this method, ask / change the properties of the dialog.
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.StdCtrls;
type
Tfrm = class(TForm)
btn: TButton;
procedure btnClick(Sender: TObject);
private
procedure OnDlgActivate(Sender: TObject);
public
{ Public-Deklarationen }
end;
var
frm: Tfrm;
implementation
uses
Vcl.Dialogs, System.TypInfo;
{$R *.dfm}
procedure Tfrm.btnClick(Sender: TObject);
var
Ldlg : TForm;
LiRet : integer;
begin
Ldlg := CreateMessageDialog('Hallo World!', mtInformation,mbYesNo, mbYes);
try
Ldlg.OnActivate := OnDlgActivate;
LiRet := Ldlg.ShowModal;
finally
Ldlg.free;
end;
end;
procedure Tfrm.OnDlgActivate(Sender: TObject);
var
Lfrm: TForm;
LcTxt: string;
begin
Lfrm := Sender as TForm;
LcTxt := Format('%s %sLeft: %d / Top: %d', [Lfrm.ClassName, sLineBreak, Lfrm.Left, Lfrm.Top]);
ShowMessage(LcTxt);
end;
end.

- 295
- 1
- 9
-
2(1) Why not simply `Ldlg.OnActivate := OnDlgActivate`? (2) You leak one form object for each invocation. Basically this is the idea outlined by Remy, but with a subpar implementation. (3) You don't want to use the Windows 95-styled dialogs in new apps. You want to use task dialogs. – Andreas Rejbrand Jun 29 '21 at 12:54
-
@AndreasRejbrand (1) Right. Was probably not possible with an earlier version of Delphi. (2) Right. I added the try block. (3) I cannot see the desired style of dialogue from the request. – USauter Jun 29 '21 at 13:55
-
@USauter: (3) It is safe to assume (unless stated otherwise) that the OP wants to use the current styles rather than styles from a decade and a half old OS. It's the same way that it is assumed that (unless stated otherwise), code related questions refer to the current version of Delphi and current code solutions are appropriate. – Ken White Jun 30 '21 at 03:54
-
@KenWhite My program code is now 20 years old and has grown over time. Nice that I was able to experience something new. – USauter Jun 30 '21 at 05:26