2009-02-06 2 views
4

Я пытаюсь использовать TreeView для отображения древовидной структуры объектов. У меня есть дерево из четырех типов объектов, Company (корневой узел), City, Store и Employee.C# TreeView design - лучший способ отобразить древовидную структуру?

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

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

Я думаю, что объект компании должен иметь такие события, как company.CityAdded и company.CityRemoved, то какая оболочка, которую я обернула вокруг TreeView, отвечает на эти события? Когда TreeView будет создан, будет узел на город/магазин/сотрудник. Затем каждый узел мог отвечать на события узла, который он представляет в дереве.

Это правильная идея? Или есть лучший метод?

ответ

1

Вы находитесь на правильных строках о концепции прослушивания событий (это стандартный шаблон издателя/подписчика).

Для фактического обновления древовидной структуры я имею тенденцию иметь два метода: AddOrUpdateTreeItem и RemoveTreeItem. Метод добавления или обновления делает то, что он говорит, ищет элемент дерева (на основе пути) и обновляет его или добавляет его. Конечно, если модель обновляется в потоке, отличном от того, на котором была создана форма, вам необходимо будет маршировать вызов, используя Control.BeginInvoke().

Этот подход может быть немного медленным, если вы заполняете полное дерево в form_load или что-то в этом роде, поэтому у вас может быть другой метод для начальной совокупности и использовать концепцию, описанную здесь для последующих обновлений.

Я делаю то же самое для списков, вот типичный пример. Основное отличие при добавлении элемента дерева состоит в том, что вам может понадобиться добавить родительские узлы, чтобы добавить запрашиваемый узел, что делает его немного рекурсивным. Попробуйте.

private void AddOrUpdateListItem(DomainModelObject item) 
{ 
    ListViewItem li = lvwListView.Items[GetKey(item)]; 

    if (li == null) 
    { 
     li = new ListViewItem 
       { 
        Name = GetKey(item), 
        Tag = item 
       }; 
     li.SubItems.Add(new ListViewItem.ListViewSubItem()); 
     li.SubItems.Add(new ListViewItem.ListViewSubItem()); 
     li.SubItems.Add(new ListViewItem.ListViewSubItem()); 
     li.ImageIndex = 0; 
     lvwListView.Items.Add(li); 
    } 

    li.Text = [Itemtext]; 
    li.SubItems[1].Text = [Itemtext]; 
    li.SubItems[2].Text = [Itemtext]; 
    li.SubItems[3].Text = [Itemtext]; 
} 

Вот пример того, как BeginInvoke() может быть реализована:

public class MyForm : Form 
{ 
    ... 

    void data_Changed(object sender, DataChangedEventArgs e) 
    { 
     if (this.InvokeRequired) 
     { 
      this.BeginInvoke(new EventHandler<DataChangedEventArgs>(data_Changed), sender, e); 
      return; 
     } 

     AddOrUpdateListItem(e.DataItem); 
    } 

    ... 
} 
+0

+1 для упоминания BeginInvoke(). Вы можете изменить свой пример, чтобы показать это. –

1

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

  1. Хранить ссылку на объект в свойстве тега TreeNode.

  2. Раздайте TreeNode уникальное имя, которое можно легко идентифицировать объект, например: объект, хэш-код компании ID и т.д.

Таким образом, вы можете легко найти и обновить TreeNode, когда состояние объекта изменяется. И когда пользователь выбирает узел, вы можете захватить объект, который он представляет из свойства Tag.

Удачи.

0

Вместо ...

  • Бизнес-объекты подписаться на события пользовательского интерфейса
  • Команды обновления пользовательского интерфейса
  • Бизнес-объекты обновляются при обновлении интерфейса

... Вы также можете сделать это наоборот (т.е. команды обновляют дерево бизнес-объектов, что приводит к соответствующему обновлению пользовательского интерфейса).

2

Я просто хотел добавить, что если WPF является вариантом для этого, он становится невероятно простым, используя heirarchtical databinding и observablecollections. Он в основном делает все события для обработки событий для вас и позволяет вам просто взаимодействовать с вашими бизнес-объектами.

0

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

Когда объект, представляющий «Store X», обновляется с новым именем и запускает событие, чтобы сообщить об этом, какой объект использует событие?

Аналогичным образом, когда добавляется City Y, какой объект должен быть уведомлен о создании?

Один общий подход заключается в том, чтобы иметь какой-то большой класс uber-manager, который обрабатывает весь процесс - он подписывается на все события и делает все.

Другой подход, который я использовал для хорошего эффекта, заключается в создании гораздо более простых объектов-оберток/координаторов, которые обрабатывают только одну часть головоломки. Как правило, я суффикс имени этих классов с "Editor".

Итак, у вас может быть класс CityEditor, конструктор которого принимает как объект City, так и TreeNode, который представляет этот объект. CityEditor будет подписаться на события как на объекте City, так и на TreeNode, и позаботится о заполнении TreeNode подписью и выбором значка.

Когда объект City обновляется, CityEditor отвечает на вызванное событие, обновляя TreeNode. Когда объект City удален, CityEditor удостоверяет, что узел удален из Treeview.

Когда новый объект Store добавлен в City, CityEditor может позаботиться о создании StoreEditor для координации обновлений на этом уровне. Аналогичным образом, когда Employee добавляется к Store, экземпляр EmployeeEditor обрабатывает обновления Treeview.

Смежные вопросы