C# TreeView дизайн - най-добрият начин за показване на дървовидна структура?

Опитвам се да използвам TreeView за показване на дървовидна структура от обекти. Имам дърво от четири типа обекти, компания (основният възел), град, магазин и служител.

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

Чудя се кой е правилният начин да накарам TreeView да показва дървовидната структура и да получава актуализации, когато се промени.

Мисля, че обектът Company трябва да има събития, като company.CityAdded и company.CityRemoved, тогава каквато и обвивка да поставя около TreeView, отговаря на тези събития? Когато TreeView бъде изграден, ще има възел за всеки град/магазин/служител. След това всеки възел може да отговори на събитията на възела, който представлява в дървото.

Това правилната идея ли е? Или има по-добър метод?


person Community    schedule 06.02.2009    source източник


Отговори (5)


Просто исках да добавя, че ако WPF е опция за това, става невероятно просто с помощта на heirarchtical databinding и observablecollections. По същество той обработва всички неща вместо вас и ви позволява просто да взаимодействате с вашите бизнес обекти.

person Jacob Adams    schedule 07.02.2009

Звучи сякаш сте на прав път. Трябваше да направя подобно нещо, няколко насоки, които бих искал да споделя:

  1. Съхранявайте препратка към обект в свойството на етикет TreeNode.

  2. Дайте на всеки Treenode уникално име, което може лесно да идентифицира обект, например: хешкод на обект, ID на фирма и т.н.

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

Късмет.

person WebMatrix    schedule 06.02.2009

Правилно сте относно концепцията за слушане на събития (това е стандартен модел издател/абонат).

За действителното актуализиране на дървовидния изглед имам два метода: 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);
    }

    ...
}
person Neil Barnwell    schedule 06.02.2009
comment
+1 за споменаването на BeginInvoke(). Може обаче да искате да промените примера си, за да покажете това. - person Matt Jordan; 10.02.2009

Вместо ...

  • Бизнес обектите се абонират за UI събития
  • Командите актуализират потребителския интерфейс
  • Бизнес обектите се актуализират, когато потребителският интерфейс се актуализира

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

person ChrisW    schedule 06.02.2009

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

Когато обектът, представляващ „Store X“, се актуализира с ново име и задейства събитие, за да обяви, че това се е случило, кой обект консумира събитието?

По същия начин, когато се добави град Y, кой обект трябва да бъде уведомен за създаването?

Един често срещан подход е да има някакъв вид голям uber-manager клас, който управлява целия процес - той се абонира за всички събития и прави всичко.

Различен подход, който съм използвал за добър ефект, е да се създадат много по-прости обвиващи/координаторни обекти, които обработват само една част от пъзела. Обикновено добавям към името на тези класове суфикс "Editor".

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

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

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

person Bevan    schedule 08.02.2009