Virtual TreeView и EasyListView: примеры использования

Комментарии

19 комментария(ев)
аватар: jimmyjonezz
jimmyjonezz
Дата: Втр, 02/11/2010 - 21:16
Звание: Мастер
Сообщений: 2469

дружище, под капот спрячь основной текст "статьи"! Wink

аватар: angryvitum
angryvitum
Дата: Втр, 02/11/2010 - 21:17
Звание: Посвященный
Сообщений: 551

Да, первый раз не расчитал Smile

аватар: Lord_of_fear
Lord_of_fear
Дата: Втр, 02/11/2010 - 22:00
Звание: Мастер
Сообщений: 2213

Просто и доступно. Респект Smile

аватар: Spider_NET
Spider_NET
Дата: ВС, 07/11/2010 - 09:45
Звание: Мастер
Сообщений: 2455

Хороший пост, респект!

Опубликуем в журнале Wink

аватар: walkersw
walkersw
Дата: ПТ, 17/12/2010 - 19:31
Звание: Наблюдатель
Сообщений: 1

У меня есть замечание.
Чекбоксы в дереве прорисовываются в столбце VT.Header.MainColumn, а это не обязательно колонка с индексом 0 (я в некоторых местах делаю так, что если пользователь меняет колонки местами, то переназначаю MainColumn, чтобы чекбоксы отрисовывались в первом видимом столбце).

А вообще идея очень хорошая. Принимаю на вооружение, спасибо.

аватар: novikey
novikey
Дата: Втр, 24/05/2011 - 17:45
Звание: Наблюдатель
Сообщений: 4

Цитировать

Класс TVirtualTreeColumn содержит свойства CheckBox и CheckState, однако все попытки заставить их работать не увенчались успехом.

А я таки добил(как-то самокритично Smile) увенчал - решение нашлось после анализа кода процедуры TVirtualTreeColumns.PaintHeader. Основная идея в том, что заголовку столбца должна быть назначена иконка из ImageList. Отображаться она не будет, но вот до штатной отрисовки checkbox'а без нее по коду не добраться.
procedure TForm1.FormCreate(Sender: TObject);
begin
with VirtualStringTree1 do
begin
Header.Options := Header.Options + [hoShowImages];
Header.Images := ImageList1;
with Header.Columns[0] do
begin
CheckBox := true;
ImageIndex := 0;
end;
end;
end;

procedure TForm1.IterateHeaderCheck(Sender: TBaseVirtualTree;
Node: PVirtualNode; Data: Pointer; var Abort: Boolean);
begin
if PBoolean(Data)^ then
Node.CheckState := csCheckedNormal
else
Node.CheckState := csUncheckedNormal;
end;

