Ir al contenido principal

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,

Comentarios

  1. Funciona de P*** Madre, Marvin. Pero creo que le encontrado un fallito: Si AllowReadOnly está desactivado y el el foco está en la penúltima celda (Cantidad) y está en modo edición, el foco se pasa a la siguiente celda(Precio Extendido) y no a la siguiente línea.

    ResponderEliminar
    Respuestas
    1. ya lo tengo resuelto.... lo publico luego de un par de pruebas.

      Salu2,

      Eliminar
    2. Estoy aprendiendo vb.net despues de 13 con vb5/6 y otra cosa buena, que no doy solucionado sería que el editcontrol no se coloreara hasta que se coja el foco. Así como cambiarle el color.
      El segundo punto lo tengo solucionado, y el primero parcialmente, ya que solo lo puedo ocultar una vez que ya ha cogido el foco por primera vez. Al arrancar un fomrulario, si tengo el foco en otro control indicado por un color de fondo, que no me aparezca tambien en el datagridview. ¿Sabes como ocultarlo sin que coja el foco y que se active al recibirlo?

      Eliminar
    3. mmmmm.... no logre entender, jajajajajaja.... mira, cualquier duda fuera de mis inventos o controles personalizados, puedes preguntar en los foros de MSDN, te dejo el enlace: http://social.msdn.microsoft.com/Forums/es-ES/vbes/threads

      Salu2,

      Eliminar
  2. en principio es sobre tu control (o cualquier datagridview). Cuando abres un formulario con un datagridview, éste se inicia siempre con una casilla en azul, aunque no tenga el foco. Yo dirigo el control del formulario marcando la casilla con foco con un color de fondo amarillo. El problema es que si tengo el foco en otro control, me aparecen dos "casilleros" en amarillo (un textbox inicial y la primera casilla del datagridview)

    ResponderEliminar
    Respuestas
    1. mmmmm.... entiendo.... acabo de hacer un par de pruebas y solo me queda preguntarte como estas haciendo para cambiar el BackColor cuando el control recibe y pierde el foco.

      y como es una pregunta no precisa de mis controles, bien lo podrias postear en el foro para obtener otras sugerencias.

      Salu2,

      Eliminar
  3. Para activar el color del control, lo hago en el evento que se lanza en el momento de entrar una celda en modo edición:

    Protected Overrides Sub OnEditingControlShowing(e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs)
    e.CellStyle.BackColor = Color.Yellow
    MyBase.OnEditingControlShowing(e)
    End Sub

    Al perder el foco el DGV anulo el currentcell para que no se vea:

    Protected Overrides Sub OnLostFocus(e As System.EventArgs)
    MyBase.CurrentCell = Nothing
    MyBase.OnLostFocus(e)
    End Sub

    El problema es que el DGV siempre se inicia con el editcontrol siempre activado y por lo tanto siempre se muestra. Una vez recibe el foco y lo pierde ya funciona correctamente.

    ResponderEliminar

Publicar un comentario

Entradas populares de este blog

TextBox con Borde Personalizado

Bien, retomando nuevamente mi blog, luego de tanto tiempo ausente, veremos como personalizar el borde del control TextBox con un color diferente. hace poco vi en los foros de MSDN, en el foro de VB.Net esta pregunta, ¿ Cómo puedo cambiar el color del borde de un control TextBox ?, anteriormente también se hizo la misma pregunta en este mismo foro donde yo respondí como hacerlo VB2010 4.0 - Como crear un textbox personalizado . no hay manera fácil de personalizar un control, generalmente se tiene que sobre escribir el evento WndProc para escuchar los mensajes de window y reemplazar la funcionalidad de estos según sea la necesidad o el control. bien, para cambiar el color del borde del control TextBox sin mucha funcionalidad, se debe de escuchar y reemplazar el funcionamiento de los mensajes WM_PAINT y WM_NCPAINT . ¿ Porque WM_PAINT ?, porque cuando cambiamos la propiedad BorderStyle de este control a FixedSingle, quien pinta el borde es el mensaje WM_PAINT no asi el WM_NC

TextBox con Icon/Imagen

Bien, continuando con este articulo: TextBox con borde personalizado , ahora le dare la funcionalidad de poder mostrar un icono o imagen dentro del Control TextBox. Existen dos maneras de hacer esto: Pintar el icono/imagen dentro del control o Pintar el icono/imagen dentro del Non-Client Area del control. Pintar el icono/imagen dentro del control. Antes de escribir el código decidi googlear un poco, para ver si alguien más ya habia tenido la misma idea de usar el mensaje EM_SETMARGINS para dejar el espacio necesario para pintar el icono o imagen ya sea a la derecha o izquierda y me he encontrado con este articulo. Adding an Icon or Control to a TextBox or ComboBox . Pintar el icono/imagen de ntro del Non-Client Area del control. Us ando el Non- Client Area no encontre resultados googleando, así que es la forma que usar e para dib ujar un icono o imagen dentro de un control TextBox. En el control TextEditor que escrib í, utilizo esta manera para pintar el icono

Personalizar DataGridView (II) - Bloquear columnas de solo lectura

Personalizar DataGridView - Actualizaciones Personalizar DataGridView (II.1) - Bloquear columnas de solo lectura. Personalizar DataGridView (III) - Cambiar Diseñador. Bien, continuando con el articulo " Personalizar DataGridView (I) - Pintar área vacía ", ahora lo que haré es darle al control la funcionalidad de bloquear las columnas cuya propiedad " ReadOnly " se establezca en " true ", entiéndase por "Bloquear" el evitar que las columnas cuya propiedad " ReadOnly=true " puedan recibir el foco, ya sea por el teclado o por el ratón ( mouse ). Para tal objetivo agregare una nueva propiedad al control la cual llamare " AllowFocusReadOnlyColumns " cuyo valor predeterminado sera " true ", en caso de ser " false " las columnas marcadas como solo lectura no recibirán el foco. También le daré la funcionalidad de poder avanzar a la siguiente columna al presionar la tecla " ENTER " agregando otr