Este apunte estudia el control Wpf TreeView para cargar dinámicamente los directorios de un disco.
Voy a usar un control Wpf, TreeView para cargar dinámicamente los directorios de un disco. Hay que emplear el evento [changed] y estudiar la forma de devolver el valor de [selectedPath], o lo que es lo mismo la cadena con el directorio completo desde el directorio raíz cuando se selecciona un directorio dentro del árbol.
El XAML es muy sencillo y sólo un detalle interesante es el momento: La forma en que suscribimos el evento Ampliado de TreeViewItem. Tenga en cuenta que este es el TreeViewItem y no el TreeView, pero debido a que el evento se propaga hacia arriba, somos capaces de simplemente capturar en un solo lugar para todo el TreeView, en lugar de tener que suscribirse a él para cada elemento lo añadimos al árbol. Este evento se llama cada vez que un elemento se expande, lo que tenemos que ser conscientes de que cargar sus elementos secundarios bajo demanda.
<UserControl x:Class="UC_TreeViewDirectory" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:WPF_Explorer_Tree_Traduccion" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> <Grid> <TreeView x:Name="trvArbolDirectoriosDisco" Background="AliceBlue" TreeViewItem.Expanded="TreeViewItem_Expanded" SelectedItemChanged="trvArbolDirectoriosDisco_SelectedItemChanged" Margin="10" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" HorizontalContentAlignment="Left"/> </Grid> </UserControl>
En código subyacente, comenzamos añadiendo al control TreeView (en el constructor del control, justo después de la inicialización general,) las unidades de disco (o CD-ROM que tiene el equipo.
La función [CreateTreeItem] se encarga de crear un nodo [TreeViewItem] y carga en el nuevo nodo la información del nodo. Observa dos cosas. Que no se cuelga del árbol, solo se crea el nodo, y que en la propiedad [Tag] guardo, normalmente el nombre objeto [DirectoryInfo] que contiene el directorio que voy a almacenar [Pe. C:\uno\dos\tres], y (solo una por disco) un objeto [DriveInfo] que contiene el directorio raíz de la unidad de disco [Pe. C:\]. El valor de esta propiedad se usa para recuperar el valor [SelectedItem] o lo que es lo mismo, una cadena con la ruta completa desde el directorio raíz del directorio seleccionado en el árbol. La función [CreateTreeItem] también añade a cada nodo un nodo hijo del tipo [_NodoNothing] que es en realidad un nodo [Object] con valor [Nothing]
Para añadir el nodo al árbol (TreeView), se utiliza el evento Ampliado [TreeViewItem.Expanded]. Este evento se genera cada vez que un elemento TreeView se expande, así que lo primero que hacemos es comprobar si en ese nodo ya se han cargado los directorios del disco, para ello miramos si los elementos hijos contienen un solo nodo y además es un objeto del tipo [_NodoNothing], si es así, significa que esta vaciao, que ahora debemos cargar el contenido real y sustituir el elemento marcador [_NodoNothing], con la lista de directorios hijos del nodo.
Observa que el bucle donde añadimos cada carpeta hija está en un bloque [try – catch], ya que algunos directorios, (por lo general) por razones de seguridad, pueden no ser accesibles. De esta forma podemos interceptar la excepción y reflejar esto en la interfaz de una manera u otra.
Imports System.IO ''' <summary> ''' Control de usuario que muestra en forma de árbol los directorios de un disco ''' </summary> <Serializable> Public Class UC_TreeViewDirectory Implements System.ComponentModel.INotifyPropertyChanged #Region "Evento PropertyChanged [Versión 2014-02-13]" '----------------------------------------------------------------------- ' Declaración del evento usando un EventHandler genérico ' !! Observación !!! Se produce un error al serializar eventos Genéricos <NonSerializedAttribute()> Public Event PropertyChanged As _ System.ComponentModel.PropertyChangedEventHandler _ Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged '---------------------------------------------------------------------- ''' <summary>Función que dispara el evento [PropertyChanged]</summary> Protected Overloads Sub OnPropertyChanged(ByVal e As ComponentModel.PropertyChangedEventArgs) If e Is Nothing Then Throw New ArgumentException("No se admiten valores Nothing") End If Call OnPropertyChanged(e.PropertyName) End Sub '---------------------------------------------------------------------- ''' <summary>Función que dispara el evento [PropertyChanged]</summary> ''' <param name=nombreDeLaPropiedadChanged">" ''' <para>Se espera una cadena de texto con uno de los siguientes valores:</para> ''' <para> a) Una cadena vacía, en este caso la función averiguara el ''' nombre de la propiedad a través de su ''' atributo [CallerMemberName]]</para> ''' <para> b) Una cadena de texto con el nombre de ''' la propiedad (Ejemplo, Apellidos") </para>" ''' </param> ''' <remarks> ''' <code> ''' system.componentmodel.inotifypropertychanged(VS.95).aspx ''' http://msdn.microsoft.com/es-es/library/ ''' Bibliografía [CallerMemberName>] ''' http://msdn.microsoft.com/es-es/library/hh534540.aspx ''' </code> '''</remarks> Protected Overloads Sub OnPropertyChanged( <System.Runtime.CompilerServices.CallerMemberNameAttribute> Optional ByVal nombreDeLaPropiedadChanged As String = Nothing) ' ---------------------------------- ' Evitar problemas tontos If String.IsNullOrWhiteSpace(nombreDeLaPropiedadChanged) = False Then ' Disparar el evento RaiseEvent PropertyChanged( Me, New System.ComponentModel.PropertyChangedEventArgs(nombreDeLaPropiedadChanged)) End If End Sub ' ' /Eof - PropertyChanged '----------------------------------------------------------------------- #End Region ''' <summary> ''' representa a un nodo [TreeViewItem] nulo ''' Se usa para indicar que este nodo (del árbol) no tiene nodos hijos ''' </summary> Private _NodoNothing As Object = Nothing ''' <summary> ''' Contiene la cadena completa desde el nodo raíz del nodo seleccionado del árbol ''' </summary> Private _selectedItem As String = String.Empty ''' <summary> ''' Contiene la cadena completa desde el nodo raíz del nodo seleccionado del árbol ''' </summary> ''' <remarks>Utiliza [INotifyPropertyChanged] para que funcione [binding] en una ventana</remarks> Public Property ZSelectedItem() As String Get Return _selectedItem End Get Set(value As String) If String.Equals(_selectedItem, value) = False Then _selectedItem = value ' Disparar el evento [Changed] Call OnPropertyChanged() End If End Set End Property ''' <summary> ''' Constructor, se cargan directamente las unidades de disco ''' </summary> Public Sub New() InitializeComponent() Dim drives As DriveInfo() = DriveInfo.GetDrives() For Each driveInfo__1 As DriveInfo In drives trvArbolDirectoriosDisco.Items.Add(CreateTreeItem(driveInfo__1)) ' aquí no uso añadir hijos [AddTreeItem], porque estoy añadiendo los nodos root del árbol Next End Sub ''' <summary> ''' cuando se expande un nodo se dispara el evento expanded ''' aquí se cargan los nodos hijos (los directorios hijos) ''' de ese directorio expandido ''' </summary> Private Sub TreeViewItem_Expanded(sender As Object, e As RoutedEventArgs) Dim itemPadre As TreeViewItem = TryCast(e.Source, TreeViewItem) If (itemPadre.Items.Count = 1) AndAlso (itemPadre.Items(0) Is _NodoNothing) Then itemPadre.Items.Clear() Dim diretorioExpandido As DirectoryInfo = Nothing If TypeOf itemPadre.Tag Is DriveInfo Then diretorioExpandido = TryCast(itemPadre.Tag, DriveInfo).RootDirectory End If If TypeOf itemPadre.Tag Is DirectoryInfo Then diretorioExpandido = TryCast(itemPadre.Tag, DirectoryInfo) End If Try For Each nodoHijo As DirectoryInfo In diretorioExpandido.GetDirectories() Call AddTreeItem(itemPadre, nodoHijo) Next Catch ex As Exception ' no hacer nada ' en este caso significa que no hay acceso a es directorio ' normalmente por temas de seguridad de acceso End Try End If End Sub ''' <summary> ''' Auxiliar que da de alta un nodo hijo ''' </summary> Private Sub AddTreeItem(ByVal padre As TreeViewItem, ByVal nodoHijo As Object) ' esta función no realiza control de errores ni de parámetros de entrada ' porque es responsabilidad de la función llamadora '------------------------------- If TypeOf (nodoHijo) Is DirectoryInfo Then ' A) Tratar el caso de Directorios que es lo mas normal ' Excluir directorios ocultos Dim objDirInfo As DirectoryInfo = CType(nodoHijo, DirectoryInfo) If (objDirInfo.Attributes And FileAttributes.Hidden) <> FileAttributes.Hidden Then padre.Items.Add(CreateTreeItem(objDirInfo)) End If Else 'B) Tratar el caso de las unidades de disco If TypeOf (nodoHijo) Is DriveInfo Then padre.Items.Add(CreateTreeItem(nodoHijo)) End If End If End Sub ''' <summary> ''' auxiliar crea un nodo hijo ''' </summary> Private Function CreateTreeItem(ByVal directorio As Object) As TreeViewItem '------------------------------------- ' Una observación Importante ' A) En la propiedad [Tag] guardo : ' - Normalmente el nombre objeto [DirectoryInfo] que contiene el directorio ' que voy a almacenar [Pe. C:\uno\dos\tres ] ' - pocas veces (solo una por disco) un objeto [DriveInfo] que contiene ' el directorio raíz de la unidad de disco [Pe. C:\] ' B) El valor de esta propiedad se usa para recuperar el valor [SelectedItem] o lo que es lo mismo, ' una cadena con la ruta completa desde el directorio raíz del directorio seleccionado en el árbol. '------------------------------------- Dim item As New TreeViewItem() item.Header = directorio.ToString() item.Tag = directorio item.Items.Add(_NodoNothing) Return item End Function ''' <summary> ''' Evento selected item changed ''' se dispara cuando cambia el nodo seleccionado ''' </summary> Private Sub trvArbolDirectoriosDisco_SelectedItemChanged(sender As Object, e As RoutedPropertyChangedEventArgs(Of Object)) Try Dim arbol As TreeView = DirectCast(sender, TreeView) Dim nodoSeleccionado As TreeViewItem = DirectCast(arbol.SelectedItem, TreeViewItem) ZSelectedItem = String.Empty If nodoSeleccionado Is Nothing Then Return End If '------------------------------------- ' Una observación Importante ' A) En la propiedad [Tag] guardo : ' - Normalmente el nombre objeto [DirectoryInfo] que contiene el directorio ' que voy a almacenar [Pe. C:\uno\dos\tres ] ' - pocas veces (solo una por disco) un objeto [DriveInfo] que contiene ' el directorio raíz de la unidad de disco [Pe. C:\] ' B) El valor de esta propiedad se usa para recuperar el valor [SelectedItem] o lo que es lo mismo, ' una cadena con la ruta completa desde el directorio raíz del directorio seleccionado en el árbol. '------------------------------------- If TypeOf (nodoSeleccionado.Tag) Is DirectoryInfo Then ' A) Tratar el caso de Directorios que es lo mas normal Dim di As DirectoryInfo = CType(nodoSeleccionado.Tag, DirectoryInfo) ZSelectedItem = di.FullName Else 'B) Tratar el caso de las unidades de disco If TypeOf (nodoSeleccionado.Tag) Is DriveInfo Then Dim di As DriveInfo = CType(nodoSeleccionado.Tag, DriveInfo) ZSelectedItem = di.Name End If End If Catch ex As Exception ' no trato el error ZSelectedItem = String.Empty End Try End Sub End Class
© 1997 - - La Güeb de Joaquín | |||||
Joaquín Medina Serrano
|
|||||
|
Codificación | |
Fecha de creación | |
Última actualización | |
![]() |