Ir al contenido principal

DataGridViewTextEditorColumn (IV Parte)

Ahora veremos la clase DataGridViewTextEditorCell, esta clase como ya había comentado al inicio de este articulo, hereda de DataGridViewTextBoxCell, esta clase se encargara de pintar los botones e interactuar con los eventos del ratón (Mouse). no publicare mucho código de esta clase, solo mostrare y explicare los eventos y métodos principales.

en esta clase he sobre escrito el evento Paint(), veamos el código:
 
protected override void Paint
(
    Graphics graphics, 
    Rectangle clipBounds, 
    Rectangle cellBounds, 
    int rowIndex, 
    DataGridViewElementStates cellState, 
    object value, 
    object formattedValue, 
    string errorText, 
    DataGridViewCellStyle cellStyle, 
    DataGridViewAdvancedBorderStyle advancedBorderStyle, 
    DataGridViewPaintParts paintParts
)
{
    RepositoryButtonCollection btnCollection = null;
    if (this.columnButtons.ContainsKey(this.RowIndex))
        btnCollection = this.columnButtons[this.RowIndex] as RepositoryButtonCollection;

    //if (btnCollection != null && btnCollection.Count > 0)
    paintParts &= ~DataGridViewPaintParts.ContentForeground;

    using (BufferedGraphics bg = BufferedGraphicsManager.Current.Allocate(graphics, cellBounds))
    {
        base.Paint(bg.Graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts);
        this.InternalPaintButtons(bg.Graphics, cellBounds);
        bg.Render();
    }

    int totalBtnsWidth = 0;
    if (btnCollection != null)
    {
        totalBtnsWidth = 1;
        foreach (RepositoryButton btn in btnCollection)
        {
            if (btn.Visible)
                totalBtnsWidth += btn.Bounds.Width;
        }
    }

    Color foreColor = cellStyle.ForeColor;
    if ((cellState & DataGridViewElementStates.Selected) > 0)
        foreColor = cellStyle.SelectionForeColor;

    TextFormatFlags flags = TextFormatFlags.VerticalCenter;
    if (cellStyle.Alignment == DataGridViewContentAlignment.BottomLeft ||
        cellStyle.Alignment == DataGridViewContentAlignment.MiddleLeft ||
        cellStyle.Alignment == DataGridViewContentAlignment.TopLeft)
        flags |= TextFormatFlags.Left;
    if (cellStyle.Alignment == DataGridViewContentAlignment.BottomCenter ||
        cellStyle.Alignment == DataGridViewContentAlignment.MiddleCenter ||
        cellStyle.Alignment == DataGridViewContentAlignment.TopCenter)
        flags |= TextFormatFlags.HorizontalCenter;
    if (cellStyle.Alignment == DataGridViewContentAlignment.BottomRight ||
        cellStyle.Alignment == DataGridViewContentAlignment.MiddleRight ||
        cellStyle.Alignment == DataGridViewContentAlignment.TopRight)
        flags |= TextFormatFlags.Right;

    Rectangle contentForegroundBounds = new Rectangle(cellBounds.X, cellBounds.Y, cellBounds.Width - totalBtnsWidth, cellBounds.Height);
    TextRenderer.DrawText
        (
            graphics,
            formattedValue.ToString(),
            cellStyle.Font,
            contentForegroundBounds,
            foreColor,
            flags
        );
}
 
bueno, me disculparan un poco por el código pero como recientemente lo escribí no me ha quedado tiempo de depurar o organizarlo bien y he estado haciendo pruebas según lo uso... por ejemplo.

se llama el Paint base de la clase pero se le indica que no pinte el ContentBackground con la idea de pintarlo más abajo, porque?, como podrán ver el evento Paint base se llamada dentro de un buffer gráfico para evitar el parpadeo de los botones al mover el cursor repetidamente y constantemente sobre los botones, esto lo hice para probarlo y hasta ahorita fue la manera en que pensé evitarlo, aunque un problema secundario era que no se pintaba el texto de la celda, porque? ni idea... seguro algún problema con mi buffer y el código en la clase base, algo esta entrando en conflicto y probando seguro la clase base usa la clase TextRenderer para dibujar el texto ya que haciendo pruebas también me dio ese problema, por eso deje por fuera del buffer el dibujado del texto.

luego de invocar el Paint base llamo un método interno que se encarga de pintar los botones de cada celda en cada linea según la propiedad ShowButtonMode en la clase DataGridViewTextEditorColumn

 
private void InternalPaintButtons(Graphics g, Rectangle bounds)
{
    int btnsWidth = 1;
    foreach (RepositoryButton cellButton in this.OwnerColumn.ColumnEditor.Buttons)
        btnsWidth += cellButton.Width < 0 ? this.defaultBtnWidth : cellButton.Width;

    RepositoryButtonCollection buttonCollection = new RepositoryButtonCollection();

    int x = bounds.Right - btnsWidth - 1;
    foreach (RepositoryButton cellButton in this.OwnerColumn.ColumnEditor.Buttons)
    {
        if (!cellButton.Visible)
            continue;

        int w = cellButton.Width < 0 ? this.defaultBtnWidth : cellButton.Width;

        RepositoryButton btn = new RepositoryButton();
        btn.Image = cellButton.Image;
        btn.Visible = cellButton.Visible;
        btn.Enabled = cellButton.Enabled;
        btn.Width = cellButton.Width;
        btn.Bounds = new Rectangle(x, bounds.Y, w, bounds.Height - 1);
        buttonCollection.Add(btn);

        x += w;
    }

    if (this.columnButtons.ContainsKey(this.RowIndex))
        this.columnButtons[this.RowIndex] = buttonCollection;
    else
        this.columnButtons.Add(this.RowIndex, buttonCollection);

    foreach (RepositoryButton btn in buttonCollection)
    {
        if (btn.Visible)
        {
            bool showBtns = false;
            if (this.OwnerColumn.Site != null)
                showBtns = true;
            else if (this.OwnerColumn.ShowButtonMode == ShowButtonMode.FocusedRow)
                showBtns = this.DataGridView.CurrentRow != null &&
                           this.DataGridView.CurrentRow.Index == this.RowIndex;
            else if (this.OwnerColumn.ShowButtonMode == ShowButtonMode.FocusedCell)
                showBtns = this.DataGridView.CurrentCell != null &&
                           this.DataGridView.CurrentCell.ColumnIndex == this.ColumnIndex &&
                           this.DataGridView.CurrentCell.RowIndex == this.RowIndex;
            else
                showBtns = true;

            if (showBtns)
                this.InternalPaintButton(g, btn);
        }
    }
}
 
... siempre se me olvida documentar cada linea y luego tengo problemas para recordar para que sirven, generalmente primero escribo y después lo documento si funciona... ;-), pero... así se vuelve más interesante para los lectores ;-)

tratare de explicarlo de una forma breve:

el primer foreach calcula el ancho de los botones en la celda:
 
int btnsWidth = 1;
foreach (RepositoryButton cellButton in this.OwnerColumn.ColumnEditor.Buttons)
    btnsWidth += cellButton.Width < 0 ? this.defaultBtnWidth : cellButton.Width;
 
el segundo foreach estable la posición de dibujado de cada botón:
 
int x = bounds.Right - btnsWidth - 1;
foreach (RepositoryButton cellButton in this.OwnerColumn.ColumnEditor.Buttons)
{
    if (!cellButton.Visible)
        continue;

    int w = cellButton.Width < 0 ? this.defaultBtnWidth : cellButton.Width;

    RepositoryButton btn = new RepositoryButton();
    btn.Image = cellButton.Image;
    btn.Visible = cellButton.Visible;
    btn.Enabled = cellButton.Enabled;
    btn.Width = cellButton.Width;
    btn.Bounds = new Rectangle(x, bounds.Y, w, bounds.Height - 1);
    buttonCollection.Add(btn);

    x += w;
}
 
y el tercer foreach se encarga de pintar cada botón:
 
foreach (RepositoryButton btn in buttonCollection)
{
    if (btn.Visible)
    {
        bool showBtns = false;
        if (this.OwnerColumn.Site != null)
            showBtns = true;
        else if (this.OwnerColumn.ShowButtonMode == ShowButtonMode.FocusedRow)
            showBtns = this.DataGridView.CurrentRow != null &&
                       this.DataGridView.CurrentRow.Index == this.RowIndex;
        else if (this.OwnerColumn.ShowButtonMode == ShowButtonMode.FocusedCell)
            showBtns = this.DataGridView.CurrentCell != null &&
                       this.DataGridView.CurrentCell.ColumnIndex == this.ColumnIndex &&
                       this.DataGridView.CurrentCell.RowIndex == this.RowIndex;
        else
            showBtns = true;

        if (showBtns)
            this.InternalPaintButton(g, btn);
    }
}
 
si se esta en tiempo de diseño siempre se mostraran los botones y si esta en ejecución se pintaran según la propiedad ShowButtonMode de clase DataGridViewTextEditorColumn como lo mencione anteriormente.

Evento InitializeEditingControl, este evento se ha sobre escrito para inicializar el editor de la celda cambiando algunas propiedades de este según se haya indicado en la clase RepositoryTextEditor.

veamos el código:
 
public override void InitializeEditingControl(int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
{
    base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle);
    DataGridViewTextEditorControl editor = this.DataGridView.EditingControl as DataGridViewTextEditorControl;
    editor.Buttons.Clear();

    if (this.columnButtons.ContainsKey(this.RowIndex))
    {
        RepositoryButtonCollection btnCollection = this.columnButtons[this.RowIndex] as RepositoryButtonCollection;
        foreach (RepositoryButton btn in btnCollection)
        {
            TextEditor.TextEditorButton editorBtn = new TextEditor.TextEditorButton()
            {
                Image = btn.Image,
                Width = btn.Width,
                Visible = btn.Visible,
                Enabled = btn.Enabled
            };
            editor.Buttons.Add(editorBtn);
        }
    }

    editor.MaskType = this.OwnerColumn.ColumnEditor.MaskType;
    editor.Text = initialFormattedValue.ToString();
    editor.TextAlign = this.OwnerColumn.ColumnEditor.TextAlign;
    editor.AllowWhiteSpace = this.OwnerColumn.ColumnEditor.AllowWhiteSpace;
    editor.CharacterCasing = this.OwnerColumn.ColumnEditor.CharacterCasing;
    editor.BorderStyle = TextEditor.BorderStyle.NoBorder;
    editor.AutoHeight = false;
    editor.Dock = DockStyle.Fill;
    editor.EnterMoveNextControl = false;
}
 
y con este último capitulo cerramos este artículo, espero sea de utilidad esta clase DataGridViewTextEditorColumn y cualquier problema o duda, estamos para ayudar y aclarar.

Código Fuente:
CSharp_DataGridViewTextEditorColumn


Comentarios

  1. Hola Marvin, recientemente descargué un código realizado por usted, realmente es excelente, pero se encuentra en C, se refiere a la activación y desactivación de los checkbox de un Treeview, permitiendo 3 estados. Lo he convertido a VB.NET sin éxito, no encuentro fallas aparentes y he respetado las propiedades indicadas. Le agradecería me ayudara. La pregunta completa la he publicado en el foro de msdn. Saludos!

    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