Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C++ Builder > Создание компонента. Сообщения...


Автор: dencraft 16.5.2011, 16:52
Что-то я совсем запутался smile. 
Пишу новый компонент на основе базового. Нужно переопределять стандартные действия над компонентом UpDown. При нажатии на мышу должно отрабатывать мое событие, а событие компонента не должно. Вроде сделал - все работает. Но как только добавил второй экземпляр компонента на форму - начались проблемы. На сообщения стал отвечать именно второй компонент, первый стал работать стандартно. 

Внутри компонента я переопределил методы Application->OnMessage и Form->WindowProc. Ламерский вопрос но все же....В чем различие между этими обработчиками Application->OnMessage и Form->WindowProc. Так есть ли разница где размещать код в этих обработчиках. И как определять в каком я нахожусь объекте. Узнать например какой у меня HWND компонента к которому пришло сообщение

кусок кода:
Код


void __fastcall TUpDownEx::WndProc1(Messages::TMessage &Message)
{
   // перехватываем сообщения
   switch (Message.Msg)
   {
      case WM_SETCURSOR:
      {
         FDataDebug = Message.WParam;

          // Проверяем, мышь внутри ли компонента?
         fInComponent = ((HWND) Message.WParam == this->Handle) ? true : false;
          // ...
      }
   }
OwnerWndProc(Message);
}


и дальше я реализую новые возможности тут

Код

void __fastcall TUpDownEx::AppMessage( tagMSG &Msg, bool &Handled)
{
 switch (Msg.message)
 {
   case WM_LBUTTONDOWN:      // Мышь: Нажатие левой кнопки
   {
    if (fInComponent)
    {...
      Handled=true;
    }
   }
 }
}




Автор: dencraft 16.5.2011, 17:59
Вот еще. Кажется начинаю понимать где собака зарылась....
Код

__fastcall TUpDownEx::TUpDownEx(TComponent* Owner)
   : TUpDown(Owner)
{
   if (!ComponentState.Contains(csDesigning)) // Только в режиме выполнения
   {
      //Получить собственника компонента
     FOwnerForm=(TForm*)Owner;

      // Переопределить функцию окна собственника
     OwnerWndProc=FOwnerForm->WindowProc;
     FOwnerForm->WindowProc=WndProc1;

      // Переопределить событие приложения
     FOldAppMessage = Application->OnMessage;
     Application->OnMessage = AppMessage;

   }
}

//---------------------------------------------------------------------------
__fastcall TUpDownEx::~TUpDownEx(void)
{
   if (!ComponentState.Contains(csDesigning)) // Только в режиме выполнения
   {
      //Востановить функцию окна собственника
     FOwnerForm->WindowProc=OwnerWndProc;
      // Востановить событие приложения
     Application->OnMessage=FOldAppMessage;


   }
}


Получается когда выполняется конструктор, он изменяет указатели на методы обработки сообщений. Но тогда как быть со вторым компонентом и последующими? При выполнении конструктора второго компонента, он затирает FOwnerForm->WindowProc новым своим значением. И сообщения идут уже последнему компоненту обновившему FOwnerForm->WindowProc. Как тут быть?

Автор: borisbn 17.5.2011, 09:42
Цитата(dencraft @  16.5.2011,  17:59 Найти цитируемый пост)
Как тут быть?

переопределять WindowProc не Owner'а, а свой
Код

OwnerWndProc=this->WindowProc;
this->WindowProc=WndProc1;

а зачем вообще перехватывать все сообщения приложения (Application->OnMessage) ?

Автор: dencraft 17.5.2011, 11:21
А мне нужно перехватывать сообщение мыши за пределами компонента и даже формы. По сути у меня все работает через SetCapture(Application->Handle); Я в Application->OnMessage перехватываю сообщения WM_SETCURSOR, далее относительно него, я уже определяю какая кнопка была нажата. Дело в том, что мне нужно еще отловить момент отпускания кнопки, который может быть не только над данным компонентом, но и над главной формой или за ее пределами, или другим компонентом. 
Через пары BEGIN_MESSAGE_MAP/END_MESSAGE_MAP не получается перехватить отпускание мыши над другими компонентами. И почему-то не работает SetCapture(Application->Handle).

