none
Crear lineas de marca horizontales en un chart RRS feed

  • Pregunta

  • ¡Hola!

    Lo que pretendo hacer es lo mismo que se hace en este blog pero en vez de se líneas verticales que sean horizontales y que no esten ligadas a los puntos sino al eje de las Ys: http://leeontech.wordpress.com/2009/02/25/linechart-with-markers/

    [Sé que existe otra posibilidad que es la que indica el toolkit, y es añadir esa línea como una función en las series del chart, pero lo he descartado para mi caso porque luego deseo poderla mover con el mouse.]

    El problema que me encuentro es que me dibuja la línea en la parte alta del Chart que la toma como (0,0) cuando debería de empezar de abajo para arriba.

    Un poquito de código creo que irá bien para ver lo que estoy haciendo (Se trata de la función que se encarga de dibujar la línea horizontal):

    Private Sub CrearMarket(ByVal limite As Double, ByVal color As System.Windows.Media.Color)
        Try
    
          Dim Canv = CType(GetChildObject(Of Canvas)(Chart1, "c1"), Canvas)
    
          Dim ln As New Line
    
          With ln
            .Stroke = New SolidColorBrush(color)
            .X1 = 0
            .X2 = 34
            .Y1 = limite
            .Y2 = limite
           End With
    
          Dim TextoMarkt As New TextBlock
    
          With TextoMarkt
            .Text = limite
            .Foreground = New SolidColorBrush(color)
            .SetValue(Canvas.LeftProperty, ln.X1 + 5.0)
            .SetValue(Canvas.TopProperty, ln.Y1 - 20)
           End With
    
          Canv.Children.Add(TextoMarkt)
          Canv.Children.Add(ln)
          
    
        Catch ex As Exception
          Er(ex)
        End Try
      End Sub
    Gracias por vuestras aportaciones. Un saludo.
    jueves, 8 de julio de 2010 22:05

