2010. június 25., péntek

How to set focus on a MessageDlg button


Problem/Question/Abstract:

Is there a way to set the focus on a certain button when using MessageDlg? I want to be able to set focus to the No button when the dialog executes. By default the focus is always on the Yes button, no matter what order I code them in the function.

Answer:

Solve 1:

I had a similar situation come up and I wanted to specify which button was considered the default when pressing ENTER and which one would be the default for pressing ESCAPE. Also, I wanted other text in the buttons. So instead of Yes/ No I would have liked Save File/ Skip Save.

Then it becomes easier for the user to determine which button to press. They don't have to read the whole message, they can just look at the button. So, I will give you my code for that. I call it MultiMessageDlg, you can specify up to 4 buttons. Here is the source for my form:


unit MultiAsk;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls;

type
  TMultiAskMenu = class(TForm)
    LAsk: TLabel;
    PButtons: TPanel;
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Panel1: TPanel;
    Image1: TImage;
    Button4: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure Setup(MsgType: TMsgDlgType; Num: Integer; Title, Ask, S1, S2, S3, S4: string);
  end;

var
  MultiAskMenu: TMultiAskMenu;

implementation

uses
  GlobalRW;

{$R *.DFM}

procedure ButtonCode(const Butt1: TButton; var Cap: string);
begin
  Butt1.Cancel := False;
  Butt1.Tag := 0;
  if Pos(' + ', Cap) = 1 then
  begin
    Butt1.Tag := 1;
    Delete(Cap, 1, 1);
  end;
  if Pos(' - ', Cap) = 1 then
  begin
    Butt1.Cancel := True;
    Delete(Cap, 1, 1);
  end;
  Butt1.Caption := Cap;
end;

procedure TMultiAskMenu.Setup(MsgType: TMsgDlgType; Num: Integer;
  Title, Ask, S1, S2, S3, S4: string);
var
  TmpBmp: TBitMap;
  IconID: PChar;
  X, W1, W2, W3, W4: Integer;
  NonClientMetrics: TNonClientMetrics;
  HIcon1: HIcon;
const
  IconIDs: array[TMsgDlgType] of PChar = (IDI_EXCLAMATION, IDI_HAND, IDI_ASTERISK,
    IDI_QUESTION, nil);
begin
  case MsgType of
    mtInformation:
      begin
        Self.Caption := ' Information ';
      end;
    mtWarning: b
      begin
        Self.Caption := ' Warning ';
      end;
    mtError:
      begin
        Self.Caption := ' Error ';
      end;
  end;
  if Title <> '' then
    Self.Caption := Title;
  NonClientMetrics.cbSize := SizeOf(NonClientMetrics);
  if SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, @NonClientMetrics, 0) then
    LAsk.Font.Handle := CreateFontIndirect(NonClientMetrics.lfMessageFont);
  IconID := IconIDs[MsgType];
  if IconID <> nil then
  begin
    with Image1 do
    begin
      HIcon1 := LoadIcon(0, IconID);
      Picture.Icon.ReleaseHandle;
      Picture.Icon.Handle := HIcon1;
    end;
  end;
  TmpBmp := TBitMap.Create;
  TmpBmp.Canvas.Font := Button1.Font;
  W1 := TmpBmp.Canvas.TextWidth(S1) + 10;
  Button1.Width := W1;
  W2 := TmpBmp.Canvas.TextWidth(S2) + 10;
  Button2.Width := W2;
  W3 := TmpBmp.Canvas.TextWidth(S3) + 10;
  Button3.Width := W3;
  W4 := TmpBmp.Canvas.TextWidth(S4) + 10;
  Button4.Width := W4;
  TmpBmp.Free;
  LAsk.Caption := Ask;
  PButtons.Top := LAsk.Top + LAsk.Height + 30;
  case Num of
    1:
      begin
        Button1.Left := Button2.Left;
        Button2.Visible := False;
        Button3.Visible := False;
        Button4.Visible := False;
        Button1.Left := (Self.Width - W1) div 2;
      end;
    2:
      begin
        Button2.Left := Button3.Left;
        Button3.Visible := False;
        Button4.Visible := False;
        Button1.Caption := S1;
        X := (Self.Width - W1 - W2) div 3;
        Button1.Left := X;
        Button2.Left := X + W1 + X;
      end;
    3:
      begin
        Button4.Visible := False;
        X := (Self.Width - W1 - W2 - W3) div 4;
        Button1.Left := X;
        Button2.Left := X + W1 + X;
        Button3.Left := X + W1 + X + W2 + X;
      end;
    4:
      begin
        X := (Self.Width - W1 - W2 - W3 - W4) div 5;
        Button1.Left := X;
        Button2.Left := Button1.Left + W1 + X;
        Button3.Left := Button2.Left + W2 + X;
        Button4.Left := Button3.Left + W3 + X;
      end;
  end;
  {Take into Account pressing ESCAPE and Default buttons!!!
  +Yes  + = Default
  -No  - = Escape}
  ButtonCode(Button1, S1);
  ButtonCode(Button2, S2);
  ButtonCode(Button3, S3);
  ButtonCode(Button4, S4);
  Self.AutoSize := True;
