IHierarchyData и IHierarchicalEnumerable в Winforms

В настоящее время я знаю, как сделать ленивую реализацию процедуры загрузки узлов в управлении treeview и считать связанные вопросы в stackoverflow, но я также читаю об интерфейсах IHierarchyData и IHierarchicalEnumerable в asp.net (я не знал для кодирования asp.net), которые позволяют связывать набор с treeview для отображения объектов автоматически.

Это хотело бы знать, могу ли я сделать то же в winforms и C#. Я думаю, что интерфейсы, предыдущие упомянутый, не доступны в winforms.

Спасибо.

6
задан Jonathan 13 March 2010 в 13:08
поделиться

3 ответа

Windows Forms TreeView не знает, как выполнить привязку к IHierarchyData , что неудивительно, учитывая, что IHierarchyData и связанные интерфейсы предназначены для использования веб-элементами управления (особенно картами сайтов).

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

Сначала создайте базовый класс Component. Visual Studio начнет вас с такого кода:

public partial class TreeViewHierarchyBinding : Component
{
    public TreeViewHierarchyBinding()
    {
        InitializeComponent();
    }

    public TreeViewHierarchyBinding(IContainer container)
    {
        container.Add(this);
        InitializeComponent();
    }
}

Один очевидный элемент «состояния», который должен иметь этот компонент, - это отображение каждого TreeNode на его IHierarchyData . Теперь мы можем обойти это, добавив его в свойство Tag в TreeNode , но давайте постараемся сделать этот компонент как можно более неинвазивным и отслеживать его собственное состояние. Следовательно, мы будем использовать словарь. Добавьте это поле в класс:

private Dictionary<TreeNode, IHierarchyData> nodeDictionary = new 
    Dictionary<TreeNode, IHierarchyData>();

Теперь, как минимум, этот компонент должен знать, как заполнить конкретный родительский TreeNode класса TreeView из его соответствующего связанного ] IHierarchyData , так что давайте напишем следующий код:

private void PopulateChildNodes(TreeNodeCollection parentCollection,
    IHierarchicalEnumerable children)
{
    parentCollection.Clear();
    foreach (object child in children)
    {
        IHierarchyData childData = children.GetHierarchyData(child);
        TreeNode childNode = new TreeNode(childData.ToString());
        if (childData.HasChildren)
        {
            childNode.Nodes.Add("Dummy");   // Make expandable
        }
        nodeDictionary.Add(childNode, childData);
        parentCollection.Add(childNode);
    }
}

private void UpdateRootNodes(TreeView tv, IHierarchyData hierarchyData)
{
    if (tv == null)
    {
        return;
    }
    tv.Nodes.Clear();
    if (hierarchyData != null)
    {
        IHierarchicalEnumerable roots = hierarchyData.GetChildren();
        PopulateChildNodes(tv.Nodes, roots);
    }
}

Эта часть должна быть довольно простой. Первый метод просто заполняет TreeNodeCollection (то есть свойство Nodes узла TreeNode ) иерархией, полученной из экземпляра IHierarchyData , используя интерфейс IHierarchyEnumerable . Единственные действительно интересные вещи, которые делает этот метод:

  1. Добавление фиктивного узла, когда у экземпляра IHierarchyData есть дочерние элементы; это делает "+" видимым в древовидном представлении, иначе мы не смогли бы развернуть его глубже; и

  2. Добавление вновь добавленного узла в словарь с экземпляром IHierarchyData , с которым он соответствует.

Второй метод еще проще, он выполняет начальную «работу привязки», заменяя все, что находится в корне дерева, нашим экземпляром IHierarchyData верхнего уровня.

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

private void RegisterEvents(TreeView tv)
{
    tv.BeforeExpand += TreeViewBeforeExpand;
}

private void UnregisterEvents(TreeView tv)
{
    tv.BeforeExpand -= TreeViewBeforeExpand;
}

private void TreeViewBeforeExpand(object sender, TreeViewCancelEventArgs e)
{
    if (e.Node.Checked)
    {
        return;
    }
    IHierarchyData hierarchyData;
    if (nodeDictionary.TryGetValue(e.Node, out hierarchyData))
    {
        PopulateChildNodes(e.Node.Nodes, hierarchyData.GetChildren());
        e.Node.Checked = true;
    }
}

