C++ Builder
| Главная | Уроки | Статьи | FAQ | Форум | Downloads | Литература | Ссылки | RXLib | Диски |

 
TTreeView, Событие OnSelectItem?
Doga
Отправлено: 07.09.2004, 19:35


Мастер участка

Группа: Участник
Сообщений: 575



TreeView->MultiSelect = true;

Необходимо в этом режиме запрещать селектирование некоторых узлов (при нажатой Shift или Control).

Поскольку TreeView не имеет события OnSelectItem приходится извращаться.
Но к сожалению, сообщения TVN_SELCHANGING и TVN_SELCHANGED не отлавливаются формой (через VCL_MESSAGE_HANDLER(TVN_SELCHANGING , TMessage, HandleTVSelChanging)) sad.gif


Кто знает как грамотно написать такой обработчик?
Gedeon
Отправлено: 08.09.2004, 10:56


Ветеран

Группа: Модератор
Сообщений: 1742



Я так думаю надо написать наследника от TTreeView и в этом наследнике отлавливать сообщение и соответсвенно генерить ивэнт.
Doga
Отправлено: 08.09.2004, 17:59


Мастер участка

Группа: Участник
Сообщений: 575



QUOTE

надо написать наследника от TTreeView


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

Я тут выяснил, что TVN_SELCHANGING и TVN_SELCHANGED в чистом виде не передаются, а посылаются в составе сообщения WM_NOTIFY.
Но и здесь засада! WM_NOTIFY тож не ловится формой... sad.gif
Что за ерунда?
Gedeon
Отправлено: 08.09.2004, 18:07


Ветеран

Группа: Модератор
Сообщений: 1742



Дело в том, что эти сообщения форме и не должны и не будут посылаться, они отсылаются напрямик окну дерева, а чтобы их отловить нужно писать наследника, да и удобнее потом будет, создал событие и работаешь с ним. А создать наследника с добавлением отлова одного сообщения и генерацией ивента дело 5 минут.
Gedeon
Отправлено: 08.09.2004, 18:17


Ветеран

Группа: Модератор
Сообщений: 1742



Вот наткнулся Devnvd на xportal отвечает как без написания наследника отлавливать нужные сообщения для TMemo, для TTreeView переделать несложно.
QUOTE

Не обязательно возиться с наследником.
Достаточно переобозначить WindowProc
CODE

TWndMethod OldMemoProc=NULL;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
       : TForm(Owner)
{
OldMemoProc=Memo1->WindowProc;
Memo1->WindowProc=MyMemoProc;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::MyMemoProc(TMessage &msg)
{
 static int i=0;
 if(msg.Msg==WM_VSCROLL)
 {
   //... ВАШ код или вызов вашей подпрограммы обработки
   Label1->Caption=IntToStr(i++);
 }
 OldMemoProc(msg);
}
Doga
Отправлено: 08.09.2004, 18:39


Мастер участка

Группа: Участник
Сообщений: 575



А если переписать TreeView->WindowProc ?
Там можно будет отловить?
Gedeon
Отправлено: 08.09.2004, 19:03


Ветеран

Группа: Модератор
Сообщений: 1742



QUOTE (Doga @ 08/09/2004, 19:41)
А если переписать TreeView->WindowProc ?
Там можно будет отловить?

Да можно.
Doga
Отправлено: 08.09.2004, 19:22


Мастер участка

Группа: Участник
Сообщений: 575



THX! smile.gif

А насчёт наследника — тут тоже засада. Все процедуры в TCustomTreeView, куда можно встроить это событие либо объявлены в секции Protected, либо вообще не перегружаемые sad.gif

За исключением WndProc, конечно smile.gif

Отредактировано Doga — 08/09/2004, 20:26
Gedeon
Отправлено: 08.09.2004, 20:08


Ветеран

Группа: Модератор
Сообщений: 1742



Да ну что вы ей богу. Ловите это сообщение через карту сообщений
Вот пример
Наследник:
h:
CODE

//---------------------------------------------------------------------------

#ifndef TreeView1H
#define TreeView1H
//---------------------------------------------------------------------------
#include <SysUtils.hpp>
#include <Classes.hpp>
#include <ComCtrls.hpp>
#include <Controls.hpp>
//---------------------------------------------------------------------------
typedef void __fastcall (__closure *TSelectChanging)(System::TObject* Sender);
class PACKAGE TTreeView1 : public TTreeView
{
private:
virtual void __fastcall SelectChanging(TMessage &Msg);
TSelectChanging FSelectChanging;
protected:
BEGIN_MESSAGE_MAP
VCL_MESSAGE_HANDLER(WM_NOTIFY,TMessage , SelectChanging)
END_MESSAGE_MAP(TTreeView)
public:
__fastcall TTreeView1(TComponent* Owner);
__published:
__property TSelectChanging OnSelectChanging = {read=FSelectChanging, write=FSelectChanging};
};
//---------------------------------------------------------------------------
#endif

cpp:
CODE

//---------------------------------------------------------------------------

#include <vcl.h>

#pragma hdrstop

#include "TreeView1.h"
#pragma package(smart_init)
//---------------------------------------------------------------------------
// ValidCtrCheck is used to assure that the components created do not have
// any pure virtual functions.
//

static inline void ValidCtrCheck(TTreeView1 *)
{
new TTreeView1(NULL);
}
//---------------------------------------------------------------------------
__fastcall TTreeView1::TTreeView1(TComponent* Owner)
: TTreeView(Owner)
{
}
//---------------------------------------------------------------------------
namespace Treeview1
{
void __fastcall PACKAGE Register()
{
TComponentClass classes[1] = {__classid(TTreeView1)};
RegisterComponents("New", classes, 0);
}
}
//---------------------------------------------------------------------------


void __fastcall TTreeView1::SelectChanging(TMessage &Msg)
{
//тут я реагирую на сообщение WM_NOTIFY, для ваших Вам нужно
   // проверить является ли
   // (LPARAM) lParam      // = (LPARAM) (LPNMHDR) pnmh;
   // TVN_SELCHANGING   pnmtv = (LPNMTREEVIEW) lParam
   // посмотрите в MSDN это описано

   if (FSelectChanging)
FSelectChanging(this);
}

Использование:
CODE

__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
TTreeView1 *TW1 = new TTreeView1(this);
TW1->Parent = this;
TW1->MultiSelect = true;
TW1->OnSelectChanging = SelChange;
TW1->Items->Add(NULL, "RootNode1");
TW1->Items->Add(NULL, "RootNode2");
TW1->Items->Add(NULL, "RootNode3");


}
//---------------------------------------------------------------------------


void __fastcall TForm1::SelChange(TObject * Sender)
{
//TODO: Add your source code here
}


Отредактировано Gedeon — 08/09/2004, 21:16
Doga
Отправлено: 08.09.2004, 20:45


Мастер участка

Группа: Участник
Сообщений: 575



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

Сейчас другая проблема — не получается расшифровать сообщение WM_NOTIFY (Я переписал WindowProc). То есть не могу найти в нём TVN_SELCHANGED.

Вот что про них написано в MSDN:
QUOTE

TVN_SELCHANGED


Notifies a tree-view control's parent window that the selection has changed from one item to another. This notification message is sent in the form of a WM_NOTIFY message.

TVN_SELCHANGED
   pnmtv = (LPNMTREEVIEW) lParam

Parameters
pnmtv
Address of an NMTREEVIEW structure. The itemOld and itemNew members of the NMTREEVIEW structure are TVITEM structures that contain information about the previously selected item and the newly selected item. Only the mask, hItem, state, and lParam members of these structures are valid. The stateMask members of the TVITEM structures specified by itemOld and itemNew are undefined on input.
The action member of the NMTREEVIEW structure indicates the type of action that caused the selection to change. It can be one of the following values: TVC_BYKEYBOARD  By a keystroke.
TVC_BYMOUSE  By a mouse click.
TVC_UNKNOWN  Unknown.

Return Values
The return value is ignored.


QUOTE

WM_NOTIFY


The WM_NOTIFY message is sent by a common control to its parent window when an event has occurred or the control requires some information.

WM_NOTIFY
   idCtrl = (int) wParam;
   pnmh = (LPNMHDR) lParam;

Parameters
idCtrl
Identifier of the common control sending the message. This identifier is not guaranteed to be unique. An application should use the hwndFrom or idFrom member of the NMHDR structure (passed as the lParam parameter) to identify the control.
pnmh
Pointer to an NMHDR structure that contains the notification code and additional information. For some notification messages, this parameter points to a larger structure that has the NMHDR structure as its first member.
Return Values
The return value is ignored except for notification messages that specify otherwise.


Казус получается — lParam это указатель на структуру NMHDR или это адрес структуры NMTREEVIEW ?

Может idCtrl = (int) wParam помог бы с этим определиться, но как — я пока не могу выяснить sad.gif

Пока обе интерпретации lParam неудачны...
Doga
Отправлено: 08.09.2004, 21:48


Мастер участка

Группа: Участник
Сообщений: 575



Частично разобрался.

Сначала в любом случае надо интерпретировать lParam как указатель на структуру NMHDR и посмотреть у неё NMHDR->code.

если NMHDR->code == TVN_SELCHANGED, то lParam можно уже интерпретировать как адрес структуры NMTREEVIEW.

Проблема в том что перехватываемое WM_NOTIFY вообще не содержит TVN_SELCHANGED, а только TTN_GETDISPINFO — для ToolTips (естественно только когда TreeView->ToolTips = true).

Куда подевалось TVN_SELCHANGED не пойму. Может оно после его обработки в TCustomTreeView дальше никуда не посылается?

Надо ещё раз глянуть исходники TCustomTreeView...
Но если это так, то что тогда делать?!!! ohmy.gif
Gedeon
Отправлено: 09.09.2004, 08:50


Ветеран

Группа: Модератор
Сообщений: 1742



Во первых я забыл
CODE

void __fastcall TTreeView1::SelectChanging(TMessage &Msg)
{
//тут я реагирую на сообщение WM_NOTIFY, для ваших Вам нужно
// проверить является ли
// (LPARAM) lParam // = (LPARAM) (LPNMHDR) pnmh;
// TVN_SELCHANGING pnmtv = (LPNMTREEVIEW) lParam
// посмотрите в MSDN это описано

if (FSelectChanging)
FSelectChanging(this);
// это
   DefWindowProc(this->Handle,Msg.Msg, Msg.wParam,Msg.lParam);
   // забыл
}

ну а приходит оно сначала все-таки к вам а потом дальше, проверка что-то типа
CODE

if(((NMHDR*)lParam)->code==TVN_SELCHANGED){
//
}

должно работать.

Отредактировано Gedeon — 09/09/2004, 09:53
Doga
Отправлено: 09.09.2004, 20:21


Мастер участка

Группа: Участник
Сообщений: 575



Создание наследника не дало никаких результатов. Ситуация та же что и с WindowProc:

QUOTE

Проблема в том что перехватываемое WM_NOTIFY вообще не содержит TVN_SELCHANGED, а только TTN_GETDISPINFO — для ToolTips (естественно только когда TreeView->ToolTips = true).


А если TreeView->ToolTips = false, WM_NOTIFY вообще никогда не приходит.

P.S. После всего этого нечего было надеяться на RxWindowHook — он тоже ожиданий не оправдал... smile.gif Теперь придётся рвать гланды через задний проход biggrin.gif

Отредактировано Doga — 09/09/2004, 22:00
Doga
Отправлено: 10.09.2004, 18:05


Мастер участка

Группа: Участник
Сообщений: 575



Вот решение проблемы:

CODE

void __fastcall TRxdEditorForm::TreeView1AdvancedCustomDrawItem(
     TCustomTreeView *Sender, TTreeNode *Node, TCustomDrawState State,
     TCustomDrawStage Stage, bool &PaintImages, bool &DefaultDraw)
{
 if (Stage == cdPrePaint)
 {
   int StateSHift   = GetKeyState(VK_SHIFT);
   int StateControl = GetKeyState(VK_CONTROL);

   if ((StateSHift >> 15) || (StateControl >> 15))
   {
     bool NeedReSelect = false;

     TTreeNode *FocusNode = NULL;
     
     TList *NodesList = NULL;
   
     for (int k = 0; k < TreeView1->SelectionCount; k++)
     {
       //Флаги, которые запрещают селектирование  
       if ( (((TNodeData *)TreeView1->Selections[k]->Data)->NodeStatus == lnsGroupLines)
         || (((TNodeData *)TreeView1->Selections[k]->Data)->NodeStatus == lnsGroupCheckLines) )
       {
         NeedReSelect = true;
         if (TreeView1->Selections[k] == TreeView1->Selected)
         {
           FocusNode = TreeView1->Selections[k];
         }
       }
       else
       {
         if (!NodesList)
         {
           NodesList = new TList();
         }

         NodesList->Add(TreeView1->Selections[k]);
       }
     }

     if (NeedReSelect)
     {
       if (NodesList)
       {
         TreeView1->Select(NodesList);
         delete NodesList;
       }
       else if (FocusNode)
       {
         NodesList = new TList();
         NodesList->Add(FocusNode);
         TreeView1->Select(NodesList);
         delete NodesList;
       }
     }
   }
 }
}

Вернуться в Вопросы программирования в C++Builder