jueves, 22 de diciembre de 2011

Personalizar DataGridView (II.1) - Bloquear columnas de solo lectura

Bien, continuando con la personalización del control DataGridView y como ya hemos visto en los artículos anteriores.

Personalizar DataGridView (III) - Cambiar Diseñador.

Continuaremos con el articulo II, en ocasiones se nos presenta la necesidad de saltar "x" columna de este control sin necesidad de que esta este marcada como solo lectura, por el simple hecho de que el usuario no quiere pasar sobre esta columna porque no necesita modificar su contenido, salvo en aquellos casos que si se requiera, generalmente no se quiere atrasar pasando sobre estas columnas.

Para estos casos lo que yo he hecho es agregarle un evento al control al cual he llamado "BeforeFocusColumn" el cual se ejecuta antes de cambiar la columna activa, con la idea de validar la columna que recibirá el foco, validar y en caso de ser necesario cambiar esta columna por la que nosotros necesitemos que reciba el foco.

ejemplo:
 
private void view_BeforeFocusColumn(object sender, BeforeFocusColumnEventArgs e)
{
    if (e.NewColumnIndex == this.colDescripcion.Index)
    {
        if (e.OldColumnIndex == this.colPrecioVenta.Index)
        {
            e.NewColumnIndex = this.colCodigo.Index;
        }
        else
        {
            // descomente esta linea para provocar una excepción al intentar
            // asignar a CurrentCell una celda no visible.
            //e.NewColumnIndex = this.colMarca.Index;
            e.NewColumnIndex = this.colPrecioVenta.Index;
        }
    }
}
 
Como podemos ver en el código de ejemplo, el evento recibe dos parámetros:
object sender: que viene a ser el control que invoca el evento.
BeforeFocusColumnEventArgs e: un nuevo argumento de evento que he creado para pasar la información necesaria al evento.

[C#]
 
public class BeforeFocusColumnEventArgs : EventArgs
{
    private MEPDataGridView Owner = null;
    private int vNewColumnIndex = -1;
    public int OldColumnIndex { get; private set; }
    public int NewColumnIndex 
    {
        get { return vNewColumnIndex; }
        set
        {
            if (this.Owner != null && this.Owner.IsHandleCreated)
            {
                if (value < 0 || value > (this.Owner.Columns.Count - 1))
                    throw new Exception("Invalid current cell index");
                else if (!this.Owner.Columns[value].Visible)
                    throw new Exception("Current cell cannot be set to an invisible cell.");
                this.vNewColumnIndex = value;
            }
        }
    }

    public BeforeFocusColumnEventArgs(int currentColumnIndex, int focusColumnIndex, MEPDataGridView owner)
    {
        this.Owner = owner;
        this.OldColumnIndex = currentColumnIndex;
        this.NewColumnIndex = focusColumnIndex;
    }
}
 
como podemos observar en el constructor de la clase la cual hereda de "EventArgs",  también recibe como parámetro el control propietario, esto con la idea de; hay que validar la columna que el programador indique que recibirá el foco en sustitución de la columna que el control nos indica que recibirá el foco.

esta validación la puede haber hecho desde el evento que ejecuta el evento pero!!!!... al hacer la validación y provocar la excepción la linea donde se marcara la excepción sera dentro de la clase "Program" en el caso de [C#], en la linea "Application.Run(new Form1());" y aquí sera difícil saber donde se ocasiono la excepción; así que, para que se marque la linea que provoca la excepción es mejor provocar esta en la propiedad "NewColumnIndex" cuando su valor es cambiado.

Este nuevo evento "BeforeFocusColumn" se ejecuta o dispara desde del evento "SetCurrentCellAddressCore".

[C#]
 
protected override bool SetCurrentCellAddressCore(int columnIndex, int rowIndex, bool setAnchorCellAddress, bool validateCurrentCell, bool throughMouseClick)
{
    if (!throughMouseClick)
    {
        int currentCellIndex = -1;
        if (this.CurrentCell != null)
            currentCellIndex = this.CurrentCell.ColumnIndex;

        BeforeFocusColumnEventArgs ea = new BeforeFocusColumnEventArgs(currentCellIndex, columnIndex, this);
        this.OnBeforeFocusColumn(ea);
        if (ea.NewColumnIndex != columnIndex)
        {
            // unselect old column index
            this.SetSelectedCellCore(columnIndex, rowIndex, false);
            // select new column index
            this.SetSelectedCellCore(ea.NewColumnIndex, rowIndex, true);
            columnIndex = ea.NewColumnIndex;
        }
    }

    return base.SetCurrentCellAddressCore(columnIndex, rowIndex, setAnchorCellAddress, validateCurrentCell, throughMouseClick);
}
 
Ademas, como podemos ver en el código, nuestro nuevo evento es ejecutado solo si la celda es cambiada por el teclado, en caso de ser cambiada por el ratón (mouse) no se ejecutara permitiendo que la celda reciba el foco, ya que la idea es esta, que el usuario final pueda modificar el contenido de la celda, posicionándose en la celda usando el ratón (mouse) pero no el teclado.



Como siempre, espero les sea de utilidad y no olviden dejar sus comentarios.


Salu2,