Respuestas

  • Hola Corsario.

    Ok, vale que estamos en silverlight, el codigo anterior era para WPF.

    En lugar del rendered, se puede usar el evento Loaded del column series, por ejemplo con algo como esto:

      <Grid x:Name="LayoutRoot" Background="White">
        <toolkit:Chart HorizontalAlignment="Left" Margin="12,12,0,0" Name="Chart1" Title="Chart Title" VerticalAlignment="Top" Height="276" Width="376">
          <toolkit:ColumnSeries DependentValuePath="X" IndependentValuePath="Y" Loaded="ColumnSeries_Loaded">
            <toolkit:ColumnSeries.ItemsSource>
              <PointCollection>
                <Point>1,10</Point>
                <Point>2,20</Point>
                <Point>3,30</Point>
                <Point>4,40</Point>
              </PointCollection>
            </toolkit:ColumnSeries.ItemsSource>
          </toolkit:ColumnSeries>
        </toolkit:Chart>
      </Grid>
    

    aqui se crea por codigo xaml, pero la idea es que cuando se carguen las series se implemente el evento que llame a la creacion de la marca

     

    codigo .cs adaptado a silverlight:

     private Line m_line;
        private Canvas m_canvas;
        private bool m_flagLeftButton;
    
        public MainPage()
        {
          InitializeComponent();      
        }
    
        /// <summary>
        /// Funcion para crear una marca en el chart
        /// </summary>
        /// <param name="limite">coordenada eje Y para mostrar la linea</param>
        /// <param name="color">color de la linea</param>
        private void CrearMarket(double limite, System.Windows.Media.Color color)
        {
          try
          {
            
            // obtener dimensiones del canvas
            m_canvas = (Canvas)Helper.GetChildObject<Canvas>(Chart1, "PlotArea");
            double canvasHeight = m_canvas.ActualHeight;
    
            m_line = new Line();
            {
              m_line.Stroke = new SolidColorBrush(color);
              m_line.X1 = 0;
              m_line.X2 = 100;
              m_line.Y1 = canvasHeight - limite; // restar del alto del canvas para invertir coordenadas
              m_line.Y2 = canvasHeight - limite;
            }
    
            TextBlock TextoMarkt = new TextBlock();
            {
              TextoMarkt.Text = "texto de la linea";
              TextoMarkt.Foreground = new SolidColorBrush(color);
              TextoMarkt.SetValue(Canvas.LeftProperty, m_line.X1 + 5.0);
              TextoMarkt.SetValue(Canvas.TopProperty, m_line.Y1 - 20);
            }
    
            m_canvas.Children.Add(TextoMarkt);
            m_canvas.Children.Add(m_line);
    
            // establecer eventos
            setEvents();
          }
          catch (Exception ex)
          {
            throw ex;
          }
        }
    
        
        /// <summary>
        /// Funcion para establecer eventos de movimiento de la linea
        /// </summary>
        private void setEvents()
        {
          m_line.MouseLeftButtonDown += delegate
          {
            m_line.CaptureMouse();
            m_flagLeftButton = true;
          };
    
          m_line.MouseMove += delegate(object sender, MouseEventArgs e)
          {
            if (m_flagLeftButton)
            {
              m_line.Y1 = m_line.Y2 = e.GetPosition(m_canvas).Y;
            }
          };
    
          m_line.MouseLeftButtonUp += delegate
          {        
            m_line.ReleaseMouseCapture();
            m_flagLeftButton = false;
          };
    
          m_line.MouseEnter += delegate
          {
            Cursor = Cursors.SizeNS;
          };
    
          m_line.MouseLeave += delegate
          {
            Cursor = Cursors.Arrow;
          };
        }
    
    
        private void ColumnSeries_Loaded(object sender, RoutedEventArgs e)
        {
          CrearMarket(10, Colors.Red);
        }
    

    Como en silverlight no se tiene acceso directo a los botones pulsados del raton, he añadido un flag para saber cuando esta pulsado el boton izquierdo del raton.

    Ya me contaras que tal.

     


    Saludos
    David González
    MCP.
    Visita mi Blog en: http://www.dgzornoza.com/
    • Marcado como respuesta CorsarioVasco miércoles, 14 de julio de 2010 8:42
    martes, 13 de julio de 2010 19:08
  • ¡Buenos días campeón!

    Las conclusiones a las que he llegado son las siguientes, tras todo lo que me has comentado (tus aportaciones) y con las pruebas que he ido haciendo:

    1. El Window_ContentRendered es para WPF y que no existe en Silverlight.

    2. Silverlight no se tiene acceso directo a los botones pulsados del raton. Pensé que era un tema del C# por eso al traducir al VB.Net, ya había dispuesto una variable boolean como flag. Gracias por la explicación  ;-)

    3. Al final el setEvents no lo voy a emplear porque si se toca sobre el chart se realiza un desZoom. Asi que se me crean conflictos de eventos. Lo resolveré de otra manera, ya he pensado en ello ;-). Pero he aprendido algo bastante tonto que es como modificar el cursor. Un detalle que le da estilo al trabajo ;-)

    4. El problema de cargar el método CrearMarket en el load de las series genera por cada serie una línea. Asi que tendremos tantas líneas como series. Lo que se puede hacer es controlar el alto del canvas que a penas supone trabajo ya que es capturar un valor e insertarlo en una variable.

    5. Al hacer el zoom el valor de los ejes se ven modificados por lo que el posicionamiento de la línea horizontal se queda desfasada. Tengo que probarlo pero creo que hay que hacer eliminar del canvas la línea y crear una nueva con ese nuevo layout de los ejes.

    6. Este es el punto más importante y que deseo que te sirva de feedback (no sólo vas a aportar tú a los demás ;-)). A tu código, creo, le faltan dos líneas y dos de modificación. Que conste que me ha aportado muchisimo tus comentarios, y sin tu ayuda no hubiera llegado a esta conclusión:

    Si restamos el valor del límite a la altura del canvas no lo va a hacer bien porque uno esta renderizado y el otro no. Por ello, debemos de obtener una referencia de ese límite renderizado mediante su valor en el eje (en un prinicipio pensé en una regla de tres pero es más sencillo que todo eso).

    Te pongo el código de las líneas:

     Dim yAxis As LinearAxis = CType(Chart1.Axes(0), LinearAxis)
     Dim nvalorY As UnitValue = yAxis.GetPlotAreaCoordinate(limite)
    

    Y las líneas modificadas serían:

     .Y1 = Canv.ActualHeight - nvalorY.Value
     .Y2 = Canv.ActualHeight - nvalorY.Value
    

    Gracias por tu inestimable ayuda ;-)

    • Marcado como respuesta CorsarioVasco miércoles, 14 de julio de 2010 8:42
    miércoles, 14 de julio de 2010 8:42

