2009-12-05 2 views
3

Я использую стандартные действия «Вырезать», «Копировать», «Вставить» в основное меню. У них есть ярлыки Ctrl-X, Ctrl-C и Ctrl-V.Как предотвратить нежелательные ссылки от Colliding/Interacting в Delphi?

Когда я открываю модальную форму, например. FindFilesForm.ShowModal, то все ярлыки работают из формы.

Но когда я открываю немодальную форму, например. FindFilesForm.Show, тогда ярлыки не работают.

Я думаю, что эти действия должны работать, если FindFilesForm является активной формой. Это модальность не должна иметь ничего общего с этим, или я ошибаюсь в своих мыслях?

Никогда, как я могу получить ярлыки для работы в немодальной форме?


После ответа Кэри я исследовал его дальше. Это не проблема с некоторыми элементами управления, например. TMemo или TEdit.

Но это для некоторых других. В частности, те, где это происходит, включают:

  1. текст в TComboBox
  2. текст в
  3. управления TElTreeInplaceEdit TFindDialog, часть ElPack LMD в

Я буду видеть, если есть другие и добавьте их в список.

Все это относится к важным немодальным формам в моей программе.

Так что мне все еще нужно решение.


Хорошо. Мне действительно нужна помощь в этом. Таким образом, это становится первым вопросом, на который я вкладываю щедрость.

Мое обсуждение с Кэри, которое происходит через его ответ, и комментарии там описывают мою проблему более подробно.

И как я уже говорил в одном из этих замечаний, проблема, связанная, как представляется, обсуждается here.

Что мне нужно, это решение или обходной путь, который позволит Ctrl-X, Ctrl-C и Ctrl-V всегда работать в TComboBox и TFindDialog в немодальном окне. Если эти два решения будут решены, я уверен, что мой TElTreeInplaceEdit также будет работать.

Для создания простой тестовой программы требуется всего несколько минут, как описывает Кэри. Надеюсь, кто-то сможет это решить.

Просто будьте осторожны, что, кажется, есть что-то, что позволяет ему работать иногда, но не работать в другое время. Если я смогу выделить это более подробно, я сообщу об этом здесь.

Спасибо за любую помощь, которую вы можете мне предложить.


Mghie работал очень трудно, чтобы найти решение, и его OnExecute обработчик в сочетании с его обработчиком ActionListUpdate сделать трюк. Поэтому, для его усилий, я даю ему приемлемое решение и пункты вознаграждения.

Но его обработчик обновления ActionScript не прост, и вам нужно указать в нем все случаи, которые вы хотите обработать. Предположим, что есть также Ctrl + A для выбора всех или Ctrl-Y для отмены вы можете захотеть. Общая процедура будет лучше.

Итак, если вы столкнулись с этим вопросом в поиске ответа, попробуйте сначала ответ, который я предоставил, добавив обработчик IsShortcut. Он работал для меня и должен обрабатывать каждый случай и не нуждается в обработчиках OnExecute, поэтому это намного проще. Питер Ниже написал этот код, и Уве Молжан получает плату искателей.

Спасибо, Кэри, мги, Уве и Питер за то, что помогли мне решить эту проблему. Не мог бы это сделать без тебя. (Может быть, я мог бы это сделать, но, возможно, это заняло у меня 6 месяцев.)

+0

Если мой ответ решает вашу проблему, вероятно, вы также должны изменить вопрос на что-то вроде «Как заставить стандартные действия редактирования работать для всех элементов управления редактированием (комбинированные поля, редакторов на месте и т. Д.)?» – mghie

+0

Или «Как я могу сделать стандартные действия редактирования, не нарушать ярлыки для собственных элементов управления?». Или что-то в этом роде, чтобы было легче найти в поисках. – mghie

+0

Изменено название как вы предложили – lkessler

ответ

2

ОК, сначала первое: это не имеет никакого отношения к модальным или немодальным формам, это ограничение того, как работают компоненты действия Delphi (если вы хотите назвать это).

Позвольте мне доказать это на простом примере: создайте новое приложение с новой формой, оставьте на нем TMemo и TComboBox и запустите приложение. Оба элемента управления будут иметь предоставленное системой контекстное меню с командами редактирования и будут правильно реагировать на них. Они будут делать то же самое для ярлыков меню, за исключением Ctrl + A, который не поддерживается в поле со списком.

Теперь добавьте компонент TActionList с тремя стандартными действиями для вырезания, копирования и вставки. Все будет работать, никаких изменений в поведении.

Теперь добавьте главное меню и добавьте меню редактирования из шаблона. Удалите все команды, кроме тех, которые вырезают, копируют и вставляют. Установите соответствующие компоненты действий для элементов меню и запустите приложение. Обратите внимание на то, что в поле со списком все еще есть контекстное меню, а команды там все еще работают, но ярлыки больше не работают.


Проблема заключается в том, что стандартные редактировать действия были предназначены для работы только TCustomEdit управления с. Посмотрите на метод TEditAction.HandlesTarget() в StdActns.pas. Поскольку элементы управления редактированием в комбинированных блоках, редакторы inplace в элементах управления деревом или элементы управления редактирования в собственных диалогах не пойманы этим, они не будут обрабатываться. Команды меню всегда будут отключены, если один из этих элементов управления имеет фокус. Что касается ярлыков, работающих только некоторое время - это зависит от того, делает ли VCL в какой-то точке карту команд быстрого доступа к действию или нет. Если это не так, то они, наконец, достигнут собственной процедуры окна и инициируют команду редактирования. В этом случае ярлыки будут работать. Я предполагаю, что для модальных диалогов обработка действий приостановлена, поэтому поведение отличается от модальных и немодальных диалогов.

Чтобы обойти это, вы можете предоставить обработчики для OnExecute этих стандартных действий. Например, для команды Paste:

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:

я мог воспроизвести проблему - ключ к каким-то образом получить редактировать действия становятся инвалидами. Оглядываясь назад, это очевидно, но, конечно, необходимо также обновить свойство действий, необходимых для действий.

Пожалуйста, попробуйте с этим дополнительным обработчиком события:

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, поскольку он много работал, и его ответ правильный, но я внедрил a simpler solution that I added as an answer myself

+0

mghie: Спасибо за подробный анализ. Я работаю час, пытаясь проверить, что вы говорите. Я делаю то, что вы говорите в первых 4 абзацах, вплоть до вашей горизонтальной линии. Вы правы, что для ComboBox в основной форме ярлыки не работают. Однако, если вы поместите две кнопки в основной форме, которые откроют модальную и немодальную форму. И в каждой форме разместите ComboBox. Затем, открывая Combo Box в модальной форме, ярлыки DO работают, но на немодальном они этого не делают. Таким образом, он все еще имеет какое-то отношение к модальным/немодальным формам. – lkessler

+0

Да, я предполагаю, что из-за модальных диалогов, отключающих все остальные формы приложения, их компоненты действия не используются для определения того, какие ключевые события будут сопоставлены командам. Если ключевые события переходят в исходные окна без изменений, то ярлыки все еще работают. Однако, если вы поместите компоненты действия и меню в модальный диалог, это тоже не сработает. Вот почему я говорил, что модальность на самом деле не имеет значения, просто это влияет на то, какие списки действий используются для поиска потенциальных ярлыков. Побочный эффект, если хотите. – mghie

+0

Итак, теперь я помещаю в свою процедуру EditPaste1Execute (а также Copy1 и Cut1). Я впечатлен вашими знаниями об этом. Благодарю. Мне потребовалось бы много времени, чтобы понять это. Но даже при этом у меня все еще есть проблема. Сделайте следующее: В основной форме нажмите кнопку, чтобы открыть диалог поиска. Поместите в строку поиска, выберите ее часть и сделайте Ctrl-X. Теперь это работает! Но продолжайте возвращаться к основной форме. Нажмите кнопку, чтобы открыть немодальную форму с помощью комбинированного блока. Сделайте Ctrl-X из поля со списком. Это НЕ работает. Повторно открыть программу. Сначала поле Combo (работает), а затем найдите (не работает). – lkessler

1

Я создал очень простой пример с двумя формами в Delphi 2009 (обновление 3 и обновление 4), установленное на 64-разрядной версии Vista. Вторая форма Form2 отображается немодально (Form2.Show;). У меня есть TMemo на Form2. Ctrl-X, Ctrl-V, и Ctrl-C Работает нормально.

Это было до того, как я разместил TMainMenu на Form2.

Итак, я разместил TMainMenu на форме и добавил TActionList. Я создаю пункты меню «Редактировать» и добавляю элементы подменю «Копировать», «Вырезать», «Вставить». Я подключил их к стандартным действиям EditCopy, EditCut и EditPaste. Тем не менее, все работает отлично, как раньше. Я могу либо использовать пункты меню, либо Ctrl-C, Ctrl-X и Ctrl-V комбинации клавиш.

Здесь должно быть что-то еще.

+0

Вы правы, что этот простой пример работал. Мне придется либо построить простой пример, либо сломать мою программу, чтобы увидеть, что меня блокирует здесь. Между моим вопросом и вашим ответом я нашел других с той же проблемой на странице https://forums.codegear.com/thread.jspa?threadID=19946, и я попробовал решение Peter Below, но это не помогло меня. Это добавляет к вашему выводу, что здесь происходит что-то еще. Я буду исследовать дальше. Спасибо за ответ. Ты дал мне кое-что для продолжения. – lkessler

+0

Кэри, см. Обновление к моему вопросу. У TMemo и TEdits нет проблем. Попробуйте свою тестовую программу с помощью TComboBox или TFindDialog. – lkessler

+1

Любопытный. Я поместил TFindDialog и компонент TComboBox в свою основную форму, а также в немодальную форму. Вырезание и вставка работали с TFindDialog в обеих формах. Вырезание и вставка работали с TComboBox на основной форме, но не на немодальной форме. –

3

Я отправил a link to this question on my blog и получил предложение от Уве Мольжана, которого нет в StackOverflow.Уве использовал для запуска DelphiPool. Он указал мне на эту тему в borland.public.delphi.objectpascal:

Action List (mis)behavior.

Том Александр, который спросил оригинальный вопрос в этой теме, даже сказал:

Это происходит обычно, но не все время. Иногда после серии из вышеперечисленных ошибок поведение начинает действовать, как я ожидал.

, который является именно тем странным поведением, с которым я столкнулся, что эта проблема почти невозможна для отслеживания.

Peter Below ответил в этой ветке, что если есть встречные ярлыки, вам нужно предпринять шаги, чтобы убедиться, что активный элемент управления получает первую трещину в ярлыке.

Принимая его код (который был написан для проблемы с кадрами), и мне просто пришлось изменить «ctrl is TCustomFrame» на «ctrl is TControl», и он отлично работает. Итак, вот что необходимо:

public 
Function IsShortcut(var Message: TWMKey): Boolean; override; 

Function TMyform.IsShortcut(var Message: TWMKey): Boolean; 
Var 
    ctrl: TWinControl; 
    comp: TComponent; 
    i: Integer; 
Begin 
    ctrl := ActiveControl; 
    If ctrl <> Nil Then Begin 
    Repeat 
     ctrl := ctrl.Parent 
    Until (ctrl = nil) or (ctrl Is TControl); 
    If ctrl <> nil Then Begin 
     For i:= 0 To ctrl.componentcount-1 Do Begin 
     comp:= ctrl.Components[i]; 
     If comp Is TCustomActionList Then Begin 
      result := TCustomActionList(comp).IsShortcut(message); 
      If result Then 
      Exit; 
     End; 
     End; 
    End; 
    End; 
// inherited; { Originally I had this, but it caused multiple executions } 
End; 

До сих пор это работало во всех случаях для меня.

Ирония заключается в том, что он не работал для Тома Александра, оригинального вопросчика. Вместо этого он добавил процедуру к событию FrameEnter, чтобы установить фокус на соответствующую сетку для фрейма. Это может означать еще одно альтернативное решение моего вопроса, но мне не нужно исследовать это, поскольку решение Питера работает для меня.

Также обратите внимание, что Питер включает в свой ответ прекрасное резюме сложных шагов обработки ключей, которые стоит знать.

Но я хочу сейчас проверить mghie's edit на его ответ и посмотреть, является ли это также решением.

+0

Это интересное решение, у которого есть небольшая проблема, что он исправляет проблему с ярлыком * только *.Если, например, есть панель инструментов с кнопками «Вырезать», «Копировать» и «Вставить», они будут отключены, если сфокусированное окно редактирования не является «TCustomEdit», в то время как ярлыки для этих действий редактирования будут работать, что сделает пользовательский интерфейс непоследовательным. Но он решает проблему в вашем вопросе, так что +1. – mghie