procedure TForm1.VirtualStringTree1HeaderClick(Sender: TVTHeader;
HitInfo: TVTHeaderHitInfo);
var
b: Boolean;
begin
if (HitInfo.Column = Innocent and (hhiOnCheckbox in HitInfo.HitPosition) then
begin
b := Sender.Columns[HitInfo.Column].CheckState in [csCheckedPressed, csCheckedNormal];
Sender.Treeview.IterateSubtree(nil, IterateHeaderCheck, PBoolean(@b), [], true);
Sender.Treeview.InvalidateColumn(0);
end;
end;

procedure TForm1.VirtualStringTree1Checked(Sender: TBaseVirtualTree; Node: PVirtualNode);
var
c: Integer;
begin
c := Sender.CheckedCount;
with TVirtualStringTree(Sender).Header do
begin
if c = 0 then
Columns[0].CheckState := csUncheckedNormal
else
if c = Sender.TotalCount then
Columns[0].CheckState := csCheckedNormal
else
Columns[0].CheckState := csMixedNormal;
end;
end;

P.S. Если быстро кликать по checkbox'у в заголовке, событие OnHeaderClick срабатывает через раз, однако если назначить метод VirtualStringTree1HeaderClick и в качестве обработчика события OnHeaderDblClick, все становится на свои места.

аватар: angryvitum
angryvitum
Дата: Втр, 24/05/2011 - 18:46
Звание: Посвященный
Сообщений: 551

Good novikey, действительно, более удобное решение, лишенное таких недостатков как определение координат checkbox'а и проблем с его отрисовкой при отключенных темах Windows (ибо все штатно). Все-таки хотелось бы ожидаемой работы от VT, т.е. выставил в свойствах колонки Checkbox := True и все. Будем надеятся, в 5-ой версии исправят...

аватар: Belketre
Belketre
Дата: Втр, 24/05/2011 - 19:38
Звание: Мастер
Сообщений: 1887

Еще очень хорошо было бы, если бы можно было использовать в качестве данных не записи а полноценные классы, а то идиотизм какой-то вроде MFC получается

P.S. Возможно, я просто не до конца понял как работать с компонентом

аватар: angryvitum
angryvitum
Дата: Втр, 24/05/2011 - 20:38
Звание: Посвященный
Сообщений: 551

В узлах Virtual Tree хранится указатель на данные (отсюда и его "виртуальность"), так что, теоретически, можно и на объекты ссылаться, хотя и не без бубна. В целом согласен, в EasyListView этот момент значительно лучше проработан.

аватар: Belketre
Belketre
Дата: Втр, 24/05/2011 - 20:40
Звание: Мастер
Сообщений: 1887

На практике если указателем ссылаться на объекты - получаем AV при рисовании элементов дерева

аватар: novikey
novikey
Дата: Втр, 24/05/2011 - 22:35
Звание: Наблюдатель
Сообщений: 4

type PRec = ^TRec; TRec = record MyClass: TObject; end;

procedure TForm4.FormCreate(Sender: TObject);
begin
VirtualStringTree1.NodeDataSize := SizeOf(TRec);
VirtualStringTree1.RootNodeCount := 2;
end;

procedure TForm4.VirtualStringTree1InitNode(Sender: TBaseVirtualTree;
ParentNode, Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
var
p: PRec;
begin
p := Sender.GetNodeData(Node);
p.MyClass := TObject.Create;
end;

procedure TForm4.VirtualStringTree1GetText(Sender: TBaseVirtualTree;
Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
var CellText: WideString);
var
p: PRec;
begin
p := Sender.GetNodeData(Node);
CellText := p.MyClass.ClassName;
end;

procedure TForm4.VirtualStringTree1FreeNode(Sender: TBaseVirtualTree;
Node: PVirtualNode);
var
p: PRec;
begin
p := Sender.GetNodeData(Node);
FreeAndNil(p.MyClass);
end;

аватар: Belketre
Belketre
Дата: Втр, 24/05/2011 - 22:44
Звание: Мастер
Сообщений: 1887

Это уже костыли, а я хотел что бы можно было использовать в качестве элемента следующее:
type
PObj = ^TObj;
TObj = class
{...}
end;

аватар: novikey
novikey
Дата: СР, 25/05/2011 - 14:38
Звание: Наблюдатель
Сообщений: 4

type PMyObj = ^TMyObj; TMyObj = class end;

procedure TForm1.FormCreate(Sender: TObject);
begin
VirtualStringTree1.NodeDataSize := SizeOf(TMyObj);
VirtualStringTree1.RootNodeCount := 2;
end;

procedure TForm1.VirtualStringTree1FreeNode(Sender: TBaseVirtualTree;
Node: PVirtualNode);
var
p: PMyObj;
begin
p := Sender.GetNodeData(Node);
FreeAndNil(p^);
end;

procedure TForm1.VirtualStringTree1GetText(Sender: TBaseVirtualTree;
Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
var CellText: WideString);
var
p: PMyObj;
begin
p := Sender.GetNodeData(Node);
CellText := p^.ClassName;
end;

procedure TForm1.VirtualStringTree1InitNode(Sender: TBaseVirtualTree;
ParentNode, Node: PVirtualNode; var InitialStates: TVirtualNodeInitStates);
var
p: PMyObj;
begin
p := Sender.GetNodeData(Node);
p^ := TMyObj.Create;
end;

аватар: Belketre
Belketre
Дата: СР, 25/05/2011 - 17:12
Звание: Мастер
Сообщений: 1887

А ты этот код проверял? Или просто заменил record на class?

аватар: novikey
novikey
Дата: СР, 25/05/2011 - 18:21
Звание: Наблюдатель
Сообщений: 4

Естественно проверял. Да и какая собственно разница, в дереве хранится указатель, а на что оно указывает дереву неважно. Память, выделенную под объект, мы очищаем самостоятельно по событию OnFreeNode, а память, выделенную под указатель, очищает (ну и резервирует) уже сам компонент на основе указанного нами NodeDataSize;

аватар: infbyte
infbyte
Дата: ПТ, 15/06/2012 - 19:32
Звание: Наблюдатель
Сообщений: 1

Скиньте готовый пример плиз
и ссылка на документацию битая

аватар: angryvitum
angryvitum
Дата: СБ, 16/06/2012 - 00:51
Звание: Посвященный
Сообщений: 551

С готовым примером, извини, не получится, а вот ]]>документацией поделюсь]]> (аттракцион невиданной щедрости продлится в течение 24 часов со времени публикации поста).

]]>Всегда свежие исходники Virtual Treeview]]>, если пользуешься SVN.

novikey, кстати, нашел лучшее решение, чем описанное в статье.

аватар: ildvild
ildvild
Дата: Втр, 19/06/2012 - 15:43
Звание: Наблюдатель
Сообщений: 1

angryvitum
Перезалей документацию еще раз пожалуйста, либо если не сложно, можешь скинуть на почту ildvild@gmail.com:)

аватар: AnalogXP
AnalogXP
Дата: СБ, 09/02/2013 - 02:39
Звание: Наблюдатель
Сообщений: 1

Подскажите пожалуйста по EasyListView. Как получить итем на который что-то тянем (Drag and Drop). В VirtualTree это просто - VT.DropTargetNode.