none
Cambiar dinámicamente Background de un listbox... RRS feed

  • Pregunta

  • Buenos dias,

    he probado a cambiar dinámicamente el background de un listbox de mi aplicación según un valor consultado en la BD al entrar en la página pero no lo visualiza :

    - He probado a crear un converter y asociarlo al "listbox.background" de esta manera :

        public class StringToBrushConverter_valor : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                ImageBrush imageBrush;
    
                imageBrush = new ImageBrush
                         {
                             ImageSource = new BitmapImage(new Uri("images/imagen" + value.ToString() + ".png", UriKind.Relative))
                         };
                return imageBrush;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                throw new NotImplementedException();
            }
        }
    

    <ListBox.Background>
    <ImageBrush Stretch="Fill" ImageSource="{Binding VALOR, Converter={StaticResource conv_valor}}" Opacity="0.2"/>
    </ListBox.Background>
    


    - También he probado a crear una propiedad "CambiarBackground" y asignarsela directamente como binding del background.

            public static object CambiarBackground
            {
                get
                {
                    int valor = 0;
                    return new ImageBrush
                             {
                                 ImageSource = new BitmapImage(new Uri("images/imagen_" + valor+ ".png", UriKind.Relative))
                             };
                }
            }
    


    Ni el primero y el segundo se ejecutan al cargar el listbox. Tengo otros converters sobre el listbox pero los que funcionan están dentro del datatemplate del listboxitem, como esto es una propiedad general del control listbox no se si podré hacerlo así.

    saludos


    Cuanto más sabes, más ignorante te crees.
    lunes, 21 de noviembre de 2011 7:41

Respuestas

  • Si al DataContext le has asignado una lista de objetos, el Binding no funcionará
    porque la lista no tiene una propiedad llamada VALOR, por eso no pasa por el converter.

    Lo que si que tienes que ver ahora es un error en la ventana de Output (View->Output), similar
    a esto:
    System.Windows.Data Error: BindingExpression path error: 'VALOR' property not found

    Si es así, lo que puede probar es a quitar la propiedad VALOR del Binding.
    Es decir, define el binding así:

    <ListBox Background="{Binding Converter={StaticResource conv_valor}}">
    

    con esto entraría en el Converter y en el parámetro value recibirás la lista que has definido
    en el DataContext.

    Otra cosa es si para cambiar el background dinámicamente necesitas realmente esa lista
    u otro tipo de parámetro, que no se si es el caso.

    Ya nos comentarás.
    Saludos

     

     

     


    @ayus
    • Marcado como respuesta Pedro Net viernes, 25 de noviembre de 2011 10:10
    martes, 22 de noviembre de 2011 12:13
  • Hola Pedro,

    La propiedad DataContext sirve para especificar el origen de los datos que se usará en los Bindings en el árbol de elementos.

    Aquí tienes una buena referencia para entender el concepto de DataContext y Bindings (es un artículo sobre WPF pero aplicable a WP7)

    http://msdn.microsoft.com/es-es/library/ms752347.aspx

    Imagina que tienes un clase Usuario con las propiedades Nombre y Apellidos.

    Y tienes un control Textblock al que le asignas como DataContext un objeto Usuario.

    textblock.DataContext = new Usuario { Nombre = "Usuario 1", Apellidos = "Apellidos 1" };

    En los bindings que estableces para el textblock, podrás usar las propiedades nombre y apellidos (que es su origen de datos). Por ejemplo:

    <TextBlock Text="{Binding Nombre}"/>

    mostrará el texto Usuario1.

    Pero imagina, que yo especifico el Binding con una propiedad VALOR de la siguiente forma:

    <TextBlock Text="{Binding VALOR}"/>

    Esto no me va a dar ningún error, la aplicación me compilará, se ejecutará pero no veré nada. Si tengo un converter, este tampoco se ejecutará. Sin embargo, en la ventana Output, una vez ejecutada la aplicación, veré un error de Binding indicando que la propiedad VALOR no pertenece al tipo Usuario (esta clase solo definía las propiedades Nombre y Apellidos)

    En el caso de tu listbox, ¿cómo estableces ese DataContext?, ¿podrías poner el código donde estableces ese DataContext y el XAML completo del ListBox?

    Saludos

    PD: Espero no haberte líado más...


    @ayus
    • Marcado como respuesta Pedro Net viernes, 25 de noviembre de 2011 10:10
    martes, 22 de noviembre de 2011 17:27
  • Hola Compañeros,

     

    al final lo he solucionado y lo quiero compartir para ayudar a otras personas si tienen el mismo problema. Resulta que he tenido que ponerle la ruta completa (apuntando al proyecto y la clase) de la imagen para que se visualizara.

    ImageSource = new BitmapImage(new Uri("/PROYECTO.WindowsPhone;component/images/imagen_" + ((Servicio)value).contenedor + ".png", UriKind.Relative)),
    


    Por otro lado, lo que he hecho ya que no me cogia como binding directamente el campo es pasarle como DataContext del listbox el objecto completo del item que está seleccionado del mismo. De esta manera, puedo acceder a la propiedad que quiera en cualquier momento.

    Gracias Ayus.

     


    Cuanto más sabes, más ignorante te crees.
    • Marcado como respuesta Pedro Net viernes, 25 de noviembre de 2011 10:15
    viernes, 25 de noviembre de 2011 10:15

