none
Pintar ObservableCollection de Lineas dinámicamente RRS feed

  • Pregunta

  • Saludos a todos.

     

    Quisiera saber si hay alguna forma de pintar una ObservableCollection de Lineas dinamicamente, es decir, necesito hacer un Binding a algun control que me permita hacer uso del OnPropertyChange cada vez que a esta colección se le adicionan objetos, y que al hacerlo se pinten las nuevas lineas dinámicamente.

    Actualmente lo hago recorriendo la colección con un foreach y adicionando elementos de la siguiente manera

        layout.Children.Add(linea)

     

    Pero me parece que es una solución algo ineficiente y consume muchisima memoria. Lo intenté con un content control y en su propiedad Content le hice el Binding a la colección observable, pero no funcionó correctamente.

     

    Alguna idea?

     

     

     


    David Cardona
    Silverlight MCTS, .Net Developer
    Medellin, Colombia

    miércoles, 22 de junio de 2011 4:31

Respuestas

  • Hola Dacar.

    Creia que las lineas era otro tipo de objeto por que los insertabas en un grid.

    Bien, el proceso es el mismo, no varia mucho, solo que hay que cambiar el modelo por las lineas. Lo que pasa es que lo que quieres hacer no es posible sobre un grid directamente, ya que la propiedad Childrens no es una propiedad de dependencia y no se puede realizar ningun binding sobre ella, la propiedad hereda de Panel y la tienen todos los contenedores Canvas, StackPanel, etc, de modo que no se puede realizar directamente sobre un contenedor.

    Asi pues, si lo quieres enlazar tienes que usar alguna clase que herede de frameworkElement que es el que implementa completamente el mecanismo de binding, de modo que al ser una coleccion, auque puedes usar un listbox, existe una clase mas abstracta que es 'ItemsControls' que hereda de control y puedes usar para enlazar multiples controles a su propiedad ItemsSource.

    De modo que el ejemplo anterior quedaria aun mas sencillo:

      <Window.Resources>
    
        <!-- Recurso con la lista de lineas para el contexto de datos -->
      	<local:LinesModel x:Key="LinesModelDataSource" d:IsDataSource="True"/>
    
      </Window.Resources>
    
      <Grid Name="mainGrid" DataContext="{Binding Source={StaticResource LinesModelDataSource}}" >
    
        <ItemsControl Name="LinesContainer" ItemsSource="{Binding Lines}"/>
        
        <!-- boton para añadir mas elementos -->
        <Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="12,276,0,0" Name="button1" 
        VerticalAlignment="Top" Width="75" Click="button1_Click" />
            
      </Grid>
    

    Un control ItemsControl sera el que represente las lineas y un boton para añadir mas a la coleccion del contexto de datos

        /// <summary>
        /// Lógica de interacción para MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
          /// <summary>
          /// Constructor por defecto de la clase
          /// </summary>
          public MainWindow()
          {
            InitializeComponent();
          }
    
          /// <summary>
          /// Evento ocurrido al hacer click en boton, para añadir lineas
          /// </summary>
          private void button1_Click(object sender, RoutedEventArgs e)
          {
            // añadir mas lineas a la coleccion
            LinesModel model = (Resources["LinesModelDataSource"] as LinesModel);
    
            for (int i = 0; i < 10; i++)
            {
              Line line = new Line();
              line.Stroke = System.Windows.Media.Brushes.LightSteelBlue;
              line.X1 = 1;
              line.X2 = 200 - (5*i);
              line.Y1 = line.Y2 = 1 + (i + 5);
              line.StrokeThickness = i;
              model.Lines.Add(line);
            }
          }
        }
    
        public class LinesModel
        {
          public LinesModel()
          {
            // inicializar coleccion de lineas
            Lines = new System.Collections.ObjectModel.ObservableCollection<System.Windows.Shapes.Line>();
          }
    
          // coleccion de lineas
          public System.Collections.ObjectModel.ObservableCollection<System.Windows.Shapes.Line> Lines { get; set; }
        }
    

    En este codigo solo esta el modelo con una coleccion de lineas y un bucle en el boton para insertar lineas, que realmente se hara desde la BBDD o cualquier otra fuente de datos.

     


    Saludos
    David González
    MCP, MCTS
    Visita mi Blog en: http://www.dgzornoza.com/
    • Marcado como respuesta Dacar7 viernes, 24 de junio de 2011 2:19
    jueves, 23 de junio de 2011 7:39

