How can I prevent the main form capturing keystrokes in a TMemo on another non-modal form?

1.2k Views Asked by At

I have an app that opens a non-modal form from the main form. The non-modal form has a TMemo on it. The main form menu uses "space" as one of its accelerator characters.

When the non-modal form is open and the memo has focus, every time I try to enter a space into the memo on the non-modal form, the main form event for the "space" shortcut fires!

I have tried turning MainForm.KeyPreview := false while the other form is open but no dice.

Any ideas?

3

There are 3 best solutions below

3
On BEST ANSWER

Disable the menu item on the main form while the memo has focus, and re-enable it when the memo loses it. You can do this from the TMemo.OnEnter and TMemo.OnExit events.

procedure TOtherForm.Memo1Enter(Sender: TObject);
begin
  if Application.MainForm is TYourMainForm then
    TYourMainForm(Application.MainForm).MenuItemWithSpace. Enabled := False;
end;

procedure TOtherForm.Memo1Exit(Sender: TObject);
begin
  if Application.MainForm is TYourMainForm then
    TYourMainForm(Application.MainForm).MenuItemWithSpace. Enabled := True;
end;

The use of Application.MainForm and the typecast are to prevent hard-coding in a form variable name in the child form.

0
On

If you have many menu items, or many controls, could be very difficult to threat the problem for each of them. Instead, you could use a function in FormActivate() and FormDeActivate() methods of main window in order to clean and restore all shortcuts in a easy and simple way:

var sstore : TStrings;

procedure Tmain_form.FormActivate(Sender: TObject);
begin
    if (sstore <> NIL) then tratta_shortcuts_menu(main_menu, {read_shortcuts}FALSE, sstore)
end;

procedure Tmain_form.FormDeactivate(Sender: TObject);
begin
    tratta_shortcuts_menu(main_menu, {read_shortcuts}TRUE, sstore)
end;

procedure tratta_shortcuts_menu(menu : TMainMenu;bo_read_shortcuts : boolean;var sstore : TStrings);
{ if BO_READ_SHORTCUTS then 1) read shortcuts 2) save them on SSTORE (Shortcuts STORE) 3) delete them from menu;
ELSE restore all shortcuts from SSTORE  }

procedure sostituisci(im : TMenuItem);
const INDICATORE_SHORTCUT = '~ ';
begin
    if bo_read_shortcuts then begin
        if (im.ShortCut <> 0) then begin
            sstore.Add(im.name);
            sstore.Add(INDICATORE_SHORTCUT + menus.ShortCutToText(im.ShortCut));    
            im.ShortCut := 0
        end
    end
    else begin
        var i : smallint := sstore.indexof(im.Name);
        if (i <> -1) then begin
            im.ShortCut := menus.TextToShortCut(copy(sstore[i + 1], length(INDICATORE_SHORTCUT) + 1, MAXINT))
        end
    end
end;

procedure tratta(im : TMenuItem);
begin
    sostituisci(im);
    for var i : smallint := 0 to im.Count-1 do tratta(im.Items[i])
end;

begin
    if (menu = NIL) then exit;

    if bo_read_shortcuts then begin if (sstore = NIL) then sstore := TStringList.Create else sstore.Clear end;
    for var i : smallint := 0 to menu.Items.Count-1 do tratta(menu.Items[i]);
    if NOT bo_read_shortcuts then begin sstore.Free;sstore := NIL end
end;
1
On

This may be an old topic, but i had the same issue a moment ago and searched for a suitable solution. Your topic came up but not with a solution i would want to use.

My problem was: I have a main form with a lot of shortcuts (Backspace, Delete, etc) and a second form with an edit box. The edit box didn't get any key actions, which are handled by the main form shortcuts.

My solution: Set the child forms OnShortCut, wich will catch the shortcuts before they get interpreted by the main form with:

procedure ChildForm.FormShortCut(var Msg: TWMKey; var Handled: Boolean);
begin
  Handled := True;
  Self.DefaultHandler(Msg);
end;

That did the trick for me, the child form catches the shortcuts and handles them as common key messages. The edit box can be used as intended.