ОК, первым делом: это не имеет ничего общего с модальными или немодальными формами, это ограничение работы компонентов действия Delphi (если вы хотите это так называть).
Позвольте мне доказать это на простом примере: создайте новое приложение с новой формой, поместите в него TMemo
и TComboBox
и запустите приложение. Оба элемента управления будут иметь системное контекстное меню с командами редактирования и будут корректно реагировать на них. Они будут делать то же самое для ярлыков меню, за исключением Ctrl + A , который не поддерживается для поля со списком.
Теперь добавьте компонент TActionList
с тремя стандартными действиями для Вырезать, Копировать и Вставить. Все будет работать, никаких изменений в поведении.
Теперь добавьте главное меню и добавьте меню редактирования из шаблона. Удалите все команды, кроме команд «Вырезать», «Копировать» и «Вставить». Установите соответствующие компоненты действий для пунктов меню и запустите приложение. Обратите внимание, что в поле со списком все еще есть контекстное меню и команды, которые там по-прежнему работают, но ярлыки больше не работают.
Проблема в том, что стандартные действия редактирования были разработаны для работы только с элементами управления TCustomEdit
. Взгляните на метод TEditAction.HandlesTarget()
в StdActns.pas. Поскольку элементы управления для редактирования в комбинированных полях, редакторы на месте в древовидных элементах управления или элементы управления для редактирования в собственных диалоговых окнах этим не учитываются, они не будут обрабатываться. Команды меню всегда будут отключены, когда один из этих элементов управления имеет фокус. Что касается ярлыков, работающих только некоторое время - это зависит от того, отображает ли VCL в какой-то момент карты сочетания клавиш для действий или нет. Если этого не произойдет, то они, наконец, дойдут до процедуры собственного окна и инициируют команду редактирования. В этом случае ярлыки все равно будут работать. Я предполагаю, что для модальных диалогов обработка действий приостановлена, поэтому поведение между модальными и немодальными диалогами отличается.
Чтобы обойти это, вы можете предоставить обработчики для OnExecute
этих стандартных действий. Например, для команды «Вставить»:
procedure TMainForm.EditPaste1Execute(Sender: TObject);
var
FocusWnd: HWND;
begin
FocusWnd := GetFocus;
if IsWindow(FocusWnd) then
SendMessage(FocusWnd, WM_PASTE, 0, 0);
end;
и аналогичные обработчики для команды Cut (WM_CUT
) и команды Copy (WM_COPY
). Выполнение этого в небольшом демонстрационном приложении заставляет вещи снова работать для поля со списком. Вы должны попробовать в вашем приложении, но я предполагаю, что это поможет. Труднее правильно включить и отключить команды главного меню для всех собственных элементов управления редактированием. Возможно, вы могли бы отправить сообщение EM_GETSEL
, чтобы проверить, есть ли выделенный элемент управления для редактирования.
Edit:
Дополнительная информация, почему поведение отличается в комбинированных полях в модальных и немодальных диалогах (анализ сделан в Delphi 2009): интересный код в TWinControl.IsMenuKey()
- он пытается найти компонент действия в одном из действий списки родительской формы сфокусированного элемента управления, который обрабатывает ярлык. Если это не удается, он отправляет сообщение CM_APPKEYDOWN
, что в конечном итоге приводит к той же проверке, выполняемой со списками действий основной формы приложения. Но вот в чем дело: это будет сделано, только если включен дескриптор окна главной формы приложения (см. TApplication.IsShortCut()
код). Теперь вызов ShowModal()
для формы отключит все другие формы, поэтому, если модальное диалоговое окно не содержит самого , действие с таким же ярлыком будет работать с собственной обработкой ярлыков.
Edit:
Я мог бы воспроизвести проблему - ключ в том, чтобы каким-то образом отключить действия редактирования. В ретроспективе это очевидно, свойство Enabled
действий, разумеется, тоже нужно обновить.
Пожалуйста, попробуйте этот дополнительный обработчик событий:
procedure TForm1.ActionList1Update(Action: TBasicAction; var Handled: Boolean);
var
IsEditCtrl, HasSelection, IsReadOnly: boolean;
FocusCtrl: TWinControl;
FocusWnd: HWND;
WndClassName: string;
SelStart, SelEnd: integer;
MsgRes: LRESULT;
begin
if (Action = EditCut1) or (Action = EditCopy1) or (Action = EditPaste1) then
begin
IsEditCtrl := False;
HasSelection := False;
IsReadOnly := False;
FocusCtrl := Screen.ActiveControl;
if (FocusCtrl <> nil) and (FocusCtrl is TCustomEdit) then begin
IsEditCtrl := True;
HasSelection := TCustomEdit(FocusCtrl).SelLength > 0;
IsReadOnly := TCustomEdit(FocusCtrl).ReadOnly;
end else begin
FocusWnd := GetFocus;
if IsWindow(FocusWnd) then begin
SetLength(WndClassName, 64);
GetClassName(FocusWnd, PChar(WndClassName), 64);
WndClassName := PChar(WndClassName);
if AnsiCompareText(WndClassName, 'EDIT') = 0 then begin
IsEditCtrl := True;
SelStart := 0;
SelEnd := 0;
MsgRes := SendMessage(FocusWnd, EM_GETSEL, WPARAM(@SelStart),
LPARAM(@SelEnd));
HasSelection := (MsgRes <> 0) and (SelEnd > SelStart);
end;
end;
end;
EditCut1.Enabled := IsEditCtrl and HasSelection and not IsReadOnly;
EditCopy1.Enabled := IsEditCtrl and HasSelection;
// don't hit the clipboard three times
if Action = EditPaste1 then begin
EditPaste1.Enabled := IsEditCtrl and not IsReadOnly
and Clipboard.HasFormat(CF_TEXT);
end;
Handled := TRUE;
end;
end;
Я не проверял, что собственный элемент управления редактирования доступен только для чтения, возможно, это можно сделать, добавив следующее:
IsReadOnly := GetWindowLong(FocusWnd, GWL_STYLE) and ES_READONLY <> 0;
Примечание: я дал mghie ответ, так как он проделал большую работу, и его ответ правильный, но я реализовал более простое решение, которое я добавил в качестве ответа сам