VirtualStringTree Правильное / рекомендуемое использование - PullRequest
4 голосов
/ 02 октября 2011

Я уже некоторое время использую virtualstringtree.Я использую его для двух разных вещей: во-первых, как обычное дерево для выбора, отображения данных, а во-вторых, как сетку для отображения выходных данных операторов SQL.

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

Мои вопросы относятся кпредпочтительный / лучший способ заселить дерево.Я прочитал из документации VST, что вы должны использовать событие onInitNode вместе с rootnodecount.Однако я обнаружил, что использование метода AddChild () очень похоже, хотя это и не поощряется.

Позвольте мне показать несколько (упрощенных) примеров:

1.Heirarchy

type PData = ^rData;
    rData = packed record
      ID : Integer;
      ParentID : Integer;
      Text : WideString;
    end;

procedure Loadtree;
 var Node : PVirtualNode;
Data : PData;
 begin
    Q1 := TQuery.Create(Self);
            try
                Q1.SQL.Add('SELECT * FROM Table');
            Q1.Open;
            Q1.Filter := 'ParentID = -1'; //to get the root nodes
            Q1.Filtered := True;
            while not Q1.Eof do
            begin
                    Node := VST.AddChild(nil);
                    Data := VST.GetNodeData(Node);
                    Data.ID := Q1.Fields[fldID].AsInteger;
                    Data.ParentID := Q1.Fields[fldParentID].AsInteger;
                    Data.Text := Q1.Fields[fldText].AsString;
                    //now filter the query again to get the children of this node
                    PopulateChildren(Data.ParentID,Node); //add children to this node and do it recursively
                    Q1.Next;
            end;
       finally
          Q1.free;
      end;

end;

2.Сетка

procedure LoadGrid;
var Node : PVirtualNode;
Data : PData;
begin
     Q1 := TQuery.Create(self);
        try
            Q1.SQL.Add('SELECT * FROM Table');
            Q1.Open;
            while not Q1.eof do
            begin
                    Node := VST.AddChild(nil);
                    Data.ID := Q1.Fields[fldID].AsInteger;
                    Data.Text := Q1.Fields[fldText].AsString;
                    Q1.Next;
            end;
    finally
            Q1.Free;
    end;
end;

Итак, по сути я обхожу методы / свойства RootNodeCount и OnInitNode и использую старомодный способ добавления узлов в дерево.Вроде нормально работает.Обратите внимание, что в примере я создаю и уничтожаю свои запросы во время выполнения.

Причина, по которой я начал использовать дерево таким образом, заключается в том, что я мог загрузить все данные в дереве один раз, а затем освободить TQuery после того, как закончилиспользуй это.Я думал, что независимо от того, чтобы сохранить / создать TQuery, мне все равно нужно будет использовать мою запись rData для хранения данных, а значит, использовать больше памяти, если я не уничтожу TQuery.В настоящее время мое приложение использует около 250 МБ при полной загрузке и может увеличиваться, когда я запускаю отчеты SQL и отображаю их в VST.Я видел, что он использует около 1 ГБ оперативной памяти, когда я запустил отчет SQL с 20000+ узлов и более 50 столбцов.Что я хотел бы знать, если способ, которым я использую VST, неверен в отношении минимизации использования памяти?

Будет ли лучше создать запрос на время существования дерева и использовать событие onInitNode?Так что, когда данные запрашиваются деревом, они извлекают их из TQuery, используя событие onInitNode / OnInitChildren (то есть чисто виртуальную парадигму дерева)?Таким образом, мне нужно будет поддерживать TQuery в течение всей формы.Будет ли какая-либо польза от памяти / производительность при использовании этого способа?

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

Ответы [ 2 ]

11 голосов
/ 02 октября 2011

Причина, по которой AddChild() не поощряется, заключается в том, что она нарушает виртуальную парадигму компонента - вы создаете все узлы, даже если они вам никогда не понадобятся.Скажем, запрос возвращает 100 записей, но дерево показывает 10 узлов одновременно.Теперь, если пользователь не прокручивает страницу вниз, вы тратите впустую ресурсы на 90 узлов, поскольку они никогда не нужны (не стали видимыми).Так что если вы беспокоитесь об использовании памяти, AddChild() - плохая идея.Другое дело, что если набор результатов большой, заполнение дерева занимает много времени, и ваше приложение не отвечает на запросы.При использовании виртуального пути (RootNodeCount и OnInitNode) дерево сразу «готово», и пользователь не испытывает никаких задержек.

С другой стороны, в случае (относительно) небольших наборов результатов с использованием AddChild()может быть лучшим вариантом - это позволит вам загружать данные в одну короткую транзакцию.Т.е. в случае древовидной структуры имеет смысл загружать весь «уровень» сразу, когда пользователь расширяет родительский узел.

Использование БД с VT немного сложнее, так как запрос к БД также является специальным ресурсом, так каксобственные ограничения (вы хотите, чтобы транзакции были короткими, это относительно медленно, БД может поддерживать только односторонний курсор и т. д.).
Таким образом, я бы сказал, что это то, что вы должны решить для каждого отдельного случая, в зависимости от варианта использования и объема данных, то есть

  • небольшой набор результатов подходит для загрузки всех сразу сAddChild() или во внутреннюю структуру данных и последующее использование ее через события VT;
  • загрузка по одному уровню за раз, вероятно, является хорошим компромиссом для деревьев;
  • в случае очень больших наборов результатов загрузка в пакетах можетобеспечивает хорошую производительность и компромисс в использовании памяти, но усложняет код, управляющий VT.
1 голос
/ 09 декабря 2011

TQuery обеспечивает массивный доступ к записям.

Если вы сверились с документацией, вы можете перейти к каждой записи, используя свойство RecNo, и вы можете перейти к каждому полю, используя Fields[i] (по индексу).

Нет сдерживания при использовании TVirtualStringTree в качестве виртуального режима соединения с базой данных.

Как только вы выполните запрос, данные все равно будут доступны в памяти как TDataSet, так почему бы не использовать их в качестве контейнера данных?

PS: доступ к полям с использованием их индекса происходит быстрее, чем FieldByName.

...