none
como recorrer los controles de una ventana en wpf RRS feed

  • Pregunta

  • Hola

    soy nueva usando wpf, e intento recorrer todos los controles de una ventana para modificar habilitar o deshabilitar sus propiedades, la accion anterior en windows form lo realizo asi:

    private void BuscarControl(Control.ControlCollection Controles, BECtrlPermiso PermisoCtrl)

    {

     

    foreach (Control c in Controles)

    {

     

    if (c.Controls.Count > 0)

    {BuscarControl(c.Controls, PermisoCtrl);}

     

     

    if (c is MenuStrip)

    {

     

    BuscaToolStipItems(((MenuStrip)(c)).Items, PermisoCtrl);}

     

     

    if (c is ToolStrip)

    {

     

    ToolStrip toolStrip = c as ToolStrip;BuscaToolsStrip(toolStrip.Items,PermisoCtrl); }

     

     

    if (c is Button || c is ComboBox || c is TextBox ||c is ListBox || c is DataGridView || c is RadioButton ||

    c

     

    is RichTextBox || c is TabPage)

    {

     

    if (c.Name == PermisoCtrl.NombreCtrl)

    {c.Enabled = PermisoCtrl.Activo;c.Visible = PermisoCtrl.Visible;break;}

    }}}

    Le envio como parametro la coleccion de controles de un formulario...

    Entonces requiero hacer lo mismo en wpf, he intento recorrer los controles de una ventana, pero solo ubico la propiedad content y solo visualizo en tiempo de ejecucion los controles y nose como referenciarlos en codigo o si existe alguna otra forma.

    saludos, ojala puedan apoyarme.

    martes, 7 de septiembre de 2010 17:57

Respuestas