Todas las respuestas

  • Saludos David,

    Si lo que deseas es que la presentación de LOS NUEVOS ELEMENTOS de la lista sea diferente creo que depende más de que el elemento es si tenga alguna propiedad que sirva para identificar dicho estado (¿se la puedes agregar?). Si estás usando WPF y DataTables, los DataRows tienen esta propiedad, si utilizas RIA services (no he hecho el intento), las entidades RIA seguro tienen este tipo de propiedad.


    Nestor Arturo Fernandez Ricaurte
    miércoles, 22 de junio de 2011 12:28
  • Hola Nestor, quizás no me di a enteder muy bien en mi pregunta, a continuación la clarifico:

     

    Tengo una aplicación Silverlight, en una vista lo que hago es pintar una colección de Lineas que traigo de la Base de Datos y en mi Model la tengo en un ObservableCollection<Lines>.  Actualmente para pintar esas lineas recorro esa colección y cada elemento lo agrego como un hijo de un Grid de la siguiente manera: grid.Children.Add(lineax),  hasta ahi ok?

     

    Lo que quisiera es poder tener un control en el xaml que me permita hacerlo más dinamico, algo en el cual yo pueda especificarle mediante un Binding esa Colección que tengo y que automaticamente pinte las lineas que tiene esa coleccion.


    De esa manera yo mediante el OnPropertyChange podría refrescar la vista automaticamente cada vez que esa colección cambie.

     

    Muchas gracias.


    David Cardona
    Silverlight MCTS, .Net Developer
    Medellin, Colombia
    miércoles, 22 de junio de 2011 12:54
  • Hola Dacar.

    Si estas haciendo uso de un ObservableCollection, no hace falta que implementes ningun tipo de notificacion, tan solo haciendo un binding de esa propiedad a cualquier control, este se tiene que actualizar automaticamente cada vez que añades o eliminas un item de la lista.

    ¿que tipo de control es 'Lines' que estas agregando a un grid?, el grid es un contenedor, es posible que lo que quieras hacer sea usar un ListBox.

    Te he puesto un ejemplo lo mas sencillo que he podido para que veas que no hace falta nada mas que un binding:

     

      <Window.Resources>    
        
        <!-- Recurso con la lista de modelos para el contexto de datos -->
        <local:models x:Key="models"></local:models>
    
        
      </Window.Resources>
    
      <Grid Name="mainGrid" >
        <!-- ListBox para presentar los datos -->
        <ListBox Name="listbox1" ItemsSource="{Binding Source={StaticResource models}, Path=Lines}" DisplayMemberPath="name" 
             Margin="0,0,0,41" SelectedValuePath="age"></ListBox>
        <!-- boton para añadir mas elementos -->
        <Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="12,276,0,0" Name="button1" 
            VerticalAlignment="Top" Width="75" Click="button1_Click" />
      </Grid>
    

    El codigo xaml solo tiene un listBox y un boton para añadir mas elementos a los datos del contexto

    En el codigo .cs solo esta el modelo y su populacion:

      /// <summary>
      /// Lógica de interacción para MainWindow.xaml
      /// </summary>
      public partial class MainWindow : Window
      {
        public MainWindow()
        {
          InitializeComponent();      
          // evento de carga del control
          this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
        }
    
        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
          // obtener el contexto de datos y añadirle lineas
          models data = Resources["models"] as models;
    
          data.Lines.Add(new model() { name = "pepito", age = 16 });
          data.Lines.Add(new model() { name = "ivan", age = 21 });
          data.Lines.Add(new model() { name = "raul", age = 43 });
          data.Lines.Add(new model() { name = "Juan", age = 22 });
        }
    
        private void button1_Click(object sender, RoutedEventArgs e)
        {
          // obtener el contexto de datos y añadirle mas lineas
          models data = Resources["models"] as models;
    
          data.Lines.Add(new model() { name = "pepito2", age = 16 });
          data.Lines.Add(new model() { name = "ivan2", age = 21 });
          data.Lines.Add(new model() { name = "raul2", age = 43 });
          data.Lines.Add(new model() { name = "Juan2", age = 22 });
        }
      }
    
      /// <summary>
      /// Clase con una lista de modelos
      /// </summary>
      public class models
      {
        public models()
        {
          Lines = new System.Collections.ObjectModel.ObservableCollection<model>();
        }
    
        public System.Collections.ObjectModel.ObservableCollection<model> Lines { get; set; }
      }
    
      /// <summary>
      /// Clase con un modelo
      /// </summary>
      public class model
      {
        public string name { get; set; }
        public int age { get; set; }
      }
    

    En el codigo se puede ver que no hay ningun tipo de notificacion, simplemente cuando se añaden mas datos al modelo del contexto de datos enlazado, se actualiza el listbox.

    Los datos estan puestos a mano, pero se puede hacer con cualquier tipo de fuente de datos

     


    Saludos
    David González
    MCP, MCTS
    Visita mi Blog en: http://www.dgzornoza.com/
    miércoles, 22 de junio de 2011 15:58
  • Hola David

    Podrias usar un Listbox para mostrar la informacion y con un DataTemplate para modificar la forma de mostrar los datos, algo como lo que muestran aqui Usando ListBox y DataBinding para desplegar un List Data con VS 2008 y C#.

     

    Saludos


    Antonio Lobo
    Mi Blog
    miércoles, 22 de junio de 2011 15:59
  • Hola David y Antonio.

     

    Quizás no me hice entender. Los objetos son Line (System.Windows.Shape) ...es decir lo que yo tengo es una colección de lineas, que tienen sus respectivos colores, posiciones en X y Y , broke, etc.

     

    Por lo tanto un ListBox no es la solución , ya que este pinta Datos de objetos serializables el cual no es mi caso.

     

    Alguna idea?


    David Cardona
    Silverlight MCTS, .Net Developer
    Medellin, Colombia
    miércoles, 22 de junio de 2011 22:13
  • Hola David

    Ahora si entendí lo que quieres decir... te dejo este enlace donde se muestra una aproximacion de lo que tu deseas hacer Drawing and removing shapes in WPF.

     

    PD: David usted donde hizo el examen de certificacion de Microsoft? yo vivo en Barranquilla y ahora mismo no se en que parte puedo averiguar esto, podrias decirme por favor.

     

    Saludos


    Antonio Lobo
    Mi Blog
    jueves, 23 de junio de 2011 0:47
  • Hola Antonio, gracias por la respuesta ya la estoy revisando.

     

    El examen lo presenté en Medellin, si entras a Prometric ahi te listan los centros autorizados para tu ciudad. El examen no es tan dificil y estudié como unas dos semanas para ganarlo.

     

    Saludos


    David Cardona
    Silverlight MCTS, .Net Developer
    Medellin, Colombia
    jueves, 23 de junio de 2011 1:34
  • Hola Dacar.

    Creia que las lineas era otro tipo de objeto por que los insertabas en un grid.

    Bien, el proceso es el mismo, no varia mucho, solo que hay que cambiar el modelo por las lineas. Lo que pasa es que lo que quieres hacer no es posible sobre un grid directamente, ya que la propiedad Childrens no es una propiedad de dependencia y no se puede realizar ningun binding sobre ella, la propiedad hereda de Panel y la tienen todos los contenedores Canvas, StackPanel, etc, de modo que no se puede realizar directamente sobre un contenedor.

    Asi pues, si lo quieres enlazar tienes que usar alguna clase que herede de frameworkElement que es el que implementa completamente el mecanismo de binding, de modo que al ser una coleccion, auque puedes usar un listbox, existe una clase mas abstracta que es 'ItemsControls' que hereda de control y puedes usar para enlazar multiples controles a su propiedad ItemsSource.

    De modo que el ejemplo anterior quedaria aun mas sencillo:

      <Window.Resources>
    
        <!-- Recurso con la lista de lineas para el contexto de datos -->
      	<local:LinesModel x:Key="LinesModelDataSource" d:IsDataSource="True"/>
    
      </Window.Resources>
    
      <Grid Name="mainGrid" DataContext="{Binding Source={StaticResource LinesModelDataSource}}" >
    
        <ItemsControl Name="LinesContainer" ItemsSource="{Binding Lines}"/>
        
        <!-- boton para añadir mas elementos -->
        <Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="12,276,0,0" Name="button1" 
        VerticalAlignment="Top" Width="75" Click="button1_Click" />
            
      </Grid>
    

    Un control ItemsControl sera el que represente las lineas y un boton para añadir mas a la coleccion del contexto de datos

        /// <summary>
        /// Lógica de interacción para MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
          /// <summary>
          /// Constructor por defecto de la clase
          /// </summary>
          public MainWindow()
          {
            InitializeComponent();
          }
    
          /// <summary>
          /// Evento ocurrido al hacer click en boton, para añadir lineas
          /// </summary>
          private void button1_Click(object sender, RoutedEventArgs e)
          {
            // añadir mas lineas a la coleccion
            LinesModel model = (Resources["LinesModelDataSource"] as LinesModel);
    
            for (int i = 0; i < 10; i++)
            {
              Line line = new Line();
              line.Stroke = System.Windows.Media.Brushes.LightSteelBlue;
              line.X1 = 1;
              line.X2 = 200 - (5*i);
              line.Y1 = line.Y2 = 1 + (i + 5);
              line.StrokeThickness = i;
              model.Lines.Add(line);
            }
          }
        }
    
        public class LinesModel
        {
          public LinesModel()
          {
            // inicializar coleccion de lineas
            Lines = new System.Collections.ObjectModel.ObservableCollection<System.Windows.Shapes.Line>();
          }
    
          // coleccion de lineas
          public System.Collections.ObjectModel.ObservableCollection<System.Windows.Shapes.Line> Lines { get; set; }
        }
    

    En este codigo solo esta el modelo con una coleccion de lineas y un bucle en el boton para insertar lineas, que realmente se hara desde la BBDD o cualquier otra fuente de datos.

     


    Saludos
    David González
    MCP, MCTS
    Visita mi Blog en: http://www.dgzornoza.com/
    • Marcado como respuesta Dacar7 viernes, 24 de junio de 2011 2:19
    jueves, 23 de junio de 2011 7:39
  • Muchas gracias David , esta excelente el ejemplo.

     

    Saludos


    David Cardona
    Silverlight MCTS, .Net Developer
    Medellin, Colombia
    viernes, 24 de junio de 2011 2:19