domingo, 29 de noviembre de 2015

Windows Forms. DataGridView. Columna para números con decimales (I)

A raíz de la publicación del artículo DataGridView. Columna que acepta sólo números. un par de lectores del blog me cuestionaron sobre la forma de modificar el ejemplo para  añadir la posibilidad de introducir números decimales.

Con el objetivo de responder a esta duda vamos a ver cómo podemos definir una columna a la que le podamos indicar no sólo el número de decimales a mostrar, si no también el separador decimal a utilizar a la hora de mostrar los valores.

Como en el ejemplo de la columna para números enteros voy a crear dos nuevas clases: una clase NumericGridColumn para definir la columna y que heredará de DataGridViewColumn, y otra clase NumericGridCell para la celda y que heredará de DataGridViewTextBoxCell.

using System;
using System.ComponentModel;
using System.Globalization;
using System.Windows.Forms;

namespace GridExtension
{
    public class NumericGridColumn : DataGridViewColumn
    {

    }

    public class NumericGridCell: DataGridViewTextBoxCell
    {

    }

}
Imports System.ComponentModel
Imports System.Globalization
Imports System.Windows.Forms

Public Class NumericGridColumn
    Inherits DataGridViewColumn

End Class

Public Class NumericGridCell
    Inherits DataGridViewTextBoxCell

End Class

En primer lugar voy a crear dos nuevas propiedades en la clase NumericGridColumn: una propiedad DecimalSeparator que permitirá definir el carácter a utilizar como separador decimal, y una propiedad DecimalDigits que indicará el número de decimales permitido.

Crearé también una tercera propiedad NumberFormat para uso interno que devolverá el objeto NumberFormatInfo a utilizar para formatear los números de las celdas. Si no se han indicado valores para las propiedades DecimalSeparator y DecimalDigits la propiedad NumberFormat utilizará los valores definidos para la cultura actual.

    public class NumericGridColumn : DataGridViewColumn
    {
        private NumberFormatInfo _numberFormat;
        private string _decimalSeparator;
        private int _decimalDigits = -1;

        [Category("Appearance")]
        public string DecimalSeparator {
            get {
                if (string.IsNullOrEmpty(_decimalSeparator))
                    return CultureInfo.CurrentUICulture.NumberFormat.NumberDecimalSeparator;
                else
                    return _decimalSeparator;
            }
            set
            {
                if (value.Length != 1)
                    throw new ArgumentException("El separador decimal debe ser un único caracter");
                _decimalSeparator = value;
            }
        }

        [Category("Appearance")]
        public int DecimalDigits {
            get
            {
                if (_decimalDigits < 0)
                    return CultureInfo.CurrentUICulture.NumberFormat.NumberDecimalDigits;
                else
                    return _decimalDigits;
            }
            set { _decimalDigits = value; }
        }

        internal NumberFormatInfo NumberFormat
        {
            get
            {
                if (_numberFormat == null)
                    _numberFormat = new NumberFormatInfo();
                _numberFormat.NumberDecimalSeparator = DecimalSeparator;
                _numberFormat.NumberDecimalDigits = DecimalDigits;
                return _numberFormat;
            }
        }
    }
Public Class NumericGridColumn
    Inherits DataGridViewColumn

    Private _numberFormat As NumberFormatInfo
    Private _decimalSeparator As String
    Private _decimalDigits As Integer

    <Category("Appearance")>
    Public Property DecimalSeparator As String
        Get
            If String.IsNullOrEmpty(_decimalSeparator) Then
                Return CultureInfo.CurrentUICulture.NumberFormat.NumberDecimalSeparator
            Else
                Return _decimalSeparator
            End If
        End Get
        Set(ByVal value As String)
            If value.Length <> 1 Then
                Throw New ArgumentException("El separador decimal debe ser un único caracter")
            End If
            _decimalSeparator = value
        End Set
    End Property

    <Category("Appearance")>
    Public Property DecimalDigits As Integer
        Get
            If _decimalDigits < 0 Then
                Return CultureInfo.CurrentUICulture.NumberFormat.NumberDecimalDigits
            Else
                Return _decimalDigits
            End If
        End Get
        Set(ByVal value As Integer)
            _decimalDigits = value
        End Set
    End Property

    Friend ReadOnly Property NumberFormat As NumberFormatInfo
        Get
            If _numberFormat Is Nothing Then _numberFormat = New NumberFormatInfo()
            _numberFormat.NumberDecimalSeparator = DecimalSeparator
            _numberFormat.NumberDecimalDigits = DecimalDigits
            Return _numberFormat
        End Get
    End Property

End Class




Para completar el código de la columna en el constructor de la clase NumericGridColumn voy a llamar al constructor de DataGridViewColumn pasándole una instancia de NumericGridCell como plantilla de celda. Voy a sobrescribir también la propiedad CellTemplate para comprobar que la celda que se establece como plantilla es del tipo NumericGridCell.

Por último sobrescribiré el método Clone para que se pasen los valores de las propiedades DecimalSeparator y DecimalDigits cuando se cree una columna clonando una ya existente.

        public NumericGridColumn():base (new NumericGridCell()) { }

        public override DataGridViewCell CellTemplate
        {
            get
            {
                return base.CellTemplate;
            }
            set
            {
                if (value != null &&
                        !value.GetType().IsAssignableFrom(typeof(NumericGridCell)))
                    throw new InvalidCastException("Debe especificar una instancia de NumericGridCell");
                base.CellTemplate = value;
            }
        }

        public override object Clone()
        {
            NumericGridColumn newColumn = (NumericGridColumn)base.Clone();
            newColumn.DecimalSeparator = DecimalSeparator;
            newColumn.DecimalDigits = DecimalDigits;
            return newColumn;
        }
    Public Sub New()
        MyBase.New(New NumericGridCell())
    End Sub

    Public Overrides Property CellTemplate As DataGridViewCell
        Get
            Return MyBase.CellTemplate
        End Get
        Set(value As DataGridViewCell)
            If value IsNot Nothing AndAlso
                Not value.GetType().IsAssignableFrom(GetType(NumericGridCell)) Then
                Throw New InvalidCastException("Debe especificar una instancia de NumericGridCell")
            End If
            MyBase.CellTemplate = value
        End Set
    End Property

    Public Overrides Function Clone() As Object
        Dim newColumn As NumericGridColumn = MyBase.Clone()
        newColumn.DecimalSeparator = DecimalSeparator
        newColumn.DecimalDigits = DecimalDigits
        Return newColumn
    End Function

Artículo siguiente:
Windows Forms. DataGridView. Columna para números con decimales (y II)

1 comentario:

  1. Gracias Asiel, pero parece que falta la clase NumericGridCell, ya que no hace nada el eontrol, hace exactamente lo que hace la columna Integer.Es decir, no da el formato de números con puntos y decimales
    Lo puedes subir ?
    Es urgente
    Si quieres lo puedes enciar a mi email tambien.
    Gracias

    ResponderEliminar