Hierarchical Datagridview

Posted by admin

Недавно в обратную связь поступил вопрос. Один из читателей поинтересовался, как можно сделать удобный вывод иерархических данных, построенных по принципу Adjacency List, в виджете CGridView вместо CTreeView. Это, например, могут быть вложенные статические страницы, категории или пункты меню, хранимые в базе данных.

Hierarchical Datagridview

Попробуем решить этот вопрос. При работе с Nested Set не возникает проблем, так как всё дерево пунктов можно получить одним SQL-запросом и использовать значение поля level модели для отрисовки сдвига. Другое дело – принцип Adjacency list, где отношение к родительскому пункту указывается только с помощью поля parentid. Для вывода такой иерархии требуется использование рекурсивного обхода. Попробуем его и реализовать. Вначале «препарируем» виджет CGridView и изучим его работу. Итак, данный компонент занимается выводом таблицы элементов.

ADO Data, Hierarchical FlexGrid, DataRepeater, DataGrid, DataList и DataCombo — см. Ниже раздел 'Новые элементы управления'. Расширенное связывание данных. В предыдущих версиях Visual Basic существовала только одна возможность привязывать элементы управления — связывать их вместе. Nested or Master Detail or Hierarchical DataGridView for winforms; Author: syed shanu; Updated: 20 Jan 2015; Section: C#; Chapter: Languages; Updated: 20. Работа с DataGridView C# (си шарп). После введения в него текста он появлялся в DataGridView: dataGridView - это отображение.

Он генерирует «шапку» таблицы, её «тело» и «подвал». Widget ( ' zii.widgets.grid.CGridView ', array ( ' dataProvider ' = $dataProvider, ' columns ' = array ( ' date ', ' type ', ' message '.

), ) );? В опции logformat можно передавать регулярное выражение с именованными параметрами, по которому будут разбиваться строки. Из этого следует, что виджет CGridView пассивно отображает полученные им данные.

Никакой конкретной логики выборки данных он не содержит. Выборка полностью производится из провайдера, который, в свою очередь, использует состояние встроенных в него элементов pagination и sort для расчёта требуемого числа и порядка сортировки возвращаемых им элементов. Соответственно, вместо доработки CGridView для решения нашей задачи нужно переключить внимание на провайдер данных. Так как привычнее работать с ActiveRecord, то рассмотрим CActiveDataProvider. Как мы помним, за получение массива элементов отвечает метод fetchData. Пусть он и возвращает элементы, отсортированые по иерархии.

Напишем наследника класса CActiveDataProvider и переопределим в нём данный метод. $rootCriteria - addCondition ( ' t.parentid = 0 ' ); запрос получится противоречивым. WHERE t.parentid = 5 AND t.parentid = 0 и таблица окажется пустой. Конечно же, мы могли проверять на вхождение строки parentid выражение для condition текущего экземпляра $criteria, но это довольно сложно.

Или в фильтре производится любой поиск. Например, по имени title.

С постоянно подключенным условием parentid = 0 поиск вообще не будет производиться по дочерним элементам. А если всё-же найдётся несколько родительских элементов, то метод buildRecursive достроит к каждому из них дерево вложенных элементов. Результат поиска будет искажён. Вторая проблема может возникнуть, когда поле parentid может содержать NULL.

Конечно же, можно в таблице установить ему NOT NULL, но это решение не универсально, так как ошибка может появиться в другом проекте. Путём нехитрых опытов и упрощений достаточно реализовать этот фрагмент так, как он и приведён. $rootCriteria = clone $criteria; $isEmptyCondition = empty ( $rootCriteria - condition ); if ( $isEmptyCondition ) $rootCriteria - addCondition ( ' t.parentid iS NULL OR t.parentid = 0 ' ); $items = $this - model - findAll ( $rootCriteria ); if ( $isEmptyCondition ) $items = $this - buildRecursive ( $items ); $this - model - setDbCriteria ( $baseCriteria ); // restore original criteria return $items; Здесь иерархический вывод включается только при остутствии каких-либо условий. При поиске вывод будет производиться обычным списком.

В общем, решение неочевидное, но работает. Ещё один нюанс. В переменную $items выберется массив найденных результатов, потом этот список передаётся в метод buildRecursive, который для каждого элемента рекурсивно добавляет в массив $items все вложенные объекты. При этом массив так и остаётся одноуровневым. Но нам нужно будет знать значение отступа $indent для подстановки пробелов перед именем категории в ячейку таблицы.

Прямой, но не очень красивый способ – создать поле для хранения уровня внутри модели. Widget ( ' zii.widgets.grid.CGridView ', array ( ' id ' = ' posts-grid ', ' dataProvider ' = $model - search ( ), ' filter ' = $model, ' columns ' = array ( array ( ' name ' = ' title ', ' value ' = ' strrepeat(' ', $data-indent. 4). CHtml::encode($data-title) ', ' type ' = ' raw ', ), array ( ' class ' = ' CButtonColumn ', ), ), ) );? В панели администрирования мы увидим вывод по десять (по умолчанию) родительских элементов на страницу с полностью выведенным деревом вложенных подпунктов.

При попытке найти любой пункт по имени вывод дерева автоматически отключится, и результаты поиска выведётся простым списком. В используемой рекурсивной функции buildRecursive используется «ленивая» загрузка подпунктов через отношение children, что для каждого пункта выполняет запрос к базе данных. Если у вас сотня пунктов, то, соответственно, будут выполняться сто запросов и страница будет без кэширования выводиться несколько секунд. Для десяти пунктов это вполне оптимальный вариант. Альтернативный способ – единственная выборка всех пунктов вызовом findAll в массив и уже рекурсивный обход этого массива. А более серьёзный – использование Nested Set.

Вместо повсеместного повторения конструкции strrepeat мы можем, например, IndentColumn с этим кодом и использовтаь её в любой таблице. На сегодня с этим всё. Буду рад комментариям о данном решении. Если есть две таблицы - статьи и лайки.И нужно вывести в GridView данные о статьях и добавить туда поле countLikes с количеством лайков статьи, которые нужно посчитать. Такого поля в таблице не существует.

Нужно посчитать кол-во лайков к статье по связи idPage в табл. Лайков к id в pages. Вероятнее всего манипуляции с подсчетом лайков нужно проводить в модели PagesSearch c dataProvider. Нужно, чьлбы поля были с сортировкой от большего числа к меньшему. Как можно добавить поле countLikes в GridView c возможностью сортировки по возростанию и убыванию?

Здравствуйте, Павел! Большое спасибо за обзор и за конструктивную критику. Как многие уже возможно слышали, компания DevExpress имеет большой центр разработки в России (и у нас даже есть небольшой блог на Хабре (Поэтому, пользуясь случаем, хочу дополнить Ваш пост комментариями нашей команды разработчиков – надеюсь, эти дополнения будут полезными.

SelectedItem vs FocusedRow – мы признаём такую проблему и планируем исправить её в ближайшем будущем. Раскраска строк через одну – обязательно сделаем, чтобы тоже работало с помощью выставления одного свойства. По поводу master-detail хотелось бы заметить, что DataControlDetailDescriptor.DataControl – это Content свойство. Таким образом, синтаксис немного сокращается до такого: Возможно, мы сделаем View или Columns контент-свойством для грида – чтобы еще больше упростить синтаксис. Но, согласитесь, уже получается не сильно длиннее по сравнению с остальными. По поводу отсутствия отступов между детальным и следующим гридом – обычно горизонтального отступа вполне достаточно для вложенного грида, чтобы визуально отделить детальный грид от мастер грида.

Видимо, это дело вкуса. «Так же, при таком подходе байндинг свойства FocusedRow у деталь грида не работает!» Обоснованно. Тут дело в том, что грид, описанный внутри DataControlDetailDescriptor, клонируется для каждого открытого детального грида. Это сделано для того, чтобы гриды в разных деталях могли полностью синхронизироваться между собой при сортировке, таскании/ресайзинге колонок, фильтрации и т.п. Это одно из основных требований различных пользователей к данной фиче и реализовать его при подходе, основанном на DataTemplate, как у большинства конкурентов, невозможно.

Соответственно, и наложить байндинг на FocusedRow этого грида нельзя, нужна возможность задать байндинг для каждого отклонированного грида. Скорее всего, мы добавим свойство DataControlDetailDescriptor.FocusedRowBinding (SelectedRowBinding), которое позволить прибиндиться к выбранной детальной строке внутри вью модели мастер строки. Байндинг команд контекстного меню.

Насколько мы понимаем, это задача сделать контекстное меню для строки или ячейки. Если контекстное меню задается на уровне всего грида, то в дата контексте этого меню будет то же самое, что у самого грида, то есть будет желаемый результат. Но как при этом добраться до значения этой строки или ячейки, что как раз чаще всего нужно для команд уровня строки/ячейки?

В нашем гриде можно обратиться как к значениям самой строки/ячейки, так и к дата контексту грида. Мы согласны, что это неочевидно, тут нам надо серьезно думать, как упростить этот сценарий.

Что касается другой функциональности, которую Вы рассматривали в Вашей статье: 1. Ленивая догрузка записей и поддержка WCF DataServices. Наш грид полностью поддерживает этот режим (у нас он называется Instant Feedback Mode) и полностью поддерживает загрузку записей по требованию, кеширование, оптимизацию объема загружаемых данных в зависимости от пропускной способности канала, сортировку, группировку, фильтрацию, вычисление Summaries полностью на стороне сервера и многое другое. Пример можно посмотреть в GridDemo/Server-side Data Processing/WCF Data Services (OData), документация по этой ссылке: 2. Древовидный грид. У нас есть TreeListControl, заточенный под различные режимы отображения древовидных данных. Подробнее можно почитать здесь: 3.

Мы предоставляем полную русскую локализацию для всех наших компонентов. Подробнее можно посмотреть в документации: 4. Мы полностью поддерживаем печать и экспорт в различные форматы — xls, xlsx, pdf и т.д. Здесь есть вся информация. Да, работает.

Мдс-1-01 инструкция. То есть для детальных гридов создаются только те строки, которые видны на экране. Кроме того, при таком подходе как у нас, когда используется один скроллер для мастер и всех детальных гридов, строчки повторно используются даже между различными детальными гридами, что ускоряет раскрытие детальных гридов (если в кеше уже есть строки от предыдущих детальных гридов, которые в данный момент не видны на экране).

А также это уменьшает использование памяти при большом количестве открытых детальных гридов благодаря тому, что в каждый момент времени в памяти находится строк не больше, чем помещается на экране. В трилисте виртуализация, соответственно, тоже полностью поддерживается. То есть строки могут повторно использоваться при скроллинге, expand/collapse, в том числе между нодами, находящимися на разных уровнях.