Todas las respuestas

  • Hola Rosario.

    Hace tiempo, escribimos una funcion en el foro que realiza una busqueda de un control en el arbol visual WPF, esta funcion la puedes encontrar en el siguiente post:

    http://social.msdn.microsoft.com/Forums/es-ES/wpfes/thread/8956494f-6fd0-4cb8-a567-82b22b9f7c4c

     

    La funcion es 'GetChildObjects' y sus variantes y en el post anterior la puedes ver en c# y en VB traducida por nuestro amigo Corsario.

    Si tienes alguna duda de como usarla comentalo


    Saludos
    David González
    MCP.
    Visita mi Blog en: http://www.dgzornoza.com/
    • Propuesto como respuesta CorsarioVasco martes, 7 de septiembre de 2010 20:36
    • Marcado como respuesta Rosario de Alba lunes, 13 de septiembre de 2010 19:08
    martes, 7 de septiembre de 2010 19:36
  • Hola David.

           Gracias por tu aportación, ya la estuve probando y me parece excelente, no  he empezado a aplicarlo a mi problema planteado

    anteriormente, pero una vez quede se los comparto, saludos y gracias de nuevo :).

     

    Excelente foro :)

     

    Saludos.

    Rosario de Alba.

     

    viernes, 10 de septiembre de 2010 15:30
  • ¡Hola Rosario!

    Marcale a David la respuesta como correcta para que quede cerrada. Gracias.

     

    Un saludo,

    viernes, 10 de septiembre de 2010 16:39
  • Hola, espero haber marcado como respuesta correctamente, disculpe la tardanza, me confundi jeje.

    Saben continuo trabajando con los controles, la ideas es evaluar todos los controles wpf  de una ventana ya sea control nativo de wpf o un control de usuario wpf, mi proyecto fue creado como un Ribbon Application WPF, por tanto recorri los controles del Ribbon de distinta formas con la propiedad de ItemCollection. Todo lo anterior es para modificar las propiedades de Visibility y IsEnabled segun el permiso.

    Primero.

    1. Maneje por separado el recorrido de los Items de un Ribbon porque con el metodo GetChildObjects me daba el valor de 0, espero haya sido la forma correcta, me funciona.  Espero su opinion

    private static int buscarItemRibbon(ItemCollection ListaItems, BECtrlPermiso Permiso)

    {

    foreach (object ItemX in ListaItems)

    {

      if (((FrameworkElement)ItemX).Name == Permiso.NombreCtrl)

    {((FrameworkElement)ItemX).IsEnabled = Permiso.Activo;

    if (Permiso.Visible)

    ((FrameworkElement)ItemX).Visibility = Visibility.Visible;

    else

    ((FrameworkElement)ItemX).Visibility = Visibility.Hidden;

    return 1;

    }

    else

    if (ItemX is ItemsControl)

    if (((ItemsControl)ItemX).Items.Count > 0)

    buscarItemRibbon(((ItemsControl)ItemX).Items, Permiso);

     }

    return 0;

    }

    2. Por otro lado al recorrer los controles existente en un grid usando la propiedad GetChildObjects en este caso de la ventana principal lo hice asi :

    public static int wpfBuscarControl(DependencyObject Objeto, BECtrlPermiso Permiso)

    {

     for (int i = 0; i < VisualTreeHelper.GetChildrenCount(Objeto); i++)

    {

    object c1 = VisualTreeHelper.GetChild(Objeto, i);

    //En esta sentencia evaluo los controles simples: botones, etiquetas y tambien los itemscontrols, porque solo

    //me interesa aplicar permiso al control de tipo items control y no asus items.

     if (((FrameworkElement)c1).Name == Permiso.NombreCtrl)

    { ((FrameworkElement)c1).IsEnabled = Permiso.Activo;

     if (Permiso.Visible)

    ((FrameworkElement)c1).Visibility = Visibility.Visible;

    else

    ((FrameworkElement)c1).Visibility = Visibility.Hidden;

    return 1;

    }

    else

    //Aqui considero el Ribbon de la ventana principal

    if (c1 is Ribbon)

    {

    int res = buscarItemRibbon(((Ribbon)c1).Items, Permiso);

    if (res == 1) return 1;

    }

    else

    //En esta sentencia evaluo solo los controles que son paneles, Aqui mi duda es ¿si puede existir otro tipo de control

    //que pueda contener controles? y no este considerando,

    if (VisualTreeHelper.GetChildrenCount((DependencyObject)c1) > 0 & (c1 is Panel))

    wpfBuscarControl((DependencyObject)c1, Permiso);

     

    // if (VisualTreeHelper.GetChildrenCount((DependencyObject)c1) > 0 & ((c1 is Panel) || (c1 is UserControl)))

    //Tuve que separar para evaluar los controles de usuarios de la sentencia anterior, porque sino no entraba a evaluar

    // los controles existente en el el control de usuario wpf debido a que el primer hijo obtenido era un tipo

    //c1 = {System.Windows.Controls.Border} y ya no continuaba con la recursividad.

    if (VisualTreeHelper.GetChildrenCount((DependencyObject)c1) > 0 & (c1 is UserControl))

    {

    wpfBuscarUsrCtrl((DependencyObject)c1, Permiso);

    }

    }

    return 0;

    }

    //Busca  los controles existente en el el control de usuario wpf 

     public static int wpfBuscarUsrCtrl(DependencyObject Objeto, BECtrlPermiso Permiso)

    {

    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(Objeto); i++)

    {

    object c1 = VisualTreeHelper.GetChild(Objeto, i);

    if (((FrameworkElement)c1).Name == Permiso.NombreCtrl)

    {

    ((FrameworkElement)c1).IsEnabled = Permiso.Activo;

    if (Permiso.Visible)

    ((FrameworkElement)c1).Visibility = Visibility.Visible;

    else

    ((FrameworkElement)c1).Visibility = Visibility.Hidden;

    return 1;

    }

    else

    if (VisualTreeHelper.GetChildrenCount((DependencyObject)c1) > 0)

    {

    wpfBuscarUsrCtrl((DependencyObject)c1, Permiso);

    }

    }

    return 0;

    }

    Espero haberme explicado bien en lo que hice para recorrer los controles, mi duda si en base a su experiencia ¿se esta haciendo de forma correcta? ¿Estoy omitiendo controles y no estoy abarcando todos? como comentaba al inicio la idea es modificar sus propiedades y abarcar los controles comunes de wpf.

    Gracias por su atencion. Saludos :) .

     

    jueves, 23 de septiembre de 2010 16:26
  • Hola Rosario.

    La funcion GetChildObjects, hace uso del VisualTreeHelper recursivamente, la puedes adaptar para recorrer los controles de la forma que desees.

    1.- siempre exitira una forma mas correcta para un caso concreto, solo tienes que evaluar el coste y si compensa, si el codigo sige una metodologia y estructura bien definida y sobre todo funciona, se puede considerar una de las opciones validas. Sobre esto hay mucha miga de la que hablar de modo que al final, son tus conocimientos los que deben evaluarlo. Yo no veo mal la forma de recorrer los ribbon, tan solo veo que hace mucho casting y queda muy espeso el codigo a parte del proceso que supone.

    En lugar de hacer esto a menudo:

     

    ((System.Windows.FrameworkElement)ItemX)
    

     

    deberias hacerlo solo una vez al principio, hacer un casting de tipos y usarlo siempre:

     

    FrameworkElement controlItemX = (System.Windows.FrameworkElement)ItemX;
    

     

    Luego, un problema que puedes tambien tener, es que has creado mucha bifurcacion, para seguirla, hacer casos de test o incluso depurarla para corregir un error en la funcion te va a dar mucha guerra.

    Intenta utilizar el operador ternario cuando puedas '?' para mantener un codigo mas limpio visualmente (ya que realmente hace lo mismo), y por ejemplo, si hacemos uso de linq podemos dejar la funcion algo como esto:

     

     private static int buscarItemRibbon(System.Windows.Controls.ItemCollection ListaItems, BECtrlPermiso Permiso)
     {
     
     // obtener todos los items de la lista haciendo un casting a FrameworkElement
     System.Collections.Generic.IEnumerable<FrameworkElement> items = ListaItems.OfType<FrameworkElement>();
    
     // obtener el primer item (supuestamente unico) que tiene el nombre deseado en toda la coleccion
     FrameworkElement control = items.Where((item) => item.Name == "hola!").First();
     if (control != null)
     {
     control.IsEnabled = Permiso.Activo;
     control.Visibility = Permiso.Visible ? System.Windows.Visibility.Visible : System.Windows.Visibility.Hidden; 
     }
     
     // recorrer recursivamente las posibles colecciones
     System.Collections.Generic.IEnumerable<ItemsControl> itemsControls =
     items.Where((itemControl) => itemControl.GetType() == typeof(ItemsControl)).OfType<ItemsControl>();
    
     itemsControls.ToList().ForEach((item) => buscarItemRibbon(item.Items, Permiso));
     }
    

     

    No la he optimizado, solo me he dedicado a traducir tu codigo para hacer uso de linq, seguro que se puede optimizar mucho mas. Pero en el codigo anterior, se puede ver que es mas legible y visualmente mas atractivo, tan solo hay un if, de modo que depurar esto es cosa de niños y crear un caso de test tambien, con lo que dara un resultado de complejidad ciclomatica y mantenimiento mucho mejor.

    Si se sabe que van a existir muchos controles 1000 o mas tambien se puede hacer uso de un hilo paralelo (solo en framework 4) tan solo añadiendo antes del Where de linq, el metodo extensor AsParallel() de forma que se recorran todos los items en paralelo, pero esto es algo que hay que evaluar ya que muchos casos puedes perder rendimiento.

    En definitiva, hay que cuidar el codigo y optimizarlo, pero todo tiene su limite, ni mucho ni poco, hay que buscar un poco el punto optimo.

     

    2.- En cuanto a este punto, tambien tiene mucha miga, pero no me voy a enrrollar tanto. Tan solo comentar que contenedores hay un monton y abarcarlos todos es una tarea bastante absurda ya que siempre hay que intentar buscar algo abstracto para evitar duplicar codigo (una de las reglas a tener en cuenta), el codigo que has puesto se repite una barbaridad, casi cada 4 lineas son iguales, esto se deberia de evitar, pero haciendo alusion al tema de los contenedores, te voy a dejar un link de lectura obligatoria que escribio Josue Yeray en su blog, seguro que te es muy util para este punto:

    http://jyeray.blogspot.com/2010/09/wpf-arboles.html

     


    Saludos
    David González
    MCP.
    Visita mi Blog en: http://www.dgzornoza.com/
    jueves, 23 de septiembre de 2010 19:40
  • ¡Hola David!

    Ya que lo has citado, podrías definir ribbon, unboxing y el ternario. Gracias.

    Un abrazo,

     

     

    jueves, 23 de septiembre de 2010 22:37
  • Hola Corsario

    El Ribbon es un control de WPF que imita al ribbon de Office, se puede descargar aquí: http://www.microsoft.com/downloads/en/details.aspx?FamilyID=2bfc3187-74aa-4154-a670-76ef8bc2a0b4&displaylang=en

    El Boxing / Unboxing de variables se refiere a la acción de convertir tipos Value en tipos Reference... ok yeray, mi vida a dado un vuelco....¡QUE COÑE SON LOS TIPOS VALUE Y REFERENCE!

    Bueno, Los tipos value, son las variables cuyo tipo de datos almacena directamente el valor, esto es, los tipos básicos, como Integer, Double, Int32, etc...

    Los tipos reference, son las variables cuyo tipo de datos almacena una referencia a memoria donde se encuentra la información, esto es... cualquier objeto o tipo de datos complejo.

    En esto hay una excepción, el tipo String, es un tipo reference pero guarda el valor directamente, cada vez que asignamos una cadena a una variable string, el CLR destruye el string en memoria y crea una nueva referencia, por eso es muy recomendable usar StringBuilder cuando necesitemos asignar varias veces valores a un tipo String.

    Bien, ahora ya sabemos que es un tipo value y tipo reference, el Boxing ocurre cuando convertimos un tipo Value a un tipo Reference y el Unboxing ocurre al reves, cuando convertimos un tipo Reference a un tipo Value. Estas operaciones incurren en un alto coste de procesamiento y DEBEN ser evitadas a toda costa, siempre que sea posible.!

    En cuanto al operador Ternario es simplemente un IF simplificado, los que hemos programado en C++ nos hemos acostumbrado mucho a el...

    en vez de escribir

    if (variable1 == variable2)
    {
      return resultado1
    }
    else
    {
      return resultado2
    } 
    

    Podemos escribir esto:

    variable1 == variable2 ? return resultado1 : return resultado2;
    

    Hace lo mismo, pero de una forma mas clara, se lee igual: si variable1 es igual a variable2 devuelve resultado1 si no devuelve resultado2.

    Un gran saludo a todos :)


    MCTS .NET Framework 3.5 Windows Forms Application Development
    MCTS .NET Framework 3.5 Windows Presentation Foundation
    Visita mi Blog
    Sigueme en Twitter
    viernes, 24 de septiembre de 2010 8:50
    Moderador
  • ¡Hola Yeray!

    "ok yeray, mi vida a dado un vuelco....¡QUE COÑE SON LOS TIPOS VALUE Y REFERENCE!" . Jajajajajajajjajajaa. Como diría un amigo ruso que tengo: "Hasta ahí ya llegaba yo" ;-). Pero agradezco mucho las explicaciones. Tienes facilidad para explicar las cosas de una manera sencilla. Es de agradecer (ya sé, don natural ;-))

    Muchisimas gracias por la explicación. Un abrazo,

    viernes, 24 de septiembre de 2010 9:14
  • Thanx Yeray.

    Creo que no se puede explicar mejor.

    De echo ahora que lo ha comentado Corsario, he realizado un mal uso de la palabra ya que en realidad no se aplica unboxing, si no un Casting de tipos(lo he modificado), ya que como comentas el boxing/unboxing es para realizar conversiones entre valores y referencias.

     


    Saludos
    David González
    MCP.
    Visita mi Blog en: http://www.dgzornoza.com/
    viernes, 24 de septiembre de 2010 10:14
  • De nada David!

    aqui te dejo un articulo del Guille ne Dotnetmania donde habla precisamente de la sobrecarga de operadores, el ejemplo usa una estructura pero se puede aplicar a clases, solo hay que usar la palabra clave operator:

    http://www.elguille.info/net/revistas/dotnetmania/pdf/dotnetmania_9_pag_21_29.pdf

    Un gran saludo compañeros!


    MCTS .NET Framework 3.5 Windows Forms Application Development
    MCTS .NET Framework 3.5 Windows Presentation Foundation
    Visita mi Blog
    Sigueme en Twitter
    viernes, 24 de septiembre de 2010 10:25
    Moderador
  • Hola Josue.

    Creo que me hago mayor jejeje, donde he necesitado usarlos y no existian fue al realizar un proyecto en JAVA y no se por que lo he mezclado.

    En cualquier caso, gracias por el articulo, le tengo que dar un repaso ya que solo lo he utilizado de momento en c++.

    Fijate que hace poco mirando un codigo por ahi, vi un operador '??' y no tenia ni idea de para que servia, la primera vez que lo vi. Pues bien, para el que lo vea ahora por primera vez tambien:

    ?? es un operador que devuelve el operador izquierdo si no es nulo, en los demas casos retorna el derecho, dejo el link de microsoft para mas info:

    http://msdn.microsoft.com/es-es/library/ms173224(v=VS.80).aspx

     


    Saludos
    David González
    MCP.
    Visita mi Blog en: http://www.dgzornoza.com/
    viernes, 24 de septiembre de 2010 13:54
  • Buenisimo, no lo conocía!!!!
    MCTS .NET Framework 3.5 Windows Forms Application Development
    MCTS .NET Framework 3.5 Windows Presentation Foundation
    Visita mi Blog
    Sigueme en Twitter
    viernes, 24 de septiembre de 2010 14:30
    Moderador
  • Hola David.

    Muchas gracias por tus comentarios, me es muy util, y ya empece a modificar algunos detalles y opte por usar el LogicalTreeHelper para recorrer los controles, y de esta forma la funcion recursiva aplica para el control de usuario wpf y los otros controles comunes, por tanto disminuyeron algunas lineas repetitivas :)

    Tengo pendiente checar lo del  ribbon con lo que comentas usando linq.

    Gracias Josue por tu aportación me aclaro algunas dudas con respecto a los arboles :).

    saludos.

    Rosario.

     

    viernes, 24 de septiembre de 2010 18:25