miércoles, 26 de diciembre de 2012

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


martes, 18 de diciembre de 2012

DataGridViewTextEditorColumn (III Parte)

Continuando con el articulo, ahora veremos la clase DataGridViewTextEditorColumn, esta clase hereda de DataGridViewColumn y le he agregado dos propiedades para nuestro propósito  ShowButtonMode y ColumnEditor, esta ultima propiedad es del tipo RepositoryTextEditor.

Los valores para ShowButtonMode son:
  • Always: Indica que los botones de la celda siempre estaran visibles.
  • FocusedRow: Indica que los botones solo se mostraran en la fila activa en el momento.
  • FocusedCell: Indica que los botones solo se mostraran en la celda activa en el momento.
Veamos el código:
 
[ToolboxBitmap(typeof(System.Windows.Forms.TextBox))]
public class DataGridViewTextEditorColumn : DataGridViewColumn
{
    private ShowButtonMode showButtonMode;
    private RepositoryTextEditor repositoryTextEditor;
    private IDesignerHost IDesignerHost
    {
        get
        {
            return this.DataGridView.Site.GetService(typeof(IDesignerHost)) as IDesignerHost;
        }
    }
    
    // Crea el editor de la celda en caso de no existir, en caso de existir
    // se estable el DataGridView que contiene la columna.
    private void CreateColumnEditor()
    {
        if (this.DataGridView == null || this.repositoryTextEditor != null)
        {
            if (this.repositoryTextEditor != null)
                this.repositoryTextEditor.DataGridViewOwner = this.DataGridView;
            return;
        }

        IDesignerHost host = this.IDesignerHost;
        this.ColumnEditor = host.CreateComponent(typeof(RepositoryTextEditor)) as RepositoryTextEditor;
        this.ColumnEditor.DataGridViewOwner = this.DataGridView;
    }
    public DataGridViewTextEditorColumn() : base(new DataGridViewTextEditorCell())
    {
        this.showButtonMode = ShowButtonMode.Always;
    }

    [DefaultValue(typeof(ShowButtonMode), "Always")]
    public ShowButtonMode ShowButtonMode
    {
        get { return this.showButtonMode; }
        set
        {
            this.showButtonMode = value;
        }
    }
    public RepositoryTextEditor ColumnEditor
    {
        get
        {
            this.CreateColumnEditor();
            return this.repositoryTextEditor;
        }
        set 
        { 
            this.repositoryTextEditor = value;
            if (this.DataGridView != null)
                this.DataGridView.Invalidate();
        }
    }
    public override DataGridViewCell CellTemplate
    {
        get
        {
            return base.CellTemplate;
        }
        set
        {
            if (value != null && !value.GetType().IsAssignableFrom(typeof(DataGridViewTextEditorCell)))
            {
                throw new InvalidCastException("Must be a DataGridViewTextEditorCell");
            }
            base.CellTemplate = value;
        }
    }

    public override object Clone()
    {
        DataGridViewTextEditorColumn clone = base.Clone() as DataGridViewTextEditorColumn;
        clone.ColumnEditor = this.ColumnEditor;
        clone.ShowButtonMode = this.ShowButtonMode;

        return clone;
    }
}
 
y el código de ShowButtonMode:
 
public enum ShowButtonMode
{
    Always,
    FocusedRow,
    FocusedCell
}
 

DataGridViewTextEditorColumn (II Parte)
DataGridViewTextEditorColumn (IV Parte)



sábado, 8 de diciembre de 2012

DataGridViewTextEditorColumn (II Parte)

Bien, continuando con el articulo.... la primera clase que veremos sera RepositoryTextEditor, este heredara de Component ya que no tendrá una UI que interactue con el usuario.
 
[ToolboxBitmap(typeof(System.Windows.Forms.TextBox))]
[DesignerSerializer(typeof(RepositoryButtonSerializer), typeof(CodeDomSerializer))]
public class RepositoryTextEditor : Component
{
    private string name;
    private bool allowWhiteSpace;
    private HorizontalAlignment textAlign;
    private CharacterCasing characterCasing;
    private TextEditor.MaskType maskType;
    private RepositoryButtonCollection buttons;
    private static readonly object buttonClick;
    internal System.Windows.Forms.DataGridView DataGridViewOwner { get; set; }

    public delegate void RepositoryButtonPressEventHandler(object sender, RepositoryButtonArgs args);
    public event RepositoryButtonPressEventHandler ButtonClick
    {
        add
        {
            base.Events.AddHandler(buttonClick, value);
        }
        remove
        {
            base.Events.RemoveHandler(buttonClick, value);
        }
    }