end;

procedure TMultiAskMenu.Button1Click(Sender: TObject);
begin
  ModalResult := 1;
end;

procedure TMultiAskMenu.Button2Click(Sender: TObject);
begin
  ModalResult := 2;
end;

procedure TMultiAskMenu.Button3Click(Sender: TObject);
begin
  ModalResult := 3;
end;

procedure TMultiAskMenu.Button4Click(Sender: TObject);
begin
  ModalResult := 4;
end;

procedure TMultiAskMenu.FormShow(Sender: TObject);
begin
  if Button1.Tag = 1 then
    Button1.SetFocus;
  if Button2.Tag = 1 then
    Button2.SetFocus;
  if Button3.Tag = 1 then
    Button3.SetFocus;
  if Button4.Tag = 1 then
    Button4.SetFocus;
end;

procedure TMultiAskMenu.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Self.Image1.Picture.Icon.ReleaseHandle;
end;

end.

So in order to use it you would do something like this:


if MultiMessageDlg('',
  ' It has been awhile since you last checked for updates. Do you  wish to check the TurboView Internet site for updates to TurboView?',
  mtInformation, 2, ' + Check NOW ', ' - Check Next Month', '', '') = 1 then
begin
  {code to do checking for the latest version of program}
end;


So the format is MultiMessageDlg(TitleText, MessageText, MessageType, NumberOfButtons, Button1Text, Button2Text, Button3Text, Button4Text);

The return value is which button was pressed [1..4];

TitleText is optional, if not title is given then the normal MessageDlg title will be used for window dialog title.

MessageText is what message you want displayed

MessageType is the same thing you provide to the normal MessageDlg function

NumberOfButtons is how many buttons to actually display

ButtonText, you can provide text for up to 4 buttons.

Note: If you want a certain button to be the DEFAULT button, then you would put a "+" plus sign in front of the text. For example: "+Save File" . And if you want a button to be the default ESCAPE button, then put a "-" minus in front, like so: "-Dont Save".


Solve 2:

The following function will let you define the default button, then center the dialog above the OwnerWnd, and then play the sound associated with the message type:


function MessageDlgEx(OwnerWnd: HWND; DefButton: Integer; const Msg: string;
  DlgType: TMsgDlgType; Buttons: TMsgDlgButtons): Integer;
var
  vButton: TButton;
  vRect: TRect;
  vWidth: Integer;
  vHeight: Integer;
  vTop: Integer;
  vLeft: Integer;
  I: Integer;
begin
  with CreateMessageDialog(Msg, DlgType, Buttons) do
  begin
    try
      { Get the TRect }
      GetWindowRect(OwnerWnd, vRect);
      { Center the dialog }
      vWidth := vRect.Right - vRect.Left;
      vHeight := vRect.Bottom - vRect.Top;
      vTop := vRect.Top;
      vLeft := vRect.Left;
      Top := vTop + ((vHeight - Height) div 2);
      Left := vLeft + ((vWidth - Width) div 2);
      { Set the default button }
      for I := 0 to Pred(ComponentCount) do
      begin
        if Components[I] is TButton then
        begin
          vButton := TButton(Components[I]);
          vButton.Default := (vButton.ModalResult = DefButton);
          if vButton.Default then
          begin
            ActiveControl := vButton;
          end;
        end;
      end;
      { Play the sound associated with the DlgType }
      case DlgType of
        mtConfirmation: MessageBeep(MB_ICONQUESTION);
        mtError: MessageBeep(MB_ICONERROR);
        mtInformation: MessageBeep(MB_ICONINFORMATION);
        mtWarning: MessageBeep(MB_ICONWARNING);
      end;
      { Show the dialog }
      Result := ShowModal;
    finally
      free;
    end;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  if MessageDlgEx(Handle, mrNo, 'Do you wan''t this program to erase all of your files?',
    mtWarning, [mbYes, mbNo]) = mrYes then
    ShowMessage('Okay...');
end;

Nincsenek megjegyzések:

Megjegyzés küldése