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))
Кто знает как грамотно написать такой обработчик?
|
|
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 тож не ловится формой...
Что за ерунда?
|
|
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!
А насчёт наследника — тут тоже засада. Все процедуры в TCustomTreeView, куда можно встроить это событие либо объявлены в секции Protected, либо вообще не перегружаемые
За исключением WndProc, конечно
Отредактировано 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! Признаю свою ошибку, действительно через карту сообщений можно.
Сколько раз пользовался этим в классах форм, а применить в компонентах не догадался...
Сейчас другая проблема — не получается расшифровать сообщение 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 помог бы с этим определиться, но как — я пока не могу выяснить
Пока обе интерпретации 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...
Но если это так, то что тогда делать?!!!
|
|
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 — он тоже ожиданий не оправдал... Теперь придётся рвать гланды через задний проход
Отредактировано 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;
}
}
}
}
}
|
|
|