    static RepositoryTextEditor()
    {
        buttonClick = new object();
    }
    public RepositoryTextEditor()
    {
        this.allowWhiteSpace = true;
        this.characterCasing = CharacterCasing.Normal;
        this.textAlign = HorizontalAlignment.Left;
        this.maskType = TextEditor.MaskType.None;
        this.buttons = new RepositoryButtonCollection(this);
    }

    [DefaultValue(typeof(TextEditor.MaskType), "None")]
    public TextEditor.MaskType MaskType
    {
        get { return this.maskType; }
        set { this.maskType = value; }
    }
    [DefaultValue(typeof(HorizontalAlignment), "Left")]
    public HorizontalAlignment TextAlign
    {
        get { return this.textAlign; }
        set { this.textAlign = value; }
    }
    [DefaultValue(true)]
    public bool AllowWhiteSpace
    {
        get { return this.allowWhiteSpace; }
        set { this.allowWhiteSpace = value; }
    }
    [DefaultValue(typeof(CharacterCasing), "Normal")]
    public CharacterCasing CharacterCasing
    {
        get { return this.characterCasing; }
        set { this.characterCasing = value; }
    }
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public RepositoryButtonCollection Buttons
    {
        get
        {
            return this.buttons;
        }
    }
    [Browsable(false), DefaultValue("")]
    public string Name
    {
        get
        {
            if (this.Site != null)
                return this.Site.Name;
            return this.name;
        }
        set
        {
            if (value != null)
                this.name = value;
        }
    }

    internal void OnButtonClick(RepositoryButtonArgs args)
    {
        RepositoryButtonPressEventHandler handler = (RepositoryButtonPressEventHandler)base.Events[buttonClick];
        if (handler != null)
            handler.Invoke(this, args);
    }
    internal void InvalidateDataGridView()
    {
        if (this.DataGridViewOwner != null)
            this.DataGridViewOwner.Invalidate();
    }
}
 
No crean que me gusta la idea de mostrar mucho código, porque en muchas ocasiones no se logra entender nada... pero bien, el editor de esta columna sera mi control TextEditor y este componente le dirá al editor que propiedades deberá de cambiar para cumplir bien con su función, por ende en este componente he agregado las siguientes propiedades:

Propiedades
  • Buttons, Colección de botones que alojara la columna como el editor.
  • MaskType, que le indicara al editor que tipo de entrada aceptara cuando se edite la celda. (esta propiedad o enum es del control TextEditor).
  • TextAlign, indicara como se alineara el texto en el editor.
  • AllowWhiteSpace, Indicara si el editor permitirá espacios en blanco entre caracteres.
  • CharacterCasingObtiene o establece si el Editor modifica la condición de mayúscula o minúscula de los caracteres a medida que se escriben.
  • Name, que indicara el nombre del componente.
Eventos
  • ButtonClick, sera el evento que se invocara al presionar cualquiera de los botones de la celda como del editor, la idea de usar un componente es poder tener acceso al evento desde el diseñador sin necesidad de instanciarlo desde el Load del contenedor del DataGridView y recibe la siguiente clase como argumento:
 
    public class RepositoryButtonArgs : EventArgs
    {
        public RepositoryButtonArgs(int rowIndex, int buttonIndex)
        {
            this.RowIndex = rowIndex;
            this.ButtonIndex = buttonIndex;
        }
        public int RowIndex { get; private set; }
        public int ButtonIndex { get; private set; }
    }
 
Ademas podemos ver entre los atributos de este componente el uso de CodeDomSerializer, para serializar o persistir en el diseñador la propiedad Buttons de una manera personalizada y ya habiamos visto su utilización con el control ComboBoxMulticolumns.

DataGridViewTextEditorColumn (III Parte).

jueves, 6 de diciembre de 2012

DataGridViewTextEditorColumn (I Parte)

Como lo dice el titulo, una clase DataGridViewTextEditorColumn... Al principio, cuando cree el control "TextEditor", la idea era luego crear una columna para el control "DataGridView" que utilice mi control como editor de la celda para poder agregarle botones, como también poder usarlo en celdas donde solo se deban digitar números.

Este articulo lo dividiremos en varios articulos para poder explicarlo mejor e intentare explicar mejor cada clase.

Por cuestiones de tiempo no lo había hecho, también por cuestiones de pereza, si!!!.. pereza, jejejeje.... solo de pensar el código que tenia que escribir me daba pereza iniciar. Pero!!!... recientemente donde trabajo hice un formulario para capturar la producción de los empleados, pero el usuario me sugirió que le seria mas fácil si el botón para buscar y eliminar los empleados de la captura apareciera en la misma celda. Ademas en varios desarrollos propios debía implementar este tipo de columna que hasta hoy lo habia resuelto de una manera diferente, para tal fin tenia que personalizar el "DataGridView", así que pense en hacer algo que no tubiera que cambiar nada en este control, que fuera independiente del "DataGridview".

Anteriormente desarrolle un sistema para gimnasios y utilize el DevExpress, valga la publicidad gratuita, excelentes controles (son mi inspiración)... ellos tienen un control llamado GridView y me  fije que las columnas de este control tiene una propiedad para seleccionar el editor de la celda de entre los controles de ellos, así que me parecio buena la idea.

Aun que mi clase no implementara toda la logica, me ayudara a poder interacturar de una mejor manera con el editor de la celda.

Así que entremos en materia, para lograr nuestro objetivo utilizaremos las clases: DataGridViewColumn, DataGridViewTextBoxCell y la interfaz IDataGridViewEditingControl y crearemos un componente que llamare RepositoryTextEditor para asociarlo a la clase DataGridViewTextEditorColumn y poder indicar el tipo de entrada que usara el editor de la celda, las cuales seran las mismas que podemos definir en el TextEditor: None (default), Currency, Numeric, Integer, Time, Percent, como tambien si la columna usara botones y el evento ButtonClick para no hacer esto por código. este ultimo componente aparecera en el ComponentTray del diseñador para tener acceso rapido y facil a el.

(Pantalla en modo de diseño)



Para este demo he utilizado el control nativo "DataGridView" ya que este es el objetivo, tal como se muestra en la imagen, he creado tres columnas tipo DataGridViewTextEditorColumn, una para el código la cual contendra 2 botones, otra para la cantidad cuyo MaskType sera "Numeric" y otra para el precio cuyo MaskType sera "Currency" estas dos ultimas columnas solo aceptaran numeros.

veamos las clases:

 
[ToolboxBitmap(typeof(System.Windows.Forms.TextBox))]
public class DataGridViewTextEditorColumn : DataGridViewColumn
{
}
public class DataGridViewTextEditorCell : DataGridViewTextBoxCell
{
}
class DataGridViewTextEditorControl : TextEditor.TextEditor, IDataGridViewEditingControl
{
}

// Componente para configurar el editor de la celda.
[ToolboxBitmap(typeof(System.Windows.Forms.TextBox))]
[DesignerSerializer(typeof(RepositoryButtonSerializer), typeof(CodeDomSerializer))]
public class RepositoryTextEditor : Component
{
}
 

estas 4 clases son las clases principales, las demás que no mencionare son de implementacion interna y ya las hemos vistos en articulos anteriores, no en detalle pero si para que las usamos.

DataGridViewTextEditorColumn (II Parte)


miércoles, 25 de enero de 2012

NetBarControl - Description Item Style (New)




  Mejoras.
  • Permite mostrar una descripción sobre el Item, permitiendo cambiar el tipo de fuente y el color de la fuente. La imagen usada para este estilo sera la asignada a la propiedad "LargeImagen" la cual se mostrara de izquierda a derecha.
  • Uso de la rueda del raton (Mouse Wheel) para probocar el movimiento de desplazamiento arriba/abajo.
Descargar

¿Como implementar esta nueva versión?
Simple, solo tienes que reemplazar el archivo "NetBarControl.dll" actual por este nuevo, recompilar tu projecto y listo.
¿Como seleccionar este nuevo estilo para un grupo?
Solo debes de seleccionar el grupo al cual le quieres cambiar el estilo y establecer la propiedad "Style = DescriptionItemList"


y como siempre.... no olviden dejar sus comentarios y cualquier problema no duden en hacermelo saber para hacer las correpciones del caso.

Salu2,


Actualizaciones:


29 Sep 2012 - Download

Correpciones:
  1. Se corrigieron los dos problemas reportados por Jose Luis, 1) Error al collapsar el control y error al reducir el tamaño del control a un tamaño muy mínimo.
  2. Se corrigió el error reportado por ElPes al momento de agregar un grupo en tiempo de ejecución cuando este control no tiene grupos previamente definidos en tiempo de diseño.

lunes, 2 de enero de 2012

Personalizar DataGridView (III) - Cambiar Diseñador.

