Розширення елемента управління treeview

Вихідний код WinFormControls.exe можна завантажити з MSDN Code Center.

  • Розробка користувальницьких елементів управління Windows за допомогою Visual Basic .NET (огляд)
  • Додавання перевірки правильності регулярного виразу
  • Об'єднання декількох елементів управління в один
  • Розширення елемента управління TreeView
  • Малювання власних елементів управління за допомогою GDI +
  • Вступ
  • Проектування пов'язаної з даними деревовидної структури
  • Реалізація зв'язування даних ® Windows ® Forms були вкладені величезні зусилля з кодування і тестування, і всю цю роботу потрібно виконувати заново, якщо побудова елемента управління необхідно починати повністю "з нуля". З урахуванням цього в даному прикладі буде виконуватися успадкування з існуючого елемента управління Windows Form - Tree View - і його налаштування. Код цього нового елемента управління TreeView можна завантажити одним файлом поряд з іншими прикладами розробки елемента управління і типовим додатком, що демонструє, як цей розширений TreeView може використовуватися спільно з іншими пов'язаними з даними елементами управління.

Проектування пов'язаного з даними Tree View

Тема додавання прив'язки даних до елементу управління TreeView періодично зачіпалася розробниками Windows, проте базовий елемент управління як і раніше не підтримує цю можливість через ключового відмінності між TreeView та іншими елементами управління, такими як ListBox або DataGrid: TreeView відображає ієрархічні дані. Окрему таблицю даних досить нескладно відобразити в ListBox або DataGrid, проте використовувати переваги ієрархічного характеру TreeView для відображення тих же даних не так просто. Існує багато різних способів використання TreeView для відображення даних, але один спосіб є найбільш загальним: угруповання даних з таблиці за певними полях, зображена на рис. 1.

Мал. 1. Відображення даних в TreeView

Для цього прикладу передбачалося створити елемент управління TreeView, в який можна передати плоский набір даних (див. Рис. 2) і легко досягти результату, зображеного на рис. 1.

Мал. 2. Плоский набір результатів, що містить всю інформацію, необхідну для створення дерева, зображеного на рис. 1.

При побудові призначеного для користувача елементу управління перед початком кодування рекомендується чітко уявити собі, для якої мети цей елемент управління буде використовуватися іншим розробником. В даному випадку розробник (дані і необхідний результат описані вище) зможе виконати тільки що описану угруповання за допомогою кількох рядків наступного коду:

Примітка. Цей код - не зовсім те, що потрібно, але він не дуже далекий від бажаного результату. При розробці елемента управління стало очевидно, що необхідно пов'язати індекс зображення із зв'язаного ImageList елемента управління TreeView з кожним рівнем угруповання. Тому був доданий додатковий параметр для методу AddGroup.

Примітки до малюнка:
Group Nodes - вузли груп
Leaf Nodes - кінцеві вузли

Мал. 3. Вузли груп і кінцеві вузли

Різниця між кінцевим вузлом і вузлом групи (див. Рис. 3) дуже важливо для цієї статті, оскільки було вирішено обробляти ці два типи вузлів по-різному шляхом створення користувацьких класів для кожного типу і генерації різних подій в залежності від обраного типу вузла.

Реалізація зв'язування даних

Першим кроком в написанні коду для цього елемента управління потрібно створити проект і відповідний стартовий клас. В даному випадку спочатку створимо новий проект Windows Control Library і потім, видаливши клас UserControl за замовчуванням, замінимо його новим класом, який успадковується з елемента управління TreeView:

З цього моменту робота ведеться з елементом управління, який можна помістити в форму і зробити так, щоб він виглядав і працював точно так же, як і звичайний TreeView. Наступним кроком необхідно додати код, що обробляє нові функціональні можливості в цій версії TreeView - зв'язування і угруповання даних.

Додавання властивості DataSource

Всі функціональні можливості нового елемента управління важливі, але двома ключовими особливостями побудови складного пов'язаного з даними елемента управління є обробка властивості DataSource і отримання властивостей окремого елемента з кожного об'єкта в джерелі даних.

Створення процедури властивості

Для початку роботи необхідно, щоб будь-який елемент управління, який реалізує складне зв'язування даних, реалізував процедуру DataSource і підтримував відповідні змінні члени:

інтерфейс IList

Об'єкти, які можуть використовуватися в якості джерела даних для складного зв'язування даних, як правило, підтримують інтерфейс IList, що надає дані у вигляді колекції об'єктів, а також кілька корисних властивостей, наприклад, Count. Новому елементу управління TreeView потрібно об'єкт, який підтримує IList для його зв'язування, однак інший інтерфейс IListSource також цілком підходить, оскільки він надає простий метод (GetList) для отримання об'єкта IList. Коли властивість DataSource встановлено, спочатку рекомендується визначити, чи був наданий допустимий об'єкт, тобто який підтримує IList або IListSource. Рекомендується вибрати IList, оскільки якщо наданий об'єкт підтримує тільки IListSource (наприклад, DataTable), то для отримання допустимого об'єкта використовується метод GetList () цього інтерфейсу.

