Custom Uninstall page (not MsgBox)

You can modify the uninstall form to behave like the install form (with pages and the Next/Back buttons).

In the InitializeUninstallProgressForm:

  • Create the new pages and insert them to the UninstallProgressForm.InnerNotebook (or the .OuterNotebook).
  • Implement the "Next" and the "Back" buttons.
  • You can also make the "Cancel" button working.
  • Run modal loop of the form using UninstallProgressForm.ShowModal.
  • Only after the modal loop exits, restore original layout of the form and let the uninstallation continue.
[Code]

var
  UninstallFirstPage: TNewNotebookPage;
  UninstallSecondPage: TNewNotebookPage;
  UninstallBackButton: TNewButton;
  UninstallNextButton: TNewButton;

procedure UpdateUninstallWizard;
begin
  if UninstallProgressForm.InnerNotebook.ActivePage = UninstallFirstPage then
  begin
    UninstallProgressForm.PageNameLabel.Caption := 'First uninstall wizard page';
    UninstallProgressForm.PageDescriptionLabel.Caption :=
      'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
  end
    else
  if UninstallProgressForm.InnerNotebook.ActivePage = UninstallSecondPage then
  begin
    UninstallProgressForm.PageNameLabel.Caption := 'Second uninstall wizard page';
    UninstallProgressForm.PageDescriptionLabel.Caption :=
      'Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.';
  end;

  UninstallBackButton.Visible :=
    (UninstallProgressForm.InnerNotebook.ActivePage <> UninstallFirstPage);

  if UninstallProgressForm.InnerNotebook.ActivePage <> UninstallSecondPage then
  begin
    UninstallNextButton.Caption := SetupMessage(msgButtonNext);
    UninstallNextButton.ModalResult := mrNone;
  end
    else
  begin
    UninstallNextButton.Caption := 'Uninstall';
    { Make the "Uninstall" button break the ShowModal loop }
    UninstallNextButton.ModalResult := mrOK;
  end;
end;  

procedure UninstallNextButtonClick(Sender: TObject);
begin
  if UninstallProgressForm.InnerNotebook.ActivePage = UninstallSecondPage then
  begin
    UninstallNextButton.Visible := False;
    UninstallBackButton.Visible := False;
  end
    else
  begin
    if UninstallProgressForm.InnerNotebook.ActivePage = UninstallFirstPage then
    begin
      UninstallProgressForm.InnerNotebook.ActivePage := UninstallSecondPage;
    end;
    UpdateUninstallWizard;
  end;
end;

procedure UninstallBackButtonClick(Sender: TObject);
begin
  if UninstallProgressForm.InnerNotebook.ActivePage = UninstallSecondPage then
  begin
    UninstallProgressForm.InnerNotebook.ActivePage := UninstallFirstPage;
  end;
  UpdateUninstallWizard;
end;

procedure InitializeUninstallProgressForm();
var
  PageText: TNewStaticText;
  PageNameLabel: string;
  PageDescriptionLabel: string;
  CancelButtonEnabled: Boolean;
  CancelButtonModalResult: Integer;
begin
  if not UninstallSilent then
  begin
    { Create the first page and make it active }
    UninstallFirstPage := TNewNotebookPage.Create(UninstallProgressForm);
    UninstallFirstPage.Notebook := UninstallProgressForm.InnerNotebook;
    UninstallFirstPage.Parent := UninstallProgressForm.InnerNotebook;
    UninstallFirstPage.Align := alClient;

    PageText := TNewStaticText.Create(UninstallProgressForm);
    PageText.Parent := UninstallFirstPage;
    PageText.Top := UninstallProgressForm.StatusLabel.Top;
    PageText.Left := UninstallProgressForm.StatusLabel.Left;
    PageText.Width := UninstallProgressForm.StatusLabel.Width;
    PageText.Height := UninstallProgressForm.StatusLabel.Height;
    PageText.AutoSize := False;
    PageText.ShowAccelChar := False;
    PageText.Caption := 'Press Next to proceeed with uninstallation.';

    UninstallProgressForm.InnerNotebook.ActivePage := UninstallFirstPage;

    PageNameLabel := UninstallProgressForm.PageNameLabel.Caption;
    PageDescriptionLabel := UninstallProgressForm.PageDescriptionLabel.Caption;

    { Create the second page }

    UninstallSecondPage := TNewNotebookPage.Create(UninstallProgressForm);
    UninstallSecondPage.Notebook := UninstallProgressForm.InnerNotebook;
    UninstallSecondPage.Parent := UninstallProgressForm.InnerNotebook;
    UninstallSecondPage.Align := alClient;

    PageText := TNewStaticText.Create(UninstallProgressForm);
    PageText.Parent := UninstallSecondPage;
    PageText.Top := UninstallProgressForm.StatusLabel.Top;
    PageText.Left := UninstallProgressForm.StatusLabel.Left;
    PageText.Width := UninstallProgressForm.StatusLabel.Width;
    PageText.Height := UninstallProgressForm.StatusLabel.Height;
    PageText.AutoSize := False;
    PageText.ShowAccelChar := False;
    PageText.Caption := 'Press Uninstall to proceeed with uninstallation.';

    UninstallNextButton := TNewButton.Create(UninstallProgressForm);
    UninstallNextButton.Parent := UninstallProgressForm;
    UninstallNextButton.Left :=
      UninstallProgressForm.CancelButton.Left -
      UninstallProgressForm.CancelButton.Width -
      ScaleX(10);
    UninstallNextButton.Top := UninstallProgressForm.CancelButton.Top;
    UninstallNextButton.Width := UninstallProgressForm.CancelButton.Width;
    UninstallNextButton.Height := UninstallProgressForm.CancelButton.Height;
    UninstallNextButton.OnClick := @UninstallNextButtonClick;

    UninstallBackButton := TNewButton.Create(UninstallProgressForm);
    UninstallBackButton.Parent := UninstallProgressForm;
    UninstallBackButton.Left :=
      UninstallNextButton.Left - UninstallNextButton.Width -
      ScaleX(10);
    UninstallBackButton.Top := UninstallProgressForm.CancelButton.Top;
    UninstallBackButton.Width := UninstallProgressForm.CancelButton.Width;
    UninstallBackButton.Height := UninstallProgressForm.CancelButton.Height;
    UninstallBackButton.Caption := SetupMessage(msgButtonBack);
    UninstallBackButton.OnClick := @UninstallBackButtonClick;
    UninstallBackButton.TabOrder := UninstallProgressForm.CancelButton.TabOrder;

    UninstallNextButton.TabOrder := UninstallBackButton.TabOrder + 1;

    UninstallProgressForm.CancelButton.TabOrder := UninstallNextButton.TabOrder + 1;

    { Run our wizard pages } 
    UpdateUninstallWizard;
    CancelButtonEnabled := UninstallProgressForm.CancelButton.Enabled
    UninstallProgressForm.CancelButton.Enabled := True;
    CancelButtonModalResult := UninstallProgressForm.CancelButton.ModalResult;
    UninstallProgressForm.CancelButton.ModalResult := mrCancel;

    if UninstallProgressForm.ShowModal = mrCancel then Abort;

    { Restore the standard page payout }
    UninstallProgressForm.CancelButton.Enabled := CancelButtonEnabled;
    UninstallProgressForm.CancelButton.ModalResult := CancelButtonModalResult;

    UninstallProgressForm.PageNameLabel.Caption := PageNameLabel;
    UninstallProgressForm.PageDescriptionLabel.Caption := PageDescriptionLabel;

    UninstallProgressForm.InnerNotebook.ActivePage :=
      UninstallProgressForm.InstallingPage;
  end;
end;

First page

Second page

Uninstalling


See also Inno Setup - How to create a OuterNotebook/welcome page in the uninstaller?


Luckily Inno Setup gives enough abilities to build own forms so you can imitate any page of uninstall form on another modal form. Here's what I managed to create. The form takes dimensions from UninstallForm, contains a TNewNotebook control and buttons to jump between pages and to cancel the dialog.

demo

const
  ControlGap = 5; // determined empirically

// Set Back/Next buttons state according to current selected notebook page
procedure UpdateButtonsState(Form: TSetupForm);
var
  Notebook: TNewNotebook;
  BtnBack, BtnNext: TButton;
begin
  Notebook := TNewNotebook(Form.FindComponent('Notebook'));
  BtnBack := TButton(Form.FindComponent('BtnBack'));
  BtnNext := TButton(Form.FindComponent('BtnNext'));

  // Update buttons state
  BtnBack.Enabled := (Notebook.ActivePage <> Notebook.Pages[0]);
  if Notebook.ActivePage <> Notebook.Pages[Notebook.PageCount - 1] then
  begin
    BtnNext.Caption := SetupMessage(msgButtonNext)
    BtnNext.ModalResult := mrNone;
  end
  else
  begin
    BtnNext.Caption := SetupMessage(msgButtonFinish);
    BtnNext.ModalResult := mrYes;
  end;
end;

// Change notebook page
procedure BtnPageChangeClick(Sender: TObject);
var
  NextPage: TNewNotebookPage;
  Notebook: TNewNotebook;
  Form: TWinControl;
  Button, BtnBack, BtnNext: TButton;
begin
  Button := TButton(Sender);
  Form := Button;
  while not (Form is TSetupForm) do
    Form := Form.Parent;
  Notebook := TNewNotebook(Form.FindComponent('Notebook'));
  BtnBack := TButton(Form.FindComponent('BtnBack'));
  BtnNext := TButton(Form.FindComponent('BtnNext'));

  // Avoid cycled style of Notebook's page looping
  if (Button = BtnBack) and (Notebook.ActivePage = Notebook.Pages[0]) then
    NextPage := nil
  else
  if (Button = BtnNext) and (Notebook.ActivePage = Notebook.Pages[Notebook.PageCount - 1]) then
    NextPage := nil
  else
    NextPage := Notebook.FindNextPage(Notebook.ActivePage, Button = BtnNext);
  Notebook.ActivePage := NextPage;

  UpdateButtonsState(TSetupForm(Form));
end;

// Add a new page to notebook and return it
function AddPage(NotebookForm: TSetupForm): TNewNotebookPage;
var
  Notebook: TNewNotebook;
begin
  Notebook := TNewNotebook(NotebookForm.FindComponent('Notebook'));
  Result := TNewNotebookPage.Create(Notebook);
  Result.Notebook:=Notebook;
  Result.Parent:=Notebook;
  Result.Align := alClient;
  if Notebook.ActivePage = nil then 
    Notebook.ActivePage := Result;
  UpdateButtonsState(NotebookForm);
end;

// Create a form with notebook and 3 buttons: Back, Next, Cancel
function CreateNotebookForm: TSetupForm;
var
  Notebook: TNewNotebook;
  NotebookPage: TNewNotebookPage;
  Pan: TPanel;
  TmpLabel: TLabel;
  MaxWidth, i: Integer;
  BtnBack, BtnNext, BtnCancel: TButton;
  BtnLabelMsgIDs: array of TSetupMessageID;
begin
  Result := CreateCustomForm;
  Result.SetBounds(0, 0, UninstallProgressForm.Width, UninstallProgressForm.Height);
  Result.Position := poOwnerFormCenter;

  Notebook := TNewNotebook.Create(Result);
  Notebook.Parent := Result;
  Notebook.Name := 'Notebook';  // will be used for searching
  Notebook.Align := alClient;

  Pan := TPanel.Create(Result);
  Pan.Parent := Notebook;
  Pan.Caption := '';
  Pan.Align := alBottom;

  // Create buttons

  BtnNext := TNewButton.Create(Result);
  with BtnNext do
  begin
    Parent := Pan;
    Name := 'BtnNext';  // will be used for searching
    Caption := SetupMessage(msgButtonNext);
    OnClick := @BtnPageChangeClick;
    ParentFont := True;
  end;

  BtnBack := TNewButton.Create(Result);
  with BtnBack do
  begin
    Parent := Pan;
    Caption := SetupMessage(msgButtonBack);
    Name := 'BtnBack';  // will be used for searching
    OnClick := @BtnPageChangeClick;
    ParentFont := True;
  end;

  BtnCancel := TNewButton.Create(Result);
  with BtnCancel do
  begin
    Parent := Pan;
    Name := 'BtnCancel';  // will be used for searching
    Caption := SetupMessage(msgButtonCancel);
    ModalResult := mrCancel;
    Cancel := True;
    ParentFont := True;
  end;

  // Determine dimensions of buttons. Should use TCanvas.TextWidth here
  // but it doesn't allow Font property assignment :(

  TmpLabel := TLabel.Create(Result);
  with TmpLabel do
  begin
    Left := 0;
    Top := 0;
    Parent := Pan;
    ParentFont := True;
    Visible := False;
    WordWrap := False;
    Autosize := True;
  end;

  // Determine max label width among these labels: Back, Next, Cancel, Finish
  SetArrayLength(BtnLabelMsgIDs, 4);
  BtnLabelMsgIDs[0] := msgButtonBack;
  BtnLabelMsgIDs[1] := msgButtonNext;
  BtnLabelMsgIDs[2] := msgButtonCancel;
  BtnLabelMsgIDs[3] := msgButtonFinish;
  MaxWidth := 0;
  for i := Low(BtnLabelMsgIDs) to High(BtnLabelMsgIDs) do
  begin
    TmpLabel.Caption := SetupMessage(BtnLabelMsgIDs[i]) + 'WWW';  // Add letters for surrounding spaces 
    if MaxWidth < TmpLabel.Width then
      MaxWidth := TmpLabel.Width;
  end;

  TmpLabel.Caption := 'Yy';  // Determine height

  // Assign sizes and positions
  Pan.ClientHeight := TmpLabel.Height*4;

  with BtnBack do
  begin
    Width := MaxWidth;
    Height := TmpLabel.Height*2;
    Left := Parent.ClientWidth - 3*(MaxWidth + ScaleX(ControlGap));
    Top := (Parent.ClientHeight - Height) div 2;
  end;
  with BtnNext do
  begin
    Width := MaxWidth;
    Height := TmpLabel.Height*2;
    Left := Parent.ClientWidth - 2*(MaxWidth + ScaleX(ControlGap));
    Top := (Parent.ClientHeight - Height) div 2;
  end;
  with BtnCancel do
  begin
    Width := MaxWidth;
    Height := TmpLabel.Height*2;
    Left := Parent.ClientWidth - 1*(MaxWidth + ScaleX(ControlGap));
    Top := (Parent.ClientHeight - Height) div 2;
  end;
end;

Usage is like

// UninstallProgressForm is about to be shown
// Show modal dialog which allows to select additional components to uninstall
procedure InitializeUninstallProgressForm;
var
  Form: TSetupForm;
  i: Integer;
  NotebookPage: TNewNotebookPage;
  ModResult: Integer;
begin
  Form := CreateNotebookForm;
  for i := 1 to 4 do
  begin
    NotebookPage := AddPage(Form);
    with NotebookPage do
    begin
      Color := clWindow;
      with TLabel.Create(Form) do
      begin
        Parent := NotebookPage;
        SetBounds(0, 0, 50, 30);
        Autosize := true;
        Font.Size := 14;
        Caption := 'Label ' + IntToStr(i);
      end;
    end;
  end;

  ModResult := Form.ShowModal;
  if ModResult = mrYes then
    MsgBox('Continuing uninstall', mbInformation, MB_OK)
  else
  begin
    MsgBox('Cancelled', mbInformation, MB_OK);
    Abort;
  end;

  ...
end;

I'm using InnoSetup 5.4.2 and in the docs are several Uninstall Event Functions including: procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);

You should be able to create an input page within the [code] section.

Tags:

Inno Setup