Первые два метода не требуют пояснений, а третий метод - это фактический код с отложенной загрузкой. Здесь мы немного обманываем, используя свойство TreeNode.Checked , чтобы определить, были ли дочерние узлы уже загружены, поэтому мы не делаем ненужных перезагрузок. Я всегда делаю это, когда реализую ленивые деревья, потому что, по моему опыту, я почти никогда не использую свойство TreeNode.Checked . Однако, если вам действительно нужно использовать это свойство для чего-то еще, вы можете использовать другое свойство (например, Tag ), создать другой словарь для хранения расширенных состояний или изменить существующий словарь для хранения составного класс (содержащий свойство IHierarchyData , а также свойство Expanded ). Я пока не усложняю.

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

private IHierarchyData dataSource;
private TreeView treeView;

[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public IHierarchyData DataSource
{
    get { return dataSource; }
    set
    {
        if (value != dataSource)
        {
            dataSource = value;
            nodeDictionary.Clear();
            UpdateRootNodes(treeView, value);
        }
    }
}

[Category("Behavior")]
[DefaultValue(null)]
[Description("Specifies the TreeView that the hierarchy should be bound to.")]
public TreeView TreeView
{
    get { return treeView; }
    set
    {
        if (value != treeView)
        {
            if (treeView != null)
            {
                UnregisterEvents(treeView);
            }
            treeView = value;
            nodeDictionary.Clear();
            RegisterEvents(value);
            UpdateRootNodes(treeView, dataSource);
        }
    }
}

Легко.У нас есть свойство DataSource , которое принимает корневой IHierarchyData , и свойство TreeView , к которому вы сможете получить доступ из дизайнера. Опять же, здесь все просто: когда свойство DataSource обновляется, мы просто сбрасываем поиск и повторно заполняем корень. Когда свойство TreeView обновляется, нам нужно проделать еще немного работы, зарегистрировав события,не забудьте отменить регистрацию событий в старом древовидном представлении и сделать все то же, что и при изменении источника данных.

Вот и все! Откройте конструктор Windows Forms, перетащите TreeView , затем перетащите TreeViewHierarchyBinding и установите его свойство TreeView для только что отброшенного представления в виде дерева. Наконец, где-нибудь в вашем коде (например, в событии Form_Load ) укажите источник данных:

private void Form1_Load(object sender, EventArgs e)
{
    DirectoryInfo dir = new DirectoryInfo("C:\\");
    treeViewHierarchyBinding1.DataSource = new FileSystemHierarchyData(dir);
}

(Примечание - здесь используется пример FileSystemHierarchyData , который находится на MSDN страница для IHierarchyData . Пример не очень надежен, он не проверяет исключение UnauthorizedAccessException или что-то еще, но его достаточно, чтобы продемонстрировать это).

Вот и все. Запустите приложение и посмотрите, как оно связывается. Теперь вы можете повторно использовать компонент TreeViewHierarchyBinding где угодно - просто поместите его в форму, назначьте ему TreeView и предоставьте ему экземпляр IHierarchyData в качестве источника данных.

Я поместил полный код в PasteBin , если вам нужна версия для копирования и вставки.

Удачи!

3
ответ дан 17 December 2019 в 04:45
поделиться

Интерфейсы доступны, но вам потребуется добавить ссылку на System.Web.UI. (Также может потребоваться, чтобы вы использовали полный распространяемый компонент .NET Framework, а не профиль клиента, хотя я не уверен в этом.)

Более важный вопрос: понимает ли элемент управления WinForms TreeView автоматически, как работать с этими интерфейсы? Я считаю, что ответ на этот вопрос - «Нет», но вам нужно будет проверить / подтвердить это.

1
ответ дан 17 December 2019 в 04:45
поделиться

Есть интересная статья здесь, которая показывает, как создавать методы расширения для достижения того, что, я думаю, вы ищете. Насколько я могу судить, в System.Windows.Forms.TreeView нет возможности привязки к коллекции.

Вы можете включить System.Web.UI в свой проект, чтобы сделать доступными интерфейсы IHierarchyData и IHierarchicalEnumerable, но TreeView не сможет привязаться к ним без методов расширения.

Пример исходного кода с веб-сайта позволит вам привязать любую коллекцию IDictionary к TreeView.

1
ответ дан 17 December 2019 в 04:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: