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

NetBarControl

Actualización.
NetBarControl - Description Item Style (New)




NetBarControl (Outlook Bar) es uno de los controles que muchas veces buscamos para usar en nuestras aplicaciones, pero siempre encontramos en internet versiones pagables y tal vez no contamos con el presupuesto esperado como para comprar uno y las versiones gratis que se logran encontrar, pues como son gratis no implementan en su totalidad la funcionalidad que esperamos encontrar en un control de este tipo. Antes de comenzar a escribir este control dedique tiempo en buscar uno por internet que tuviera toda la funcionalidad o por lo menos una interfaz disponible en modo de diseño, pero, no logre encontrar uno, así que me propuse crearlo como a mí me gustaría que funcionara uno gratis y al final llegue a la conclusión de ¿Porque no hay uno completo, gratis y que incluya el código fuente? y la respuesta es:No es fácil, pero tampoco es cosa de otro mundo. Con esto no digo que otro programador no lo pueda hacer o que los ejemplos en…

ComboBox-MultiColumns

ComboBox-MultiColumns Update


Como bien sabemos el Set de Controles de Windows de .Net Framework incluye el control ComboBox pero este no soporta mostrar multiples columnas, en su lugar muestra una lista simple ya sea agregada manual mente usando la propiedad Items o según el origen de datos definido a traves de la propiedad DataSource, usando la propiedad DisplayMember para indicar la propiedad o campo a mostrar en la lista desplegable. Bien… la ventaja de los controles de .Net Framework es que los podes extender para mejorar su uso en nuestros desarrollos, en este caso extenderemos el Control ComboBox para reemplazar la lista desplegable por nuestra propia versión que mostrara “N” cantidad de columnas que necesitemos, para esto haremos uso de las siguientes clases ToolStripDropDown y ToolStripControlHost. En la ayuda de MSDN podemos encontrar un ejemplo sobre como usar la clase ToolStripDropDown para mostrar un TreeView. parte de este ejemplo lo he tomado para crear este Control ComboBo…

TextEditor

Este control nace a raíz de una pregunta en el foro de Visual Basic .Net, sobre como cambiar el borde de un TextBox a un borde personalizado y además andaba buscando cambiar la apariencia del control NetBarControl para poder aplicarle una nueva propiedad para cambiar el estilo. ejemplo:


Aun que esta imagen solo es un pre-formato de cómo funcionara el control NetBarControl, solo que me distraje creando este nuevo control TextEditor. Así que le echaré mano al terminado este nuevo control.







Bien, la idea inicial era solo agregar botones a un control TextBox, pero luego usando el Mozilla, al descargar unos archivos me percate de un control en la parte inferior de esta venta de descargar, aun que se suele ver mucho en las páginas Web, pero me llamo la atención aquí en el Mozilla y en el Window Live Messenger.


Entonces… me entro el gusano de la curiosidad, agregar la imagen luego de agregar los botones ya no era la parte difícil, la parte curiosa es mostrar el Texto como marca de agua, enco…