2010-03-13 8 views
6

Actualmente, sé cómo hacer una implementación diferida del procedimiento de carga de los nodos en un control treeview, y leer las preguntas relacionadas en stackoverflow, pero también estoy leyendo acerca de las interfaces IHierarchyData y IHierarchicalEnumerable en asp.net (no sabía codificar asp.net) que permite vincular una colección a una vista de árbol para mostrar los elementos automáticamente.IHierarchyData y IHierarchicalEnumerable en Winforms

Me gustaría saber si puedo hacer lo mismo en winforms y C#. Creo que las interfaces mencionadas anteriormente no están disponibles en winforms.

Gracias.

Respuesta

3

El Windows Forms TreeView no sabe cómo enlazar a una instancia IHierarchyData, lo cual no es sorprendente dado que las interfaces IHierarchyData y afines están destinados para el consumo de controles web (especialmente los mapas de sitio).

Sin embargo, no es muy difícil crear su propia clase de enlace de datos. Esto me pareció un problema interesante, así que armé uno solo por diversión. Te guiaré por el funcionamiento interno.

Primero, crea una clase de Componente básica. Visual Studio que se desencadene el descuento con el código de esta manera:

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

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

de una pieza obvia de "estado" de este componente tiene que tener es un mapeo de cada TreeNode a su IHierarchyData. Ahora podemos hackear esto arrojándolo en la propiedad TreeNode, Tag, pero apuntemos a hacer que este componente sea lo menos invasivo posible y realizar un seguimiento de su propio estado. Por lo tanto, usaremos un diccionario. Añadir este campo a la clase:

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

Ahora, como mínimo, este componente tiene que saber cómo llenar un padre específico TreeNode de una clase TreeView de su correspondiente obligado IHierarchyData, por lo que vamos a escribir el código siguiente:

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); 
    } 
} 

Esta parte debe ser bastante sencilla. El primer método simplemente rellena un TreeNodeCollection (es decirla propiedad Nodes de TreeNode) con la jerarquía obtenida de una instancia IHierarchyData, utilizando la interfaz IHierarchyEnumerable. Las cosas realmente interesantes solamente este método son:

  1. Adición de un nodo ficticio cuando la instancia IHierarchyData tiene hijos; esto hace que el "+" sea visible en la vista de árbol; de lo contrario, no podríamos expandirlo más; y

  2. Añadiendo el nodo recién agregado al diccionario con la instancia IHierarchyData con la que coincide.

El segundo método es aún más simple, lo hace la inicial "trabajo obligatorio", en sustitución de lo que está en la raíz del árbol con nuestra instancia de nivel superior IHierarchyData.

Lo siguiente que nuestro componente necesita hacer es enganchar los eventos de carga desde el TreeView para realizar la carga diferida. Aquí está el código para hacer esto:

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; 
    } 
} 

Los dos primeros métodos deben explicarse por sí mismo, y el tercer método es el código real perezoso de carga. Aquí estamos haciendo trampa un poco, utilizando la propiedad TreeNode.Checked para delinear si los nodos secundarios ya se han cargado o no para que no hagamos recargas innecesarias. Siempre hago esto cuando implemento árboles con carga diferida porque, en mi experiencia, casi nunca uso la propiedad TreeNode.Checked. Sin embargo, si necesita usar esta propiedad para otra cosa, puede usar una propiedad diferente (como Tag), crear otro diccionario para contener los estados expandidos o modificar el diccionario existente para contener una clase compuesta (que contiene el IHierarchyData como así como una propiedad Expanded). Lo estoy manteniendo simple por ahora.

El resto ya debería tener sentido si ha implementado la carga diferida en un árbol antes, así que salteemos. En realidad, el único que queda por hacer en este momento es poner en práctica algunas propiedades/usuario de diseño que realmente cable hasta el árbol y los datos:

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); 
     } 
    } 
} 

Peasy fácil. Tenemos una propiedad DataSource que acepta la raíz IHierarchyData, y una propiedad TreeView a la que podrá acceder desde el diseñador. Una vez más, aquí lo simple, cuando se actualiza la propiedad DataSource, simplemente reiniciamos la búsqueda y repoblamos la raíz. Cuando se actualiza la propiedad TreeView, tenemos que trabajar un poco más, registrando los eventos, asegurándonos de anular el registro de eventos de la vista de árbol anterior y haciendo las mismas cosas que hacemos cuando cambia la fuente de datos.

¡Eso es todo! Abra el diseñador de Windows Forms, coloque un TreeView, luego suelte un TreeViewHierarchyBinding y configure su propiedad TreeView en la vista en árbol que acaba de colocar. Por último, en su código en algún lugar (es decir, en el evento), darle una fuente de datos:.

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

(Nota - esto utiliza el ejemplo FileSystemHierarchyData que está en la MSDN page for IHierarchyData El ejemplo no es muy robusto, que doesn 'verifique UnauthorizedAccessException o cualquier cosa, pero es lo suficientemente bueno para demostrar esto).

Y eso es todo. Ejecuta tu aplicación y obsérvala.Ahora puede reutilizar el componente TreeViewHierarchyBinding en cualquier lugar; simplemente colóquelo en un formulario, asígnele un TreeView y asígnele una instancia IHierarchyData como fuente de datos.

He puesto el complete code on PasteBin si desea una versión de copiar y pegar.

¡Diviértete!

+0

En lugar de utilizar la propiedad Comprobada, también podría usar un nombre de nodo hijo ficticio que sea imposible o improbable (como tener un \ en un árbol del sistema de archivos). De esta forma, la validación de IsLoaded podría ser Si TreeNodeCollection.Nodes.Count == 1 y TreeNodeCollection.Nodes [0] .Text == "\ MyDummyValue /" – bkqc

1

Las interfaces están disponibles, pero requerirán que agregue una referencia a System.Web.UI. (También podría requerir el uso de .NET Framework redistribuible en lugar de Client Profile, aunque no estoy seguro de eso.)

La pregunta más importante es: ¿El control TreeView de WinForms entiende automáticamente cómo trabajar con estas interfaces? Creo que la respuesta a esa pregunta es "No", pero necesitaría probar/verificar eso.

1

Hay un interesante artículo here que le muestra cómo crear métodos de extensión para lograr lo que creo que está buscando. No hay disponibilidad nativa dentro del System.Windows.Forms.TreeView para enlazar a una colección con lo que puedo encontrar.

PUEDE incluir System.Web.UI en su proyecto para hacer que las interfaces IHierarchyData e IHierarchicalEnumerable estén disponibles, pero TreeView no podrá adjuntarlas sin los métodos de extensión.

El código fuente de muestra del sitio web le permitirá vincular cualquier colección IDictionary al TreeView.