Заполните TreeView от объекта - PullRequest
0 голосов
/ 23 февраля 2010

У меня проблема с treeview в моем приложении WinForm. Я создал класс TreeViewItem, который содержит данные. Есть только 5 полей: CaseNoteID, ContactDate, ParentNoteID, InsertUser, ContactDetails.

public class TreeItem
{
    public Guid CaseNoteID;
    public Guid? ParentNoteID;
    public string ContactDate;
    public string InsertUser;
    public string ContactDetails;

    public TreeItem(Guid caseNoteID, Guid? parentNoteID, string contactDate, string contactDetails, string insertUser)
    {
        CaseNoteID = caseNoteID;
        ParentNoteID = parentNoteID;
        ContactDate = contactDate;
        ContactDetails = contactDetails;
        InsertUser = insertUser;
    }
}

План состоял в том, чтобы показать взаимосвязь примечаний, показывая примечание под его родителем, как определено полем ParentNoteID . Довольно упрощенно на самом деле. К сожалению, все мои попытки до сих пор помещали «дочернюю» заметку, одну с ParentNoteID, в обе позиции. Первый уровень И под соответствующим Родителем.

Когда я перебираю свой код, мои данные возвращаются точно.

 List<TreeItem> items = BLLMatrix.GetTreeViewData(HUD.PersonId);
        PopulateTree(tvwCaseNotes,items);

Я просто не знаю, как это взять и точно заполнить мой TreeView. Это то, что я начал, но теперь я застрял.

  public static void PopulateTree(TreeView tree, ICollection<TreeItem> items)

Я просто не могу обернуть голову вокруг этого. Нужно ли мне разделить вызов данных и сначала вернуть все записи с помощью ParentNoteID = null, а затем пойти получить остальные и как-то соединить два?

@ Хоган: Я прошу прощения за радикальные изменения в вопросе. Из вашего ответа было видно, что я не подошел к этому с хорошей точки зрения. Во-вторых, оригинальный метод все еще не работал.

Ответы [ 3 ]

4 голосов
/ 24 февраля 2010

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

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        PopulateTreeView(treeView, SampleData());
    }

    private IEnumerable<Item> SampleData()
    {
        yield return new Item { CaseID = "1" };
        yield return new Item { CaseID = "2" };
        yield return new Item { CaseID = "3" };
        yield return new Item { CaseID = "4", ParentID = "5" };
        yield return new Item { CaseID = "5", ParentID = "3" };
        yield return new Item { CaseID = "6" };
        yield return new Item { CaseID = "7", ParentID = "1" };
        yield return new Item { CaseID = "8", ParentID = "1" };
    }

    private void PopulateTreeView(TreeView tree, IEnumerable<Item> items)
    {
        Dictionary<string, Tuple<Item, TreeNode>> allNodes = new Dictionary<string, Tuple<Item, TreeNode>>();

        foreach (var item in items)
        {
            var node = CreateTreeNode(item);
            allNodes.Add(item.CaseID, Tuple.New(item, node));
        }

        foreach (var kvp in allNodes)
        {
            if (kvp.Value.First.ParentID != null)
            {
                allNodes[kvp.Value.First.ParentID].Second.Nodes.Add(kvp.Value.Second);
            }
            else
            {
                tree.Nodes.Add(kvp.Value.Second);
            }
        }
    }

    private TreeNode CreateTreeNode(Item item)
    {
        var node = new TreeNode();
        node.Text = item.CaseID;

        return node;
    }
}

public class Item
{
    public string CaseID { get; set; }
    public string ParentID { get; set; }
}

public class Tuple<T>
{
    public Tuple(T first)
    {
        First = first;
    }

    public T First { get; set; }
}

public class Tuple<T, T2> : Tuple<T>
{
    public Tuple(T first, T2 second)
        : base(first)
    {
        Second = second;
    }

    public T2 Second { get; set; }
}

public static class Tuple
{
    //Allows Tuple.New(1, "2") instead of new Tuple<int, string>(1, "2")
    public static Tuple<T1> New<T1>(T1 t1)
    {
        return new Tuple<T1>(t1);
    }

    public static Tuple<T1, T2> New<T1, T2>(T1 t1, T2 t2)
    {
        return new Tuple<T1, T2>(t1, t2);
    }
}

Что такое кортеж?

Просто чтобы ответить на вопрос в комментарии:

Это простой контейнерный объект, содержащий два других объекта. Вот и все.

Я использовал его, потому что в моем Словаре есть уникальный идентификатор (string CaseID), который ссылается на два объекта (TreeNode и Item). Другой подход заключается в создании двух словарей как Dictionary<string, TreeNode> и Dictionary<string, Item>, но, поскольку существует отношение 1: 1, я выбрал подход кортежа.

Возможно, переименуйте его в ItemTreeNodeContainer, и вам станет понятнее, что это значит в конкретной ситуации.

1 голос
/ 26 сентября 2012

Решил мою проблему, используя решение Оливера. Просто рефакторинг, используя Tuple, часть .Net 4.0

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        PopulateTreeView(treeView1, SampleData());
    }

    private IEnumerable<Item> SampleData()
    {
        yield return new Item { CaseID = "1" };
        yield return new Item { CaseID = "2" };
        yield return new Item { CaseID = "3" };
        yield return new Item { CaseID = "4", ParentID = "5" };
        yield return new Item { CaseID = "5", ParentID = "3" };
        yield return new Item { CaseID = "6" };
        yield return new Item { CaseID = "7", ParentID = "1" };
        yield return new Item { CaseID = "8", ParentID = "1" };
    }

    private void PopulateTreeView(TreeView tree, IEnumerable<Item> items)
    {
        Dictionary<string, Tuple<Item, TreeNode>> allNodes = new Dictionary<string, Tuple<Item, TreeNode>>();

        foreach (var item in items)
        {
            var node = CreateTreeNode(item);
            allNodes.Add(item.CaseID, Tuple.Create(item, node));
        }

        foreach (var kvp in allNodes)
        {
            if (kvp.Value.Item1.ParentID != null)
            {
                allNodes[kvp.Value.Item1.ParentID].Item2.Nodes.Add(kvp.Value.Item2);
            }
            else
            {
                tree.Nodes.Add(kvp.Value.Item2);
            }
        }
    }

    private TreeNode CreateTreeNode(Item item)
    {
        var node = new TreeNode();
        node.Text = item.CaseID;

        return node;
    }

}

public class Item
{
    public string CaseID { get; set; }
    public string ParentID { get; set; }
}

Справка по кортежу на MSDN:

Класс кортежа

В моем случае я передаю источник данных из структуры сущностей: Entities.Categories и заменил класс Item классом Category, сгенерированным структурой сущности.

1 голос
/ 23 февраля 2010

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

редактирование на основе комментария

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

Вот проблемная область:

// using the Find method uses a Predicate generic delegate.
if (nodeList.Find(FindNode) == null)
{
  var tmpCNoteID = dr["CaseNoteID"].ToString();
  var filter = "ParentNote='" + tmpCNoteID + "'";

  DataRow[] childRows = cNoteDT.Select(filter);

  if (childRows.Length > 0)
  {
    // Recursively call this function for all childRows
    TreeNode[] childNodes = RecurseRows(childRows);

    // Add all childnodes to this node
    node.Nodes.AddRange(childNodes);
  }

  // Mark this noteID as dirty (already added)
  nodeList.Add(node);
}

Нечто подобное должно решить проблему, которую я вижу (примечание: это не элегантный или хороший код, это просто исправление проблемы, которую я описал выше, я бы никогда не поставил свое имя в этом коде). Кроме того, не имея возможности протестировать код, я даже не могу быть уверен, что это проблема.

// using the Find method uses a Predicate generic delegate.
if (nodeList.Find(FindNode) == null)
{
  var tmpCNoteID = dr["CaseNoteID"].ToString();
  var filter = "ParentNote='" + tmpCNoteID + "'";

  DataTable DTCopy = cNoteDT.Copy()

  DataRow[] childRows = DTCopy.Select(filter);

  if (childRows.Length > 0)
  {
    // Recursively call this function for all childRows
    TreeNode[] childNodes = RecurseRows(childRows);

    // Add all childnodes to this node
    node.Nodes.AddRange(childNodes);
  }

  // Mark this noteID as dirty (already added)
  nodeList.Add(node);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...