2010. december 26., vasárnap

How to fix the MDI close button and window menu glitches


Problem/Question/Abstract:

I am trying to write a MDI application. I use a main form with a MainMenu. Every child form merges its main menu in the main form's main menu. If one of the child forms gets maximized, the close button (x button in the upper right corner) is grayed out but still works. If I merge the child form's main menu manually, the close button behaves in the same way.

Answer:

Solve 1:

I have tried the following patch to Menus.pas and it works wonders for me. The button no longer disappears or disable and the window menu functions after changes are made to it. I would like to know how well this works for them. Neither of these two fixes are 'hacks' into that they don't cause extra flashing or refreshing. They just fix the 'problematic' code in Menus.pas. The below snippits of code are based on D5.

procedure TMenuItem.RebuildHandle;
const
  cFAF = $04;
var
  I: Integer;
  LRepopulate: Boolean;
begin
  if csDestroying in ComponentState then
    Exit;
  if csReading in ComponentState then
    FStreamedRebuild := True
  else
  begin
    if FMergedWith <> nil then
      FMergedWith.RebuildHandle
    else
    begin
      I := GetMenuItemCount(Handle);
      LRepopulate := I = 0;
      while I > 0 do
      begin
        if (WordRec(LongRec(GetMenuState(Handle, I - 1, MF_BYPOSITION)).Lo).Lo and
          cFAF) = 0 then
        begin
          RemoveMenu(Handle, I - 1, MF_BYPOSITION);
          LRepopulate := True;
        end;
        Dec(I);
      end;
      if LRepopulate then
      begin
        if (FParent = nil) and (FMenu is TMainMenu) and (GetMenuItemCount(Handle) = 0)
          then
        begin
          DestroyMenu(FHandle);
          FHandle := 0;
        end
        else
          PopulateMenu;
        MenuChanged(False);
      end;
    end;
  end;
end;

function TMenu.DispatchPopup(AHandle: HMENU): Boolean;

  function IsMDIWindowMenu(AItem: TMenuItem): Boolean;
  begin
    Result := Assigned(Application.MainForm) and (Application.MainForm.FormStyle =
      fsMDIForm)
      and (Application.MainForm.WindowMenu = AItem);
  end;

var
  Item: TMenuItem;
  LRebuild: Boolean;
begin
  Result := False;
  Item := FindItem(AHandle, fkHandle);
  if Item <> nil then
  begin
    if not (csDesigning in Item.ComponentState) then
      Item.InitiateActions;
    Item.Click;
    LRebuild := Item.InternalRethinkHotkeys(False);
    LRebuild := Item.InternalRethinkLines(False) or LRebuild;
    if LRebuild then
      Item.RebuildHandle;
    if IsMDIWindowMenu(Item) then
      if SendMessage(Application.MainForm.ClientHandle, WM_MDIREFRESHMENU, 0, 0) <> 0
        then
        DrawMenuBar(Application.MainForm.Handle);
    Result := True;
  end
  else if not (csDesigning in ComponentState) and (Self is TPopupMenu) then
    Items.InitiateActions;
end;

You cannot recompile the standard packages, your license does not allow it and there are some units missing anyway. Copy the menus unit to your project directory, modify the copy, and compile it as part of your project. You can copy the produced DCU back into the LIB directory for other projects to use. This will work as long as you don't build with packages and don't change anything in the units interface.


Solve 2:

This piece of code fixes a bug present in all versions of Delphi, that occurs when switching between maximized MDI child windows, causing the close icon to be grayed in Delphi 3 & 4 or the system menu and max/min/close icons to vanish in Delphi 5. Tested in Delphi Client/Server 3, 4 & 5.

{$IFDEF VER100}
{$DEFINE DELPHI3&4}
{$ENDIF}

{$IFDEF VER120}
{$DEFINE DELPHI3&4}
{$ENDIF}

type
  TMDIChild = class(TForm)
    { ... }
  private
    procedure WMMDIActivate(var Msg: TWMMDIActivate); message WM_MDIACTIVATE;
    { ... }
  end;

procedure TMDIChild.WMMDIActivate;
var
  Style: Longint;
begin
  if (Msg.ActiveWnd = Handle) and (biSystemMenu in BorderIcons) then
  begin
    Style := GetWindowLong(Handle, GWL_STYLE);
    if (Style and WS_MAXIMIZE <> 0) and (Style and WS_SYSMENU = 0) then

{$IFDEF DELPHI3&4}
      SetWindowLong(Handle, GWL_STYLE, Style or WS_SYSMENU);
{$ELSE}
      SendMessage(Handle, WM_SIZE, SIZE_RESTORED, 0);
{$ENDIF}
  end;
  inherited;
end;

Nincsenek megjegyzések:

Megjegyzés küldése