Конференция "Компоненты" » Создание компонента. MDI приложение.
 
  • Decoding (24.07.08 14:50) [0]
    Добрый день.
    Заканчиваю компонент, который представляет собой некий аналог виндового TaskBar'а, для дочерних окон MDIForm. Есть ряд задач (3 штуки), с которыми пока немогу справиться...

    1) Мне необходимо, чтобы во время разработки приложения этот компонент можно было "бросить" только на форму со стилем  FormStyle = MDIForm. Понятно, что нужно чтото написать в методе Create этого компонента, но вот что?

    2) Вопрос примерно из той же серии, что и первый. Нужно, чтобы на форме мог быть только один подобный компонент. Каким способом этого можно добиться?

    3) Третья задача заключается в отслеживании изменения заголовка дочерней MDI формы в RunTime, для того, чтобы соответствующим образом изменить текст на кнопке, ассоциированной с данным окном. Во время загрузки приложения компонент устанавливает хук
    procedure TMDIPanel.Loaded;
    begin
      inherited;
      ...
      WndProcHook := SetWindowsHookEx( WH_CALLWNDPROC, @CallWndHook, 0, GetCurrentThreadID );
      ...
    end;


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

    Если у кого есть идеи, с удовольствием выслушаю... Всем спасибо.
  • Медвежонок Пятачок © (24.07.08 15:00) [1]
    Понятно, что нужно чтото написать в методе Create этого компонента, но вот что?

    проверку что оунер это то что тебе надо
  • Olegz77 © (24.07.08 15:32) [2]
    1), 2)

    constructor TMegaTaskBar.Create(aOwner: TComponent);
    var I: Integer;
    begin
     inherited Create(aOwner);
     if Owner is TForm then
     begin
       if (TForm(Owner).FormStyle <> fsMDIForm) then
         raise Exception.Create('Руки оторву!!!');
     for I := 0 to TForm(Owner).ComponentCount - 1 do
       if TForm(Owner).Components[I] is TMegaTaskBar then
         raise Exception.Create('Ноги выдерну!!!');
     end;
    end;

  • DimaBr © (24.07.08 16:11) [3]
    if Owner is TForm then


    1. предлагаю заменить на поиск вверх подобно GetParentForm
    3. Зачем вешать хук ? На изменения заголовка формы CM_TEXTCHANGED послать сообщени главной форме

  • Decoding (24.07.08 16:14) [4]
    to Медвежонок Пятачок, Olegz77

    подобные идеи у меня уже были, но они не катят... Olegz77, приведенный тобой код не работает в дизайнере. Я спокойно могу положить свою панель на любой другой компонент (например TPanel), и добавить их сколько угодно...

    несколько иная картина наблюдается при трассировке приложения во время запуска. Не каком бы компоненте не лежала моя панель, условие if Owner is TForm then всегда срабатывает (вообще не врубаюсь почему...)

    Может есть другие идеи? или у Вас есть готовые примеры, в которых это реализовано и 100% работает верно? если есть, поделитесь, скиньте на decoding@yandex.ru
  • Медвежонок Пятачок © (24.07.08 16:16) [5]
    Я спокойно могу положить свою панель на любой другой компонент (например TPanel), и добавить их сколько угодно...

    И чего?
    Оунером то все равно не панель будет
  • Decoding (24.07.08 16:24) [6]

    > if Owner is TForm then
    > 1. предлагаю заменить на поиск вверх подобно GetParentForm
    > 3. Зачем вешать хук ? На изменения заголовка формы CM_TEXTCHANGED
    > послать сообщени главной форме


    1. попробую... хотя есть предположение, что в дизайнере это работать тоже не будет

    2. в компоненте 3 хука, на оконную процедуру, мышь и клаву... они решают много задач, например отслеживание изменение активной формы, создание, сворачивание и восстановление форм, для того, чтобы "нажать" на панели нужную кнопку. И уж раз они есть, то хотелось бы и эту задачу решить с их помошью... Понимаешь, мне бы не хотелось возлагать эту задачу на разработчика (в противном случае можно сделать у компонента публичный метод для этих целей). Вся идея в том и заключается, чтобы возложить эту задачу на компонент, и чтобы разработчик в дальнейшем этим не замарачивался...
  • DimaBr © (24.07.08 16:26) [7]

    constructor TMegaTaskBar.Create(AOwner: TComponent);
    var C: TComponent;
    begin
     C := AOwner;
     while Assigned(C.Owner) do C := C.Owner;
     if C is TForm then begin
      if (TForm(C).FormStyle <> fsMDIForm) then raise Exception.Create('Руки оторву!!!');
    end;

  • Decoding (24.07.08 16:27) [8]

    > И чего?
    > Оунером то все равно не панель будет


    я же пояснил, в дизайнере этот код не работает вообще (а мне именно в дизайнере и надо), а в RunTime условие if Owner is TForm then непонятно почему срабатывает всегда, даже если компонент лежит не на форме, а на панели.
  • DimaBr © (24.07.08 16:28) [9]
    Странно возлагать на MDIPanel управление заголовком окна !!!
  • Медвежонок Пятачок © (24.07.08 16:28) [10]
    интересно как можно в дизайне умудриться сделать оунером не форму на которую кидают мегатаскбар
  • Медвежонок Пятачок © (24.07.08 16:29) [11]
    я же пояснил, в дизайнере этот код не работает вообще (а мне именно в дизайнере и надо),

    Это еще с каких щей он не работает в дизайне?
  • DimaBr © (24.07.08 16:31) [12]
    В конструкторе Owner = nil, поскольку компонент только создаётся. Вот тут и присваивается.

    procedure TComponent.Insert(AComponent: TComponent);
    begin
     if FComponents = nil then FComponents := TList.Create;
     FComponents.Add(AComponent);
     AComponent.FOwner := Self;
    end;



    Скорее всего автор имел ввиду AOwner
  • Медвежонок Пятачок © (24.07.08 16:32) [13]
    а в RunTime условие if Owner is TForm then непонятно почему срабатывает всегда, даже если компонент лежит не на форме, а на панели.

    Оунером и будет форма. Парентом будет панель.
    Ку?
  • Медвежонок Пятачок © (24.07.08 16:34) [14]
    В конструкторе Owner = nil, поскольку компонент только создаётся. Вот тут и присваивается.

    Абалдеть.
  • Медвежонок Пятачок © (24.07.08 16:34) [15]
    А в параметре конструктора тоже нил "потому что тока создается"?
    :)
  • Decoding (24.07.08 16:38) [16]

    > constructor TMegaTaskBar.Create(AOwner: TComponent);
    > var C: TComponent;
    > begin
    >  C := AOwner;
    >  while Assigned(C.Owner) do C := C.Owner;
    >  if C is TForm then begin
    >   if (TForm(C).FormStyle <> fsMDIForm) then raise Exception.
    > Create('Руки оторву!!!');
    > end;


    и что сделает этот код? он пройдет по всей цепочке, дойдя до самого ее верхнего уровня, найдет форму, и условие
    if C is TForm then

    обязательно сработает. мне же нужно, чтобы если я кидаю компонент например на панель, компонент не позволил бы мне этого сделать... Причем обращаю ваше внимание, это нужно делать именно в DesignTime.
  • Медвежонок Пятачок © (24.07.08 16:40) [17]
    Дядя! При чем здесь твоя панель?
    Оунером будет форма на которой лежит панель.
    И у формы есть знание мдичайлд ли она.
    И список контролов и их типы у нее есть.
  • Медвежонок Пятачок © (24.07.08 16:44) [18]
    мне же нужно, чтобы если я кидаю компонент например на панель, компонент не позволил бы мне этого сделать...

    Установку парента лови. А вообще лучше подумай от кого наследоваться чтобы на панель не попасть автоматом
  • DimaBr © (24.07.08 16:45) [19]
    Вы сами себе противоречите

    > 1) Мне необходимо, чтобы во время разработки приложения этот компонент можно было "бросить" только на форму со стилем  FormStyle = MDIForm.



    >  мне же нужно, чтобы если я кидаю компонент например на панель, компонент не позволил бы мне этого сделать
  • DimaBr © (24.07.08 16:49) [20]

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


    Перекройте ValidateContainer панели

    procedure TComponent.InsertComponent(AComponent: TComponent);
    begin
     AComponent.ValidateContainer(Self);

  • Медвежонок Пятачок © (24.07.08 16:49) [21]
    мне же нужно, чтобы если я кидаю компонент например на панель, компонент не позволил бы мне этого сделать...

    От кого наследуемся и зачем? Если не хотим попасть на панель.
  • Медвежонок Пятачок © (24.07.08 16:54) [22]
    судя по тексту штука невизуальная. но отнаследована зачем-то от тконтрола.
  • Медвежонок Пятачок © (24.07.08 17:03) [23]
    один экземпляр на форме контрлируется конструктором через оунера.
    оунер чтоб был мдичайлд - там же.
    чтобы не попасть на панель - принудительно ставить парентом оунера или вообще наследваться от ткомпонент.

    все три задачи решены.
  • Decoding (24.07.08 17:13) [24]
    Компонент наследуется от TCustomPanel.

    то, что Owner будет формой, а Parent nil, я уже установил опытным путем)


    > Перекройте ValidateContainer панели


    Сейчас попробую
  • Decoding (24.07.08 17:15) [25]

    > судя по тексту штука невизуальная. но отнаследована зачем-
    > то от тконтрола.

    Это с чего такие выводы?????


    > ...для того, чтобы "нажать" на панели нужную кнопку....


    визуальный компонент
  • Медвежонок Пятачок © (24.07.08 17:20) [26]
    ну хорошо.
    а почему я как пользователь компонента не должен мочь положить визуальное на панель?
  • Decoding (24.07.08 17:56) [27]
    теоретически можешь... но в MDI приложениях в этом все равно нет смысла...
  • Decoding (24.07.08 18:05) [28]
    Первый вопрос решен через переопределение метода SetParent... осталось 2 и 3... Есть идеи?
  • Медвежонок Пятачок © (24.07.08 18:44) [29]
    через канструктор. сказали же уже
  • Decoding (24.07.08 20:26) [30]
    2 вопрос закрыт, спасибо всем, кто откликнулся...
    если будут идеи по поводу 3-его вопроса, также буду благодарен
  • Сергей М. © (24.07.08 20:34) [31]

    > Есть ряд задач (3 штуки)


    Спасибо. Мы тут, валенки, совсем разучились считать "штуки".


    > во время разработки приложения этот компонент можно было
    > "бросить" только на форму со стилем  FormStyle = MDIForm


    Ну , положим, реализовал ты  это.
    А потом тупой юзер взял да изменил ФормСтайл на иной стиль.
    Как тебе эти грабли ?)
  • Decoding (24.07.08 23:51) [32]

    > Спасибо. Мы тут, валенки, совсем разучились считать "штуки".

    Ну никак не мог за это не зацепиться...


    > Как тебе эти грабли ?)

    да, что есть, то есть. упустил этот момент из виду. не страшно, немного изменю логику...

    DimaBr
    не получается реализовать перехват сообщения CM_TEXTCHANGED в компоненте. Если это (procedure TextChanged(var Message: TMessage); message CM_TEXTCHANGED;) написать в самом проекте, то все работает, но тоже самое в компоненте работать отказывается... и хотя в инете подобных примеров немало, пока непонимаю, что я такого наделал, что у меня не работает.
  • DimaBr © (25.07.08 09:01) [33]

    >  написать в самом проекте, то все работает, но тоже самое
    > в компоненте работать отказывается...

    Покажите код
  • Thunderchild (25.07.08 09:03) [34]
    2) Вопрос примерно из той же серии, что и первый. Нужно, чтобы на форме мог быть только один подобный компонент. Каким способом этого можно добиться?

    А почему не один во всем приложении... Может проще так:

    ...
    var
     flag: boolean;
    ...
    constructor TMDIPanel.Create(AOwner: TComponent);
    begin
    ...
      if flag then
        raise Exception.Create('')
      else
       flag := true;
    end;

    ...

    destructor TMDIPanel.Destroy;
    begin
      flag := False;
    ...
    end;
  • Сергей М. © (25.07.08 12:59) [35]

    > Ну никак не мог за это не зацепиться.


    )

    Да просто, извини уж, к месту вспомнилось:

    "Три штуки. Размер сорок два, сорок три, сорок пять" (В) Василий Алибабаевич
  • Decoding (25.07.08 14:29) [36]
    С третьим вопросом выкрутился через WM_MDIREFRESHMENU.
    Спасибо всем, кто проявил участие и помогал.
    Кому интересно, что получается, скачайте демку http://slil.ru/26008874
  • Interior (26.07.08 18:36) [37]
    Не качается оттуда что-то.
    Может выложишь на webfile.ru ?
  • Interior (26.07.08 18:37) [38]
    У меня вообще-то есть компонент на эту тему.
    Напиши мыло - скину
  • Decoding (27.07.08 14:38) [39]
    http://webfile.ru/2117620
    скинь сюда - decoding@yandex.ru

    не все с компонентом проходит гладко... есть вопросы, и если сам не решу их в ближайшее время, снова обращусь за помощью, т.ч. тема пока не закрыта
  • Decoding (28.07.08 10:35) [40]
    Не смотря на то, что третий вопрос я решил через перехват сообщения WM_MDIREFRESHMENU, этот вариант меня не очень устраивает.

    я набросал небольной демонстрационный пример, в котором есть компонент и приложение, которое его использует. скачать можно тут:
    http://slil.ru/26014035
    http://webfile.ru/2119137
    Компонент ставит хук, и имеет процедуру procedure TextChanged(var Message: TMessage); message CM_TEXTCHANGED;. Но ни тот ни другой метод это сообщение не отлавливают.

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

    Если не сложно, посмотрите компонент, и подскажите, что не так сделано?
  • DimaBr © (28.07.08 12:44) [41]
    Вот откуда прилетает WM_MDIREFRESHMENU

    procedure TCustomForm.CMTextChanged(var Message: TMessage);
    begin
     inherited;
     if (FormStyle = fsMDIChild) and (Application.MainForm <> nil) and
       (Application.MainForm.ClientHandle <> 0) then
       SendMessage(Application.MainForm.ClientHandle, WM_MDIREFRESHMENU, 0, 0);
    end;

  • DimaBr © (28.07.08 12:46) [42]
    А хук вы вешаете на главное окно (точнее на окно где лежит TDemoPanel)
  • Decoding (28.07.08 13:17) [43]
    Да это понятно, видел я это... Оно мне не совсем подходит, т.к. lParam и wParam нулевые, и я не знаю у какого именно окна произошли изменения. Приходится делать цикл, и пробегаться по всем окнам, проверяя, в каком именно изменился Caption. Это неэффективно при большом количестве окон, особенно если Caption при этом меняется только у одного... Вот почему мне нужно перехватывать именно CM_TEXTCHANGED или WM_SETTEXT. Но до MDI формы они не доходят (проверял через Spy++). Можно ли это как то обойти (может есть другое сообщение), чтобы отказаться от цикла? Чтобы сразу знать, у какого именно дочернего MDI окна произошло изменение...

    Да и вообще странно, что эти сообщения не доходят (с MDI окнами вообще много странностей, как выяснилось во время работы над компонентом...), на фоне например того, что сообщение WM_SETICON приходит (и перехватывается) нормально...
  • Decoding (28.07.08 13:21) [44]

    > А хук вы вешаете на главное окно (точнее на окно где лежит
    > TDemoPanel)

    WndProcHook := SetWindowsHookEx( WH_CALLWNDPROC, @CallWndHook, 0, GetCurrentThreadID );
    хук вроде бы вешается на весь поток, а не на конкретное окно... и опять же повторюсь, проверял какие сообщения получает и главное окно, и то, на котором лежит панель, при помощи утилитки Spy++. Не приходят сообщения CM_TEXTCHANGED и WM_SETTEXT приложению...
  • DimaBr © (28.07.08 13:24) [45]
    Полная чушь !
    Поставьте BreakPoint в
    procedure TCustomForm.CMTextChanged(var Message: TMessage);


    В WitchList введите Name и увидите что изменение текста происходит в Form2, которая и является MDIChild
  • Decoding (28.07.08 13:33) [46]
    И что мне от этого факта? До хука это сообщение не доходит... А WM_MDIREFRESHMENU приходит без параметров... Вопрос не в том, что в TCustomForm это срабатывает нормально, а в том, как мне эти сообщения получить...
  • DimaBr © (28.07.08 15:10) [47]
    Вы хотите перехватить в компоенете изменения в другом окне ?
  • Decoding (28.07.08 15:38) [48]
    Именно так. Если уточнить задачу, то: перехватывать компонентом изменение Caption в MDIChild форме.
  • DimaBr © (29.07.08 08:39) [49]
    Ваша задача решается просто. Пишется наследник от TForm, в котором реализуются все ваши примбабасы, от которого в последствии и наследуются все MDIChild формы приложения. И не нужно засорять программу хуками.
  • Decoding (31.07.08 22:53) [50]
    Еще вопросик возник. Как в момент создания компонента (runtime) определить, был ли он брошен на форму как компонент, или создается динамически? Дело в том, что часть важного для работы компонента кода находится в процедуре Loaded. А она не отрабатывает при динамическом создании компонента... Не предлагайте перенести код из Loaded в Create. Если знаете, как определить, каким образом создается компонент, подскажите.
  • DimaBr © (01.08.08 10:31) [51]
    Никак, когда компонент создаётся он ещё никуда не брошен.
    Loaded возникает после окончания чтения из ресурса, так что при первой заброске никакого Loaded нет, поскольку читать нечего.
    Design-Time от Run-Time можно отлечить проверив свойство ComponentState
    if csDesigning in ComponentState then

  • Игорь Шевченко © (04.08.08 17:48) [52]

    > Как в момент создания компонента (runtime) определить, был
    > ли он брошен на форму как компонент, или создается динамически?
    >  


    Срабатывает Loaded или нет
  • DimaBr © (05.08.08 08:41) [53]
    Многоуважаемый Игорь, у компонента брошеного на форму тоже не срабатывает Loaded (в момент заброски).
  • Игорь Шевченко © (05.08.08 10:23) [54]
    DimaBr ©   (05.08.08 08:41) [53]

    Насколько я понял автора, ему нужно определить (в компоненте) загружен ли он из ресурсов или создан в коде. Причем, определить ему надо в run-time, а не в desing-time. Насколько мне известно, это определяется срабатыванием метода TComponent.Loaded

    Во время работы в design-time этот вопрос попросту не имеет смысла - так как компонент может быть только "брошен на форму" но никак не создан. Если только в каком-то хитром эксперте компонент будет создан, как побочное действие, но это уже настолько отдает странным, что мне с трудом представляется, что автор имеет в виду эту ситуацию.

    По сабжу - определять, каким путем заполнены свойства компонента есть нонсенс и ламерство.
  • Decoding (18.09.08 12:03) [55]
    По поводу ламерства можно было бы и воздержаться. Меня не интересовало, каким образом заполняются свойства компонента, меня интересовало каким образом компонент создавался.

    Однако это уже в прошлом, компонент уже написан. Все, кому интересно, могут скачать его и протестировать.
    exe - http://decoding.narod.ru/download/mycomponent/mdipanel_exe.zip
    source - http://decoding.narod.ru/download/mycomponent/mdipanel.zip

    Также мне было бы интересно узнать ваше мнение об этом компоненте. Можете писать здесь, или мне на почту (адрес смотрите на сайте).
  • Игорь Шевченко © (18.09.08 23:32) [56]

    > Также мне было бы интересно узнать ваше мнение об этом компоненте


    Слишком сложно и непонятно. Собственно, непонятны цели и задачи, каковые должен решать этот компонент - если аналог таскбара для окон MDI то есть масса готовых решений (даже я когда-то пример делал), но оно без хуков как-то работало.
  • Decoding (19.09.08 09:47) [57]
    Ну вот пример. У нас открыта MDIChild форма, на ней есть мемо, которое имеет фокус. При этом, когда форма окажется свернутой, нужно подавлять ввод с клавиатуры (чтобы м мемо ничего не печаталось, что логично при свернутой форме). Каким образом реализовать это без хуков?

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

    это только один из примеров, но думаю он достаточно наглядный.
  • Игорь Шевченко © (22.09.08 16:35) [58]
    "У нас открыта MDIChild форма, на ней есть мемо, которое имеет фокус. При этом, когда форма окажется свернутой, нужно подавлять ввод с клавиатуры (чтобы м мемо ничего не печаталось, что логично при свернутой форме). Каким образом реализовать это без хуков?
    "

    Поведение ввода в компоненты на свернутой форме определяет система, как она позволяет (вводить или нет), так оно и должно быть и вмешиваться в ее работу - это только изумлять пользователя.

    Насколько я видел исходники этого компонента, я не увидел подмены ClientWindowProc у главной MDI-формы, а через эту подмену можно многие вещи реализовать довольно просто.
    Это в качестве подсказки.
  • Decoding (22.09.08 23:57) [59]
    > Это в качестве подсказки

    Спасибо. Будет свободное время, проработаю и этот вариант.

    >Поведение ввода в компоненты на свернутой форме определяет система, как она позволяет (вводить или нет), так оно и должно быть и вмешиваться в ее работу - это только изумлять пользователя.

    Утверждение отнюдь не лишено смысла. Однако в каждом правиле бывают исключения. Если провести аналогию, то врятли вы адекватно отреагируете на то, что минимизированный блокнот продолжает реагировать на ввод с клавиатуры. И врятли вы обрадуетесь, развернув блокнот, увидев там "левый" текст. Не вижу причин, по которым в MDI должно быть по-другому. Спорить на эту тему можно долго... И каждое мнение имеет право на существование... Я посчитал, что так будет правильнее...
  • Игорь Шевченко © (23.09.08 10:57) [60]

    > Я посчитал, что так будет правильнее...


    Тогда об этом надо большими буквами предупреждать пользователя или, по меньшей мере сделать отключение этой возможности для совместимости с штатным поведением системы.
  • имя (26.01.09 07:22) [61]
    Удалено модератором
 
Конференция "Компоненты" » Создание компонента. MDI приложение.
Есть новые Нет новых   [134465   +63][b:0][p:0.002]