Todas las respuestas

  • Habia pensando en utilizar INotifyProperty , aqui un ejemplo aunque en este caso modificar un elemento del listboxitem :

     

    Código perteneciente a una respuesta copiada de su autor Juan Manuel Servera .

    public class Channel:INotifyPropertyChanged
    {
     //declaración de evento necesaria para el interfaz
     public event PropertyChangedEventHandler PropertyChanged;
    
     bool _favorito;
     //modificamos la propiedad por una explícita
     public bool Favorito
     {
      get { return _favorito; }
      set
      {
       if (_favorito != value)
       {
        _favorito = value;
        //si ha cambiado y el evento se utiliza llamamos al evento
        if (PropertyChanged != null)
        {
         //notificamos que esta propiedad ha cambiado
         PropertyChanged(this, 
          new PropertyChangedEventArgs("Favorito"));
         //y también la propiedad relacionada, para que
         //el UI se refresque
         PropertyChanged(this, 
          new PropertyChangedEventArgs("URLFavorito"));
        }
       }
      }
     }
    // etc...
    
     
    
    Y luego en el evento click bastará que cambies la propiedad para que se refresque la imagen en el UI:
    
     
    
    private void btn1_Click(object sender, RoutedEventArgs e)
    {
     Channel channel = ((Button)sender).DataContext as Channel;
     if (channel != null)
      channel.Favorito = !channel.Favorito;
    }
    
    
    
    

     


    ¿ Como podria implementar este iNotify sobre la propiedad "Background" del listbox ?

    ¿ Cual de las tres posibilidades seria la correcta o la más adecuada ?


    Cuanto más sabes, más ignorante te crees.
    • Editado Pedro Net lunes, 21 de noviembre de 2011 8:36
    lunes, 21 de noviembre de 2011 8:35
  • Hola Pedro,

    en tu primera pregunta, en el primer caso, tu converter ya está devolviendo un ImageBrush, por tanto no deberías asignar en el backgrond un nuevo ImageBrush, sino hacer directamente el Binding a la propiedad Backgroud.

    <ListBox Background="{Binding VALOR, Converter={StaticResource conv_valor}}">
    

    Por otro lado, ¿cual es el DataContext de tú página? En concreto la propiedad VALOR a quién pertence?

     

    ¿Ves algún error de Binding en la ventana Output cuando depuras?

    Si hay algún error de Binding lo puedes ver en esa ventana y te dará pistas.

    Saludos


    @ayus
    lunes, 21 de noviembre de 2011 23:14
  • Gracias Ayus, la prueba que comentas la habia probado pero sigue sin entrar en el convert. No me dá ningún error en el depurador ni al compilar ni al ejecutar la acción. He estado leyendo bastante y me parece que el problema es que no puedes hacer un "binding converter" fuera de un DataTemplate, de ahí que no pase. De todas maneras, solo es una suposición formada por la información que estoy recopilando.

    A ver si podemos aclararlo entre todos.

     

    saludos


    Cuanto más sabes, más ignorante te crees.
    martes, 22 de noviembre de 2011 7:32
  • Hola Pedro,

    En principio el Binding lo puedes hacer sobre cualquier dependecy property  de cualquier objeto, no es necesario que esté dentro de un DataTemplate.

    Si no está pasando por el converter, es posible que la propiedad DataContext esté a nula.

    ¿En algún momento estás asignando el DataContext de la página al objeto que tiene la propiedad VALOR definida?

    Saludos


    @ayus
    martes, 22 de noviembre de 2011 9:12
  • Normalmente en mis listbox suelo rellenar el itemsource. El DataContext lo tenia a null y le asignado lo mismo que a itemsource, es decir, una lista de objectos de una determinada clase. ¿Seria así válido? Sigue sin pasar por el converter.

    Os pongo en antecedentes : Tengo un listbox donde sus elementos son instancias de una clase definida llamemosla "operario". Para cargar este listbox, relleno primero una observablelist de tipo "operario" y luego le paso esta lista al itemsource del listbox.

     

    saludos y gracias por las respuestas.


    Cuanto más sabes, más ignorante te crees.

    • Editado Pedro Net martes, 22 de noviembre de 2011 10:01
    martes, 22 de noviembre de 2011 9:57
  • Si al DataContext le has asignado una lista de objetos, el Binding no funcionará
    porque la lista no tiene una propiedad llamada VALOR, por eso no pasa por el converter.

    Lo que si que tienes que ver ahora es un error en la ventana de Output (View->Output), similar
    a esto:
    System.Windows.Data Error: BindingExpression path error: 'VALOR' property not found

    Si es así, lo que puede probar es a quitar la propiedad VALOR del Binding.
    Es decir, define el binding así:

    <ListBox Background="{Binding Converter={StaticResource conv_valor}}">
    

    con esto entraría en el Converter y en el parámetro value recibirás la lista que has definido
    en el DataContext.

    Otra cosa es si para cambiar el background dinámicamente necesitas realmente esa lista
    u otro tipo de parámetro, que no se si es el caso.

    Ya nos comentarás.
    Saludos

     

     

     


    @ayus
    • Marcado como respuesta Pedro Net viernes, 25 de noviembre de 2011 10:10
    martes, 22 de noviembre de 2011 12:13
  • Hola Ayus,

    no he visto ningún error en output y la propiedad si lo detecta seguro porque tengo otros 2 converters apuntando contra otras propiedades de la clase pero están dentro del DataTemplate. He probado dejando "Binding Converter...." y pasa lo mismo.También he probado a poner uno de los converter fuera del DataTemplate y declararlo en la misma linea del listbox y tampoco se ejecuta.

    Lo que no entiendo bien es que debes poner en un DataContext de un listbox, lo normal es que tengas una lista de elementos de una determinada clase personalizada.

     

    saludos


    Cuanto más sabes, más ignorante te crees.
    martes, 22 de noviembre de 2011 14:50
  • Hola Pedro,

    La propiedad DataContext sirve para especificar el origen de los datos que se usará en los Bindings en el árbol de elementos.

    Aquí tienes una buena referencia para entender el concepto de DataContext y Bindings (es un artículo sobre WPF pero aplicable a WP7)

    http://msdn.microsoft.com/es-es/library/ms752347.aspx

    Imagina que tienes un clase Usuario con las propiedades Nombre y Apellidos.

    Y tienes un control Textblock al que le asignas como DataContext un objeto Usuario.

    textblock.DataContext = new Usuario { Nombre = "Usuario 1", Apellidos = "Apellidos 1" };

    En los bindings que estableces para el textblock, podrás usar las propiedades nombre y apellidos (que es su origen de datos). Por ejemplo:

    <TextBlock Text="{Binding Nombre}"/>

    mostrará el texto Usuario1.

    Pero imagina, que yo especifico el Binding con una propiedad VALOR de la siguiente forma:

    <TextBlock Text="{Binding VALOR}"/>

    Esto no me va a dar ningún error, la aplicación me compilará, se ejecutará pero no veré nada. Si tengo un converter, este tampoco se ejecutará. Sin embargo, en la ventana Output, una vez ejecutada la aplicación, veré un error de Binding indicando que la propiedad VALOR no pertenece al tipo Usuario (esta clase solo definía las propiedades Nombre y Apellidos)

    En el caso de tu listbox, ¿cómo estableces ese DataContext?, ¿podrías poner el código donde estableces ese DataContext y el XAML completo del ListBox?

    Saludos

    PD: Espero no haberte líado más...


    @ayus
    • Marcado como respuesta Pedro Net viernes, 25 de noviembre de 2011 10:10
    martes, 22 de noviembre de 2011 17:27
  • Gracias por la aclaración Ayus. El DataContext lo establezco desde código (actualmente tengo la misma lista de objectos "servicios") después de cargar un ObservableList sacado de una consulta de base de datos.

    Esta seria la definición del listbox :

     

            <ListBox x:Name="lbServicios" Foreground="Black" Background="{Binding contenedor, Converter={StaticResource CONVERTER}}" BorderBrush="#00000000" BorderThickness="0" Margin="12,270,12,0" Height="399" VerticalAlignment="Top" Style="{StaticResource ListBoxStyle1}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <Button toolkit:TiltEffect.IsTiltEnabled="True" x:Name="btListaEleccionServicio" BorderThickness="0"  Width="462" Tap="btListaEleccionServicio_Tap">
                            <!-- MENU CONTEXTUAL SOBRE CADA ITEM DEL LISTBOX -->
                            <toolkit:ContextMenuService.ContextMenu>
                                <toolkit:ContextMenu x:Name="MenuContextual">
                                    <toolkit:MenuItem Click="Contextual1_Click">
                                        <toolkit:MenuItem.Header>
                                            <StackPanel Orientation="Horizontal">
                                                <Image Width="48" Source="icons/warning.png"/>
                                                <TextBlock Text="Menu 1"/>
                                            </StackPanel>
                                        </toolkit:MenuItem.Header>
                                    </toolkit:MenuItem>
                                    <toolkit:MenuItem Click="Contextual2_Click">
                                        <toolkit:MenuItem.Header>
                                            <StackPanel Orientation="Horizontal">
                                                <Image Width="48" Source="icons/warning.png"/>
                                                <TextBlock Text="Menu 2"/>
                                            </StackPanel>
                                        </toolkit:MenuItem.Header>
                                    </toolkit:MenuItem>
                                </toolkit:ContextMenu>
                            </toolkit:ContextMenuService.ContextMenu>
    
                            <Grid Height="79" Width="419">
                                <TextBlock x:Name="tbSucursal" Text="{Binding Sucursal}" Width="327" Height="36" Foreground="#FF0F1921" FontWeight="Bold" FontSize="32" HorizontalAlignment="Left" VerticalAlignment="Top" FontStretch="Expanded" TextWrapping="Wrap" Margin="1,0,0,0" d:LayoutOverrides="VerticalAlignment"/>
                                <TextBlock x:Name="tbArticulo" Text="{Binding Articulo}"  Width="309" Height="33" Foreground="{Binding Servicio, Converter={StaticResource conv1}}" FontWeight="Bold" FontSize="24" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="1,40,0,0"/>
                                <TextBlock x:Name="tbServicio" Text="{Binding Servicio}" Width="100" Foreground="{Binding Servicio, Converter={StaticResource conv1}}" FontSize="24" RenderTransformOrigin="0.5,0.5" HorizontalAlignment="Right" Margin="0,0,0,6" FontWeight="Bold" Height="33" VerticalAlignment="Bottom" TextAlignment="Center"/>
                                <Ellipse x:Name="eEstado" Height="23" Stroke="Black" StrokeThickness="3" Width="26" HorizontalAlignment="Right" Margin="0,5,2,0" VerticalAlignment="Top">   
                                    <Ellipse.Fill>
                                        <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                            <GradientStop Color="#FFD6CECE" Offset="0"/>
                                            <GradientStop Color="{Binding estado, Converter={StaticResource conv_estado}}" Offset="1"/>
                                        </LinearGradientBrush>
                                    </Ellipse.Fill>
                                </Ellipse>
                                <Image HorizontalAlignment="Right" Height="25" Margin="0,5,28,0" Source="icons/warning.png" Stretch="Fill" VerticalAlignment="Top" Width="31" Visibility="{Binding incidencia, Converter={StaticResource conv_visibility}}"/>
                                <Rectangle HorizontalAlignment="Right" Margin="0,36,1,2" Width="101" Stroke="{Binding Servicio, Converter={StaticResource conv1}}" StrokeThickness="2"/>
                                <Image HorizontalAlignment="Right" Margin="0,4,58,0" Source="icons/retener_rojo.png" Stretch="Fill" Width="31" Height="25" VerticalAlignment="Top" Visibility="{Binding retenido, Converter={StaticResource conv_visibility}}"/>
                            </Grid>
                        </Button>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
    


     


    Cuanto más sabes, más ignorante te crees.

    • Editado Pedro Net miércoles, 23 de noviembre de 2011 7:20
    miércoles, 23 de noviembre de 2011 7:14
  • Ok Pedro,

    Un par de preguntas,

    ¿Podrías poner las dos líneas de código donde estableces el lbServicios.DataContext y lbServicios.ItemsSource?

    ¿La propiedad "contenedor" que aplicas en el Binding del Background, a quien pertenece?

    Si pruebas a poner el binding así y pones un punto de ruptura en el método Convert, ¿no pasa por el converter que defines en StaticResource CONVERTER?:

    <ListBox x:Name="lbServicios" Foreground="Black" Background="{Binding Converter={StaticResource CONVERTER}}" BorderBrush="#00000000" BorderThickness="0" Margin="12,270,12,0" Height="399" VerticalAlignment="Top" Style="{StaticResource ListBoxStyle1}">

    Un saludo 


    @ayus
    miércoles, 23 de noviembre de 2011 8:06
  • Noticias frescas! jeje. Dejando como tu me dijistes sin el campo "contenedor" en Binding pasa por el Converter. Lo curioso es que asigna correctamente el imagesource al background porque lo veo desde debugger pero no lo visualiza sobre el listbox.

     

    El converter es :

     

        public class CONVERTER : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                ImageSource image;
    
                image = new BitmapImage(new Uri("images/fija" + ".png", UriKind.Relative));
    
                return image;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                throw new NotImplementedException();
            }
        }
    

     


    Desde código asigno al listbox su itemsource y datacontext :

     

          foreach (DataRow fila in dt.Rows)
          {
            num_articulo = dato extraido de BD...
            estado = dato extraido de BD...
            incidencia = dato extraido de BD...
            retenido =dato extraido de BD... 
            contenedor =dato extraido de BD... 
    
            listaserv.Add(new Servicio( num_articulo, estado, contenedor, incidencia, retenido));
    
          }
          lbServicios.DataContext = listaserv;
          lbServicios.ItemsSource = listaserv;
    

    + info: Si no pongo el DataContext (como tu decias) no pasa por el converter. Si pongo el campo "contenedor" en el Binding me dá error de que no lo encuentra en la "listaserv".

     

    saludos

     


    Cuanto más sabes, más ignorante te crees.
    • Editado Pedro Net miércoles, 23 de noviembre de 2011 8:33
    miércoles, 23 de noviembre de 2011 8:31
  • Me alegro que ya pase por el converter! :)

    En el parámetro value del método Convert recibirás la colección listserv, que en tu caso no se si será suficiente para cargar dinámicamente el background.

    Efectivamente, si quitas la asignación del DataContext, el Binding no se produce puesto que no hay origen de datos donde enlazar.

    Por otro lado, si no visualizas la imagen en el Background del Listbox pese a estar asignada puede ser porque al definir los DataTemplates de los elementos de la lista, estos se superpongan sobre la imagen. Para ver si se muestra correctamente la imagen podrías comentar la asignación lbServicios.ItemsSource = listaserv; para que el listbox no tenga elementos y comprobar si se muestra la imagen de fondo.

    Espero que te haya servido de ayuda.

    Saludos

     


    @ayus
    miércoles, 23 de noviembre de 2011 12:17
  • Hola Compañeros,

     

    al final lo he solucionado y lo quiero compartir para ayudar a otras personas si tienen el mismo problema. Resulta que he tenido que ponerle la ruta completa (apuntando al proyecto y la clase) de la imagen para que se visualizara.

    ImageSource = new BitmapImage(new Uri("/PROYECTO.WindowsPhone;component/images/imagen_" + ((Servicio)value).contenedor + ".png", UriKind.Relative)),
    


    Por otro lado, lo que he hecho ya que no me cogia como binding directamente el campo es pasarle como DataContext del listbox el objecto completo del item que está seleccionado del mismo. De esta manera, puedo acceder a la propiedad que quiera en cualquier momento.

    Gracias Ayus.

     


    Cuanto más sabes, más ignorante te crees.
    • Marcado como respuesta Pedro Net viernes, 25 de noviembre de 2011 10:15
    viernes, 25 de noviembre de 2011 10:15