Автор: xvr 17.5.2011, 12:01
Цитата(dencraft @  16.5.2011,  16:52 Найти цитируемый пост)
Внутри компонента я переопределил методы Application->OnMessage и Form->WindowProc.
Это делать КАТЕГОРИЧЕСКИ нельзя!

Цитата(dencraft @  17.5.2011,  11:21 Найти цитируемый пост)
А мне нужно перехватывать сообщение мыши за пределами компонента и даже формы. По сути у меня все работает через SetCapture(Application->Handle);

Делайте SetCapture(Handle);, т.е. от своего (компонента) Handle'а, а не приложения

Автор: borisbn 17.5.2011, 13:48
Цитата(dencraft @  17.5.2011,  11:21 Найти цитируемый пост)
А мне нужно перехватывать сообщение мыши за пределами компонента и даже формы

для этого существует TApplicationEvents. делаешь так
Код

// h-файл
TApplicationEvents *appEvents;
// cpp-файл
// Конструктор
  appEvents = new TApplicationEvents( 0 );
  appEvents->OnMessage = onMessage;
// Деструктор
  delete appEvents;
//
void __fastcall MyClass::onMessage(tagMSG &Msg, bool &Handled)
{
  if ( Msg.hwnd == Handle ) // наше окно
  {
       switch ( Msg.message )
       {
            case WM_KEYDOWN:
                ...
            break;
           ...
        }
   }
   else // другое окно
   {
       ...
   }


Автор: dencraft 23.5.2011, 03:10
C TApplicationEvents получилось. Но не до конца.  Это по сути то тот же WinProc в удобной оболочке. Но уже вроде работает раздельно. Но все же одна проблема осталась.
Я пользуюсь SetCapture(...) для перехвата всех сообщений мыши, даже если она вышла за пределы компонента, над которым я нажал кнопку мыши. Но вот за пределами главного окна программы, сообщения уже идут мимо приложения, т.е. мимо WinProc. Вот поэтому я тогда и использовал переопределенный обработчик Application->OnMessage. Только туда направляются все сообщения WM_MOUSEMOVE даже если мышь за пределами главного окна. Это работает только при нажатии кнопки и удержании ее в нажатом положении при перемещении курсора за пределы главного окна приложения. 

Хотелось бы все реализовать независимо от основного приложения. Но похоже тут придется писать какой-нибудь ретранслятор сообщений компонентам. Что-то вроде автоматической рассылки сообщений из сообщений приложения этому компоненту если совпадает его Handle. Не хотелось бы, т.к. это разрушает саму идею компонента - "все в одном". 


Автор: dencraft 30.5.2011, 16:34
Решил проблему несколько по-другому. Может кому будет интересно. 

Использую TApplicationEvents. При нажатии кнопки мыши запускается таймер и SetCapture(). По этому таймеру я определяю координаты мыши через GetCursorPos(). SetCapture нужен для того чтобы отлавливались сообщения об отпускании мыши за пределом текущего компонента. 
Вот собственно и все. И без переопределения WinProc и Application->OnMessage. Все замечательно работает в любом количестве этих компонентов на форме.

Также, кому интересно, что это было и для чего. Это Spinner, созданный на основе TUpDown, значение Position которого можно менять не только нажатием на стрелки Up и Down, но и перетаскиванием мыши вверх и вниз. Если курсор выходит за край экрана, то он перебрасывается на противоположную сторону, тем самым обеспечивается бесконечное прокручивание. Весьма удобно для интерактивного изменения параметров и отображения результата в окне. 

Далее буду прикручивать к нему уже TEdit, работу с Float, округлением и может еще чего.


Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)