Todas las respuestas

  • Hola Corsario.

    uff, con el mundial apenas he tocado un pc estos dias. !Al fin ganamos el mundial !!!!.

    y ahora a lo nuestro ;).

    Vale, tu codigo me ha ayudado a ver que querias realizar, de modo que te he preparado un codigo (como siempre en c# lo siento, no estoy acostumbrado a usar VB y me costaria el doble).

    En realidad, lo unico que he hecho es coger el alto del canvas y restar la posicion, de modo que matematicamente se invierte la posicion, ademas me he tomado la libertad de crear los eventos para mover la linea ya que me intrigaba.

    Bueno, un codigo vale mas que mil palabras, supongo que a partir de aqui saldran mas cosas, asi que tenemos un punto de partida.

     /// <summary>
      /// Lógica de interacción para MainWindow.xaml
      /// </summary>
      public partial class MainWindow : Window
      {
        private Line m_line;
        private Canvas m_canvas;
    
        public MainWindow()
        {
          InitializeComponent();
        }
    
        /// <summary>
        /// Funcion para crear una marca en el chart
        /// </summary>
        /// <param name="limite">coordenada eje Y para mostrar la linea</param>
        /// <param name="color">color de la linea</param>
        private void CrearMarket(double limite, System.Windows.Media.Color color)
        {
          try
          {
            // obtener dimensiones del canvas
            m_canvas = (Canvas)Helper.GetChildObject<Canvas>(Chart1, "PlotArea");
            double canvasHeight = m_canvas.ActualHeight;
    
            m_line = new Line();
            {
              m_line.Stroke = new SolidColorBrush(color);
              m_line.X1 = 0;
              m_line.X2 = 100;
              m_line.Y1 = canvasHeight - limite; // restar del alto del canvas para invertir coordenadas
              m_line.Y2 = canvasHeight - limite;          
            }
    
            TextBlock TextoMarkt = new TextBlock();
            {
              TextoMarkt.Text = "texto de la linea";
              TextoMarkt.Foreground = new SolidColorBrush(color);
              TextoMarkt.SetValue(Canvas.LeftProperty, m_line.X1 + 5.0);
              TextoMarkt.SetValue(Canvas.TopProperty, m_line.Y1 - 20);
            }
    
            m_canvas.Children.Add(TextoMarkt);
            m_canvas.Children.Add(m_line);
    
            // establecer eventos
            setEvents();
          }
          catch (Exception ex)
          {
            throw ex;
          }
        }
    
        /// <summary>
        /// Funcion para establecer eventos de movimiento de la linea
        /// </summary>
        private void setEvents()
        {
          m_line.MouseLeftButtonDown += delegate
          {
              Mouse.Capture(m_line);
          };
    
          m_line.MouseMove += delegate (object sender, MouseEventArgs e)
          {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
              m_line.Y1 = m_line.Y2 = e.GetPosition(m_canvas).Y;          
            }
          };
    
          m_line.MouseLeftButtonUp += delegate
          {
            Mouse.Capture(null);
          };
    
          m_line.MouseEnter += delegate
          {
            Cursor = Cursors.ScrollNS;
          };
    
          m_line.MouseLeave += delegate
          {
            Cursor = Cursors.Arrow;
          };
        }
    
        private void Window_ContentRendered(object sender, EventArgs e)
        {
          CrearMarket(10, System.Windows.Media.Colors.Black);
        }
      }
    

    La unica nota importante a tener en cuenta, es el evento 'Rendered' de la ventana, esto es necesario para obtener el ActualHeight del canvas, ya que hasta que no se renderiza no se puede obtener el tamaño real renderizado en pantalla. De modo que he usado aqui tu funcion modificando las coordenadas Y1, Y2.

    Ademas me gustaria comentar (para el que lea este post) el metodo 'GetChildObject' que es un metodo extensor para ayudar a obtener un objeto hijo en el arbol visual WPF y existen varias implementaciones por la red, la añado aqui, ya que considero es de mucha ayuda:

     public static class Helper
      {
    
        #region [metodos extensores]
    
        /// <summary>
        /// Metodo extensor para obtener una lista de hijos en el arbol visual del objeto especificado.
        /// </summary>
        /// <typeparam name="T">Tipo de control que quiere obtenerse</typeparam>
        /// <param name="_obj">objeto de dependencia en el cual se buscara</param>
        /// <param name="_name">nombre del control/controles a buscar</param>
        /// <returns>
        /// lista del tipo de control a buscar con todos los controles coincidentes con el nombre y tipo. 
        /// null en caso de no encontrar ninguno
        /// </returns>
        public static List<T> GetChildObjects<T>(this DependencyObject _obj, string _name)
        {
          var retVal = new List<T>();
          for (int i = 0; i < VisualTreeHelper.GetChildrenCount(_obj); i++)
          {
            object c = VisualTreeHelper.GetChild(_obj, i);
            if (c.GetType().FullName == typeof(T).FullName && (String.IsNullOrEmpty(_name) || ((FrameworkElement)c).Name == _name))
            {
              retVal.Add((T)c);
            }
            var gc = ((DependencyObject)c).GetChildObjects<T>(_name);
            if (gc != null)
              retVal.AddRange(gc);
          }
    
          return retVal;
        }
    
        /// <summary>
        /// Metodo extensor para obtener un hijo en el arbol visual del objeto especificado.
        /// </summary>
        /// <typeparam name="T">Tipo de control que quiere obtenerse</typeparam>
        /// <param name="_obj">objeto de dependencia en el cual se buscara</param>
        /// <param name="_name">nombre del control a buscar</param>
        /// <returns>control coincidentes con el nombre y tipo. null en caso de no encontrarlo</returns>
        public static T GetChildObject<T>(this DependencyObject _obj, string _name) where T : DependencyObject
        {
          for (int i = 0; i < VisualTreeHelper.GetChildrenCount(_obj); i++)
          {
            object c = VisualTreeHelper.GetChild(_obj, i);
            if (c.GetType().FullName == typeof(T).FullName && (String.IsNullOrEmpty(_name) || ((FrameworkElement)c).Name == _name))
            {
              return (T)c;
            }
            object gc = ((DependencyObject)c).GetChildObject<T>(_name);
            if (gc != null)
              return (T)gc;
          }
    
          return null;
        }
    
        #endregion [metodos extensores]
      }
    

    esta es la version en C# de la funcion, a ver si puede Corsario poner la traduccion que usa el en VB para los que usen ese lenguaje.

    Ya me contaras si es mas o menos lo que buscas.

     


    Saludos
    David González
    MCP.
    Visita mi Blog en: http://www.dgzornoza.com/
    lunes, 12 de julio de 2010 21:59
  • ¡Buenos días David!

    Gracias por responder. Si, y además jugando bien. Que tiene más merito ;-)

    No te preocupes por lo C#, cada día me cuesta menos traducirlo. ;-). He tardado en responderte porque no consigo encontrar el evento ContentRendered. Mi Chart esta ubicado en un ChildWindow (Silverlight).

    He leido por ahi que aparece cuando hay contenido. Hasta he creado un frame que le he asignado como content el canvas pero nada, no aparece. También probé con el layoutupdate pero sin éxito.

    Por favor, ¿podrías decirme cómo puedo obtenerlo?.

    Respecto a 'GetChildObject'. Tengo que comentarte que para mi fue un gran descubrimiento; encontré la propiedad parent limitada cuando utilizamos WPF o Silverlight. Asi que me documenté en la red y encontré que la gente de C# tenía creadas unas funciones para recorrer los objetos visualmente (no sé por qué no están integradas en el IDE-En sus respectivos controles... en fin... es lo que hay). Encontré ciertas dificultades para pasarlos a VB.net (posiblemente por mi desconocimiento en la materia) pero hallé (después de mucho navegar) alguién que había hecho algo al respecto. Es posible que terminara retocándolas (lo siento, no me puedo estar quieto ;-)) y si no, lamento no poner el nombre de su autor porque de esto hace ya algunos meses. [David, me alegro que me invitaras para que aportara mi granito de arena ;-)]

    Te pongo las mías en vez de traducir las tuyas, al fin y al cabo hacen lo mismo ;-):

    LA LISTA:

     Friend Function GetChildObjects(Of T As FrameworkElement)(ByVal obj As DependencyObject, Optional ByVal name As String = "") As List(Of T)
      Dim child As DependencyObject = Nothing, childList As List(Of T) = New List(Of T)
    
      For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(obj) - 1
       child = VisualTreeHelper.GetChild(obj, i)
    
       If TypeOf child Is T AndAlso (CType(child, T).Name = name Or String.IsNullOrEmpty(name)) Then
        childList.Add(CType(child, T))
       End If
    
       childList.AddRange(GetChildObjects(Of T)(child))
      Next
    
      Return childList
     End Function
    

    EL ELEMENTO: 

     Friend Function GetChildObject(Of T As FrameworkElement)(ByVal obj As DependencyObject, Optional ByVal name As String = "") As T
      Dim child As DependencyObject = Nothing, grandChild As T = Nothing
    
      For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(obj) - 1
       child = VisualTreeHelper.GetChild(obj, i)
    
       If TypeOf child Is T AndAlso (CType(child, T).Name = name Or String.IsNullOrEmpty(name)) Then
        Return CType(child, T)
       Else
        grandChild = GetChildObject(Of T)(child, name)
        If grandChild IsNot Nothing Then Return grandChild
       End If
      Next
    
      Return Nothing
     End Function
    
    Yo las tengo metidas en un módulo para acceder a ellas desde cualquier parte de la aplicación.
    martes, 13 de julio de 2010 11:26
  • Hola Corsario.

    Ok, vale que estamos en silverlight, el codigo anterior era para WPF.

    En lugar del rendered, se puede usar el evento Loaded del column series, por ejemplo con algo como esto:

      <Grid x:Name="LayoutRoot" Background="White">
        <toolkit:Chart HorizontalAlignment="Left" Margin="12,12,0,0" Name="Chart1" Title="Chart Title" VerticalAlignment="Top" Height="276" Width="376">
          <toolkit:ColumnSeries DependentValuePath="X" IndependentValuePath="Y" Loaded="ColumnSeries_Loaded">
            <toolkit:ColumnSeries.ItemsSource>
              <PointCollection>
                <Point>1,10</Point>
                <Point>2,20</Point>
                <Point>3,30</Point>
                <Point>4,40</Point>
              </PointCollection>
            </toolkit:ColumnSeries.ItemsSource>
          </toolkit:ColumnSeries>
        </toolkit:Chart>
      </Grid>
    

    aqui se crea por codigo xaml, pero la idea es que cuando se carguen las series se implemente el evento que llame a la creacion de la marca

     

    codigo .cs adaptado a silverlight:

     private Line m_line;
        private Canvas m_canvas;
        private bool m_flagLeftButton;
    
        public MainPage()
        {
          InitializeComponent();      
        }
    
        /// <summary>
        /// Funcion para crear una marca en el chart
        /// </summary>
        /// <param name="limite">coordenada eje Y para mostrar la linea</param>
        /// <param name="color">color de la linea</param>
        private void CrearMarket(double limite, System.Windows.Media.Color color)
        {
          try
          {
            
            // obtener dimensiones del canvas
            m_canvas = (Canvas)Helper.GetChildObject<Canvas>(Chart1, "PlotArea");
            double canvasHeight = m_canvas.ActualHeight;
    
            m_line = new Line();
            {
              m_line.Stroke = new SolidColorBrush(color);
              m_line.X1 = 0;
              m_line.X2 = 100;
              m_line.Y1 = canvasHeight - limite; // restar del alto del canvas para invertir coordenadas
              m_line.Y2 = canvasHeight - limite;
            }
    
            TextBlock TextoMarkt = new TextBlock();
            {
              TextoMarkt.Text = "texto de la linea";
              TextoMarkt.Foreground = new SolidColorBrush(color);
              TextoMarkt.SetValue(Canvas.LeftProperty, m_line.X1 + 5.0);
              TextoMarkt.SetValue(Canvas.TopProperty, m_line.Y1 - 20);
            }
    
            m_canvas.Children.Add(TextoMarkt);
            m_canvas.Children.Add(m_line);
    
            // establecer eventos
            setEvents();
          }
          catch (Exception ex)
          {
            throw ex;
          }
        }
    
        
        /// <summary>
        /// Funcion para establecer eventos de movimiento de la linea
        /// </summary>
        private void setEvents()
        {
          m_line.MouseLeftButtonDown += delegate
          {
            m_line.CaptureMouse();
            m_flagLeftButton = true;
          };
    
          m_line.MouseMove += delegate(object sender, MouseEventArgs e)
          {
            if (m_flagLeftButton)
            {
              m_line.Y1 = m_line.Y2 = e.GetPosition(m_canvas).Y;
            }
          };
    
          m_line.MouseLeftButtonUp += delegate
          {        
            m_line.ReleaseMouseCapture();
            m_flagLeftButton = false;
          };
    
          m_line.MouseEnter += delegate
          {
            Cursor = Cursors.SizeNS;
          };
    
          m_line.MouseLeave += delegate
          {
            Cursor = Cursors.Arrow;
          };
        }
    
    
        private void ColumnSeries_Loaded(object sender, RoutedEventArgs e)
        {
          CrearMarket(10, Colors.Red);
        }
    

    Como en silverlight no se tiene acceso directo a los botones pulsados del raton, he añadido un flag para saber cuando esta pulsado el boton izquierdo del raton.

    Ya me contaras que tal.

     


    Saludos
    David González
    MCP.
    Visita mi Blog en: http://www.dgzornoza.com/
    • Marcado como respuesta CorsarioVasco miércoles, 14 de julio de 2010 8:42
    martes, 13 de julio de 2010 19:08
  • ¡Buenos días campeón!

    Las conclusiones a las que he llegado son las siguientes, tras todo lo que me has comentado (tus aportaciones) y con las pruebas que he ido haciendo:

    1. El Window_ContentRendered es para WPF y que no existe en Silverlight.

    2. Silverlight no se tiene acceso directo a los botones pulsados del raton. Pensé que era un tema del C# por eso al traducir al VB.Net, ya había dispuesto una variable boolean como flag. Gracias por la explicación  ;-)

    3. Al final el setEvents no lo voy a emplear porque si se toca sobre el chart se realiza un desZoom. Asi que se me crean conflictos de eventos. Lo resolveré de otra manera, ya he pensado en ello ;-). Pero he aprendido algo bastante tonto que es como modificar el cursor. Un detalle que le da estilo al trabajo ;-)

    4. El problema de cargar el método CrearMarket en el load de las series genera por cada serie una línea. Asi que tendremos tantas líneas como series. Lo que se puede hacer es controlar el alto del canvas que a penas supone trabajo ya que es capturar un valor e insertarlo en una variable.

    5. Al hacer el zoom el valor de los ejes se ven modificados por lo que el posicionamiento de la línea horizontal se queda desfasada. Tengo que probarlo pero creo que hay que hacer eliminar del canvas la línea y crear una nueva con ese nuevo layout de los ejes.

    6. Este es el punto más importante y que deseo que te sirva de feedback (no sólo vas a aportar tú a los demás ;-)). A tu código, creo, le faltan dos líneas y dos de modificación. Que conste que me ha aportado muchisimo tus comentarios, y sin tu ayuda no hubiera llegado a esta conclusión:

    Si restamos el valor del límite a la altura del canvas no lo va a hacer bien porque uno esta renderizado y el otro no. Por ello, debemos de obtener una referencia de ese límite renderizado mediante su valor en el eje (en un prinicipio pensé en una regla de tres pero es más sencillo que todo eso).

    Te pongo el código de las líneas:

     Dim yAxis As LinearAxis = CType(Chart1.Axes(0), LinearAxis)
     Dim nvalorY As UnitValue = yAxis.GetPlotAreaCoordinate(limite)
    

    Y las líneas modificadas serían:

     .Y1 = Canv.ActualHeight - nvalorY.Value
     .Y2 = Canv.ActualHeight - nvalorY.Value
    

    Gracias por tu inestimable ayuda ;-)

    • Marcado como respuesta CorsarioVasco miércoles, 14 de julio de 2010 8:42
    miércoles, 14 de julio de 2010 8:42
  • Hola Corsario.

    ;), OK, ese ultimo punto es el que esperaba que me contaras, ya que no tenia muy claro como enlazar las unidades del canvas a las unidades del eje y de forma sencilla. (no he pasado mucho tiempo con el chart)

    Asi que supongo que ya estamos en paz ya que voy a implementar un hibrido de los codigos para una aplicacion que tengo y ademas, la funcion GetChildObject la he conocido gracias a ti. Hasta ahora siempre lo hacia manualmente mediante el TreeViewHelper (no se me ocurrio hacerme una funcion recursiva, pero viene de fabula)

    existen tantas funciones entre el framework y los controles que apenas conocemos un 5% de todo. (a ver si al año que viene subimos al 6% comentando en el foro ;)


    Saludos
    David González
    MCP.
    Visita mi Blog en: http://www.dgzornoza.com/
    miércoles, 14 de julio de 2010 12:25