Деякі об'єкти, що реалізують IListSource (наприклад, DataSet), фактично містять кілька списків, що позначено властивістю ContainsListCollection. Якщо ця властивість має значення True, то GetList поверне об'єкт IList, що представляє список декількох подсписков. У розглянутому прикладі було вирішено підтримувати зв'язки безпосередньо з об'єктами IList або IListSource, що зберігають тільки один об'єкт IList і ігнорують такі об'єкти, як DataSet, для яких необхідно додатково визначити джерело даних.

Примітка. Якщо потрібна підтримка даного типу об'єктів (DataSet або йому подібних), можна додати другу властивість (наприклад, DataMember), що визначає використовуваний для зв'язування конкретний подсписок.

Якщо наданий допустимий джерело даних, то в кінцевому результаті буде створений екземпляр класу CurrencyManager (cm = Me.BindingContext (Value)). Цей екземпляр зберігається в локальну змінну, так як він буде використовуватися для звернення до базового джерела даних, властивостями об'єкта та інформації про позиції.

Додавання властивостей Display і Value

Наявність DataSource - це перший крок в складному зв'язуванні даних, проте елементу управління необхідно знати, які конкретні поля або властивості даних повинні використовуватися для значення і відображення. Член Display (відображення) використовуватиметься як заголовок вузлів дерева, член Value (значення) буде доступний через властивість Value вузла. Ці властивості є лише рядками, що представляють назви поля або властивості, і легко додаються до елементу управління:

В даному TreeView ці властивості становлять членів Display і Value тільки для кінцевих вузлів, відповідна інформація для кожного рівня угруповання визначається в методі AddGroup.

Використання об'єкта CurrencyManager

У розглянутому раніше властивості DataSource екземпляр класу CurrencyManager був створений і збережений в змінну рівня класу. Клас CurrencyManager, до якого звертаються через цей об'єкт, є ключовим в реалізації зв'язування даних, оскільки він має властивості, методами і подіями, що дозволяють:

  • звертатися до базового об'єкту IList джерела даних;
  • отримувати і встановлювати поля або властивості на об'єкті в джерелі даних;
  • синхронізувати елемент керування з інших питань, пов'язаних з даними елементами управління в тій же формі.

Отримання значень Property / Field

Об'єкт CurrencyManager дозволяє отримати значення властивості або поля окремих елементів в джерелі даних, наприклад, значення полів DisplayMember або ValueMember, через свій метод GetItemProperties. Потім використовуються об'єкти PropertyDescriptor, щоб отримати значення конкретного поля або властивості певного елемента списку. Наведений нижче фрагмент коду показує, як створюються ці об'єкти PropertyDescriptor, і як надалі може використовуватися функція GetValue, щоб отримати значення властивості одного з елементів в базовому джерелі даних. Зверніть увагу на властивість List об'єкта CurrencyManager: він надає доступ до примірника IList, з яким був пов'язаний елемент управління:

GetValue повертає об'єкт незалежно від типу базових даних властивості, тому перед використанням повертається значення необхідно конвертувати.

Підтримка синхронізації пов'язаних з даними елементів управління

CurrencyManager має ще одну важливу особливість: крім надання доступу до пов'язаного джерела даних і до властивостей елемента, він здійснює координацію зв'язування даних між цим елементом управління і будь-якими іншими елементами управління, що використовують той же DataSource. Ця підтримка забезпечує те, що кілька елементів управління, пов'язаних з одним джерелом даних, залишаються на тому ж самому елементі в джерелі даних. Для розглянутого елемента управління необхідно зробити так, щоб при виборі елемента в дереві будь-які інші елементи управління, пов'язані з тим же джерелом даних, вказували на один і той же елемент (запис, рядок або кортеж, використовуючи термінологію баз даних). Для цього необхідно перевизначити метод OnAfterSelect базового елементу управління TreeView. У цьому методі, що викликається після вибору вузла дерева, встановимо властивість Position об'єкта CurrencyManager на індекс поточного вибраного елемента. Типове додаток, що надається разом з елементом управління TreeView, ілюструє, як явище синхронізованих елементів управління полегшує побудову пов'язаних з даними користувальницьких інтерфейсів. Щоб полегшити визначення позиції вибраного елемента в списку, використовуйте створені користувальницькі класи TreeNode (TreeLeafNode або TreeGroupNode) і збережіть індекс списку кожного вузла в властивість Position:

Установка властивості Position об'єкта CurrencyManager дозволяє зберегти синхронізацію інших елементів управління з поточним обраним елементом, але CurrencyManager також генерує події, коли інші елементи керування змінюють позицію так, щоб можна було змінити вибраний елемент відповідним чином. Щоб побудувати невеликий хороший пов'язаний з даними компонент, вибір повинен переміщатися при зміні позиції джерела даних, і якщо ці речовини змінилися, відображення має оновитися. CurrencyManager генерує три події: CurrentChanged, ItemChanged і PositionChanged. Остання обставина досить пряме; однією з цілей CurrencyManager є управління індикатором поточної позиції для джерела даних так, щоб кілька пов'язаних елементів управління відображали ту ж запис або елемент списку, і ця подія буде генеруватися при будь-якій зміні цієї позиції. У деяких випадках інші події накладаються один на одного, тому вони не дуже зрозумілі. Тут представлена ​​розгортка їх використання в своєму призначеному для користувача елементі управління: PositionChanged - це проста подія, тому розглянемо його відразу; використовуйте його, коли необхідно скорегувати поточний вибраний елемент в складному пов'язаному з даними елементі управління, такому як дане дерево. Подія ItemChanged генерується при зміні будь-якого елементу в джерелі даних, а CurrentChanged - тільки при зміні поточного елемента.

У розглянутому TreeView було виявлено, що всі три події генеруються при виборі нового елемента, тому було вирішено обробляти подія PositionChanged при зміні поточного вибраного елемента, а два інших події не обробляти взагалі. У Документації по .Net Framework рекомендується перетворити джерело даних в IBindingList (якщо він підтримує IBindingList) і замість цього використовувати його подія ListChanged, проте ці функціональні можливості не були реалізовані:

Перетворення DataSource в дерево

Закінчивши з кодом зв'язування даних, можна приступити до роботи над додаванням решти коду, що дозволяє управляти рівнями угруповання, побудувати дерево відповідним чином і додати кілька користувальницьких подій, методів і властивостей.

управління групами

Необхідно створити функції AddGroup, RemoveGroup і ClearGroups, щоб настроїти колекцію груп. При кожній зміні колекції груп дерево має перемальовуватись (щоб відобразити нову конфігурацію), тому була створена загальна процедура GroupingChanged, що викликається різними кодом всюди в елементі управління при будь-якій зміні і оновлююча дерево: P

побудова дерева

Фактичне оновлення дерева обробляється двома процедурами: BuildTree і AddNodes. Їх код достатньо довгий, тому спробуємо зробити огляд їх поведінки і не включати весь код в статтю (звичайно, його можна завантажити). Як згадувалося раніше, розробник взаємодіє з цим елементом управління шляхом установки ряду груп, які потім використовуються в BuildTree, щоб встановити вузли дерева. BuildTree очищає поточну колекцію вузла, проходить цикл через весь джерело даних до процесів першого рівня угруповання (див. Publisher в прикладах і малюнки в цій статті вище), додаючи по одному вузлу для кожного різного значення угруповання (один вузол для кожного значення pub_id в даному прикладі ), і потім викликає AddNodes, щоб заповнити всі вузли нижче першого рівня угруповання. AddNodes викликає себе рекурсивно, щоб обробити будь-яке число рівнів, що додаються в вузли групи і кінцеві вузли відповідно. Два призначені для користувача класу, засновані на TreeNode, використовуються для розрізнення вузлів групи і кінцевих вузлів і надання кожному типу вузла свого набору релевантних властивостей.

Налаштування подій TreeView

При виборі вузла елемент управління TreeView генерує дві події: BeforeSelect і AfterSelect. Однак для елемента, що розглядається управління краще мати різні події для вузлів групи і кінцевих вузлів, тому додамо власні події BeforeGroupSelect / AfterGroupSelect і BeforeLeafSelect / AfterLeafSelect до призначених для користувача класами параметрів подій, які генеруються в додаток до основних подій:

Призначені для користувача класи вузлів (TreeLeafNode і TreeGroupNode) і призначені для користувача класи параметрів подій доступні в коді для завантаження.

Типове додаток

Щоб повністю зрозуміти весь код в цьому типовому елементі управління, проаналізуйте його роботу в додатку. Надане типове додаток працює з базою даних Microsoft Access pubs.mdb і ілюструє, як елемент управління Tree взаємодіє з іншими пов'язаними з даними елементами управління, щоб створити додатки Windows. Головною особливістю цього прикладу є те, що необхідно приділити особливу увагу включенню синхронізації Tree з іншими пов'язаними елементами управління і автоматичного вибору вузла дерева, коли в джерелі даних виконується пошук.

Примітка. Е то типове додаток (що називається "TheSample") можна завантажити для цієї статті.

Мал. 4. Демонстраційне додаток для пов'язаного з даними TreeView

Пов'язаний з даними елемент управління Tree, розглянутий в цій статті, не є рішенням для кожного проекту, що вимагає елемента управління Tree для відображення інформації з бази даних, однак ілюструє один спосіб своєї настройки для певних цілей. Не слід забувати, що велика частина коду в цьому елементі управління збігається з кодом в будь-якому складному пов'язаному з даними елементі управління, який потрібно побудувати, і що можна розширити цю існуючу роботу, щоб спростити розробку елементів управління в майбутньому.

У наступному прикладі "Малювання власних елементів управління за допомогою GDI +" буде проілюстрований значно простіший спосіб реалізації зв'язування даних в тих ситуаціях, коли не потрібно використовувати специфічний базовий клас (як в цьому елементі управління) для наслідування з елемента управління TreeView.

Форум Програміста
Новини
Огляди
магазин Програміста
Каталог посилань
Пошук
Додати файл
Зворотній зв'язок