Как форма может отправить сообщение своему владельцу? - PullRequest
2 голосов
/ 23 января 2010

Я написал приложение на основе MDI, в котором дочерние формы бывают разных типов. Теперь я столкнулся с ситуацией, когда мне нужна одна дочерняя форма, чтобы отправить сообщение другой дочерней форме с указанием обновить ее. Первая дочерняя форма не знает, отображается ли вторая дочерняя форма в данный момент.

Я думал о том, чтобы первая дочерняя форма (форма A) отправляла сообщение в основную форму MDI (форма 0), которая затем могла проверить список дочерних форм MDI, отображаемых в данный момент на экране. Если отображается желаемая форма (форма B), то основная форма может отправить второе сообщение в эту форму (форма B).

К сожалению, я не смог успешно написать код, который позволял бы первой дочерней форме сигнализировать о главной форме. Как дочерняя форма может отправить сообщение своему владельцу?

ТИА, No'am

Ответы [ 4 ]

4 голосов
/ 23 января 2010

Владелец формы не обязательно является другой формой. Свойство Владелец - это просто TComponent, который может быть любым, , включая nil . Но если владелец является формой, вы можете отправить ему следующее сообщение:

if Owner is TForm then
  SendMessage(TForm(Owner).Handle, am_Foo, 0, 0);

Возможно, вам не нужно знать владельца. Родительская форма MDI всегда является основной формой проекта, а основная форма всегда обозначается Application.MainForm . Отправьте сообщение на указатель этой формы.

SendMessage(Application.MainForm.Handle, am_Foo, 0, 0);

Список детей с MDI будет в Application.MainForm.MDIChildren . Ваши дочерние формы могут сами проверить этот список, а не делать это у родителей MDI. Вот функция, которую любая из ваших форм может использовать для поиска экземпляров любого дочернего класса MDI. (Если формы, которые хотят передать , не являются дочерними MDI, вы все равно можете использовать эту технику, но вместо Application.MainForm.MDIChildren найдите список Screen.Forms .)

function FindMDIChild(ChildClass: TFormClass): TForm;
var
  i: Integer;
begin
  for i := 0 to Pred(Application.MainForm.MDIChildCount) do begin
    if Application.MainForm.MDIChild[i].InheritsFrom(ChildClass) then begin
      Result := Application.MainForm.MDIChildren[i];
      exit;
    end;
  end;
  Result := nil;
end;

Ваш первый дочерний класс может использовать его так:

var
  SecondChild: TForm;
begin
  SecondChild := FindMDIChild(TSecondChild);
  if Assigned(SecondChild) then begin
    SendMessage(SecondChild.Handle, am_Foo, 0, 0);
  end;
end;

Когда оконные сообщения отправляются окнам в том же потоке, что и отправитель (что всегда имеет место для любых двух форм VCL), их обработчики вызываются немедленно, пока отправитель ожидает ответа. Это как обычный вызов функции, так что вы можете пропустить сообщения и сделать обычные функции в ваших классах форм. Тогда вы можете использовать такой код:

var
  SecondForm: TSecondForm;
begin
  SecondForm := TSecondForm(FindMDIChild(TSecondForm));
  if Assigned(SecondForm) then begin
    SecondForm.Foo(0, 0);
  end;
end;
2 голосов
/ 26 января 2010

Я не знаю, какая конкретно у вас проблема, поэтому это может не относиться к вашей ситуации.

Полагаю, ваша ситуация в FormA - отредактируйте какое-то значение , которое влияет на "рендеринг" FormB. Я обычно имею дело с такой ситуацией, изменяя значение, чтобы вызвать событие. Если необходимо изменить более одного компонента в системе, я использую событие «многоадресная рассылка».

Простой механизм многоадресной передачи выглядит следующим образом.

TMultiCastNotifyEventReceiver = class(TComponent)
private
  FEvent : TNotifyEvent
public
  property Event : TNotifyEvent read FEvent write FEvent; 
end;

TMultiCastNotifyEvent = class(TComponent)
private
  //TList or TObjectList to keep a list of Listener.
  //Housekeeping code to make sure you don't keep reference to dangling pointers (I derived from TComponent to have access to the FreeNotification mechanis
public
  procedure DoEvent(Sender : Tobject); //Same parameters as TNotifyEvent
  function AddListener(NotifyEvent : TNotifyEvent) : TMultiCastNotifyEventReceiver
end;

Таким образом, ваша форма A не должна знать, что она является родителем ... Не должна знать FormB. FormB не нужно знать FormA. Однако для того, чтобы это работало, требуется, чтобы FormA AND FormB должны знать Значение , а Значение должно знать, что необходимо отправить уведомление при его изменении. Обычно приводит к лучшей модульности и инкапсуляции.

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

2 голосов
/ 25 января 2010

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

Чтобы использовать эту модель, сначала создайте глобальный модуль (или добавьте к существующему) следующие объявления интерфейса:

type
  ISpecificSignal = interface
    {type CTRL-SHIFT-G here to generate a new guid}
    procedure PerformSignal(Handle:Integer);
  end;

Затем измените интерфейс формы MAIN, добавив следующее:

TYPE
  TMainForm = Class(TForm,ISpecificSignal)
    :
  private
    Procedure PerformSignal(Handle:Integer);
  end;

и при реализации процедуры PerformSignal выглядит следующим образом:

Procedure TMainForm.PerformSignal(Handle:Integer);
var
  i: Integer;
  Intf : ISpecificSignal;
begin
  for i := 0 to Pred(Application.MainForm.MDIChildCount) do begin      
    if Supports(Application.MainForm.MDIChild[i],ISpecificSignal,Intf) and
      (Application.MainForm.MDIChild[i].Handle <> Handle) then 
      Intf.PerformSignal(Handle);
end;

В вашей дочерней форме, которая в конечном итоге должна обрабатывать сигнал, выполните те же шаги, что и для основной формы, но измените PerformSignal, чтобы вызывать нужный код. Повторите при необходимости.

В форме, которая должна фактически запустить процесс, добавьте следующий код:

procedure DoSomething;
var
  Intf : ISpecificSignal;
begin
  if Supports(Application.MainForm,ISpecificSignal,Intf) then
    Intf.PerformSignal(Handle);
end; 

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

РЕДАКТИРОВАТЬ Добавлен дескриптор, чтобы избежать ситуации, когда существующая форма также нуждается в таких же уведомлениях от других форм.

0 голосов
/ 23 января 2010

Почему бы просто не отправить сообщение в Application.Mainform.Handle, затем в цикле формы Main от 0 до MDIChildcount и повторно отправить сообщение каждому. Затем ответьте на конкретное сообщение только в том классе формы, который вам нужен. Я надеюсь, что это отвечает вашим потребностям.

...