Articulos anteriores:
Personalizar DataGridView (I) - Pintar área vacía.
Personalizar DataGridView (II) - Bloquear columnas de solo lectura.
Personalizar DataGridView (II.1) - Saltar columna usando un metodo.

Como ya hemos visto en los artículos anteriores sobre este mismo tema, ahora lo que haré es modificar el diseñador de este control.

¿Con que objetivo?
Bien... este es un control que se suele usar mucho en los desarrollos, para mostrar muchos registros de forma vertical, pero en lo personal, cuando tengo más de 6 columnas en este control y necesito modificar una o dos columnas, cualquiera de sus propiedades, se me olvida que no puedo con un simple "click" sobre el encabezado de la columna seleccionar esta para poderla modificar, para esto tengo que usar el editor de la colección "Columns", que no digo que este mal.

pero!!!!... porque este control no implementa esta funcionalidad que vendría a ser de mucha utilidad, bueno!!!!... desde mi punto de vista.

veamos un posible caso de tantos.

Caso #1:
Tienes este control con unas 15 columnas y necesitas modificar el ancho de la columna 10 y no la puedes ver porque en el diseñador solo puedes ver las primeras 8 columnas, tienes que buscar la propiedad "Columns" para abrir su editor para poder modificar esta propiedad.

si pudieras usar la barra de desplazamiento en tiempo de diseño y solo seleccionar la columna que queremos como si fuera otro control más, seria mucho mejor este control, o ¿Me equivoco? controles de terceros lo pueden hacer, no mencionare ninguno.

Pero, gracias Microsoft por haber creado este control tan potente y flexible, y proveernos las clases necesarias para poder cambiar la funcionalidad de este control.

Así es, le daremos esta funcionalidad a nuestro control que hemos estado personalizando.

Para esto reemplazare el diseñador del control.

[C#]
 
[Designer(typeof(MEPDataGridViewDesigner), typeof(IDesigner))]
public class MEPDataGridView : DataGridView
{
}
 
internal class MEPDataGridViewDesigner : ControlDesigner
{
}
 
Al cambiar el diseñador del control nos vemos obligados a proveer un nuevo "Smart Tag".




como podemos notar en la imagen, ademas hemos agregado dos propiedades más al "Smart Tag", "Fill Empty Area" y "Enable Focus Read-Only Columns", propiedades que hemos agregado a nuestro control en los artículos anteriores.

bien, para implementar nuestra nueva funcionalidad al control he sobre escrito el metodo "WndProc" de la clase "ControlDesigner" para escuchar los mensajes "WM_RBUTTONDOWN", "WM_LBUTTONDOWN", "WM_LBUTTONUP" y "WM_RBUTTONUP", este ultimo con la idea de mostrar un menú contextual con la opción para borrar la columna al presionar el botón derecho de ratón (Mouse) sobre esta. Tal como se muestra en la siguiente imagen.


[C#]
 
internal class MEPDataGridViewDesigner : ControlDesigner
{
    [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
    protected override void WndProc(ref Message m)
    {
        switch (m.Msg)
        {
            case NativeMethods.WM_RBUTTONDOWN:
            case NativeMethods.WM_LBUTTONDOWN:
                this.WmLButtonDown(ref m);
                return;
            case NativeMethods.WM_RBUTTONUP:
                this.WmRButtonUp(ref m);
                return;
            case NativeMethods.WM_LBUTTONUP:
                this.WmLButtonUp(ref m);
                return;
        }
        base.WndProc(ref m);
    }
}
 

como también el método "OnSetCursor" y para activar la barra de desplazamiento horizontal en tiempo de diseño he sobre escrito la función "GetHitTest"

[C#]
 
internal class MEPDataGridViewDesigner : ControlDesigner

    protected override void OnSetCursor()
    {
        Point point = this.Owner.PointToClient(Control.MousePosition);
        int columnIndex = this.GetColumnIndexByPoint(point);
        if (columnIndex > -1)
            Cursor.Current = Cursors.Default;
        else
            base.OnSetCursor();
    }
    protected override bool GetHitTest(Point point)
    {
        return this.IsOverHScrollBar(point);
    }
}
 
Dando como resultado un control "DataGridView" personalizado con la funcionalidad de poder seleccionar las columnas en tiempo de diseño para poder cambiar sus propiedades o eliminar sin necesidad de usar el editor de la colección de columnas.




Descargar:
Codigo Fuente [C#]
Codigo Fuente [VB.NET] (Pendiente)

bien!!!... espero les sea de utilidad y no olviden dejar sus comentarios.


Salu2,