Search code examples
delphidelphi-xe7

Displaying a disabled modal form


I'm trying to disable a TForm's descendant and showing it as a modal form.

procedure TForm1.Button1Click(Sender: TObject);
var
  Frm : TMyForm;
begin
  Frm := TMyForm.Create(nil);
  try
    Frm.Enabled := False;
    Frm.ShowModal();
  finally
    Frm.Free;
  end;
end;

At runtime, it raises the following error message:

Picture of the error saying "Cannot make a visible window modal"

Cannot make a visible window modal.


Solution

  • The OP wants to display a disabled form modally when the form should be displayed for read-only purposes.

    Disabling the form is the wrong thing to do.

    How do you display the information? If you are using TEdit, TMemo, or TRichEdit controls, you should simply set them to read only. Otherwise, if you have some combinations of various controls like radio buttons, you should disable each and every such control, not the form itself. I mean, surely you still want the Cancel button to be enabled?

    In addition, disabling the form instead of the actual controls will make the controls look enabled, which is very confusing! That's an important point.

    So what you need to do is to display the form normally (not disabled!) and then set its controls to their appropriate states when the dialog is shown.


    Just to emphasise my point about disabling the form vs its controls, consider this dialog box:

    Screenshot of a standard dialog box.

    If I do

    procedure TCustomViewFrm.FormShow(Sender: TObject);
    begin
      Enabled := False;
    end;
    

    then it looks like this when shown:

    Screenshot of the form displayed when disabled. It looks exactly like it did in the last screenshot! Nothing looks disabled!

    As you can see, every control looks very enabled indeed, but no control responds to mouse or keyboard input. This is very confusing and a horribly bad UX.

    In fact, you cannot even close the dialog box using its title-bar Close button or Alt+F4. You cannot close it using its system menu, either. In fact, you cannot close it at all, because to close a window, it must respond to user input, and a disabled window doesn't do that. (You cannot move the window, either.)

    Instead, if we disable all controls (except the Cancel button),

    procedure DisableControl(AControl: TWinControl);
    begin
      for var i := 0 to AControl.ControlCount - 1 do
      begin
        if
          (AControl.Controls[i] is TCustomButton)
            and
          (TCustomButton(AControl.Controls[i]).ModalResult = mrCancel)
        then
          Continue;
        if AControl.Controls[i] is TWinControl then
          DisableControl(TWinControl(AControl.Controls[i]));
        AControl.Controls[i].Enabled := False;
      end;
    end;
    
    procedure TCustomViewFrm.FormShow(Sender: TObject);
    begin
      DisableControl(Self);
    end;
    

    you get this nice UI:

    Screenshot of the form with all its controls except for the Cancel button disabled, including the labels.

    Not only is it very clear that all controls are disabled, the user can also close the dialog box without killing your application using the Task Manager.