Friday, February 11, 2011

Overriding DataGridViewTextBoxCell Paint Method

I am trying to override the DataGridViewTextBoxCell's paint method in a derived class so that I can indent the foreground text by some variable amount of pixels. I would like it if the width of the column adjusts so that its total width is the length of my cells text plus the "buffer" indent. Does anyone know of a way to accomplish this? My lame implementation is listed below:

public class MyTextBoxCell : DataGridViewTextBoxCell{ ....
        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) {
           clipBounds.Inflate(100, 0);

            DataGridViewPaintParts pp = DataGridViewPaintParts.Background | DataGridViewPaintParts.Border | DataGridViewPaintParts.ContentBackground
                | DataGridViewPaintParts.ErrorIcon;
            base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, pp);            

                string text = formattedValue as string;

//My lame attempt to indent 20 pixels??
                TextRenderer.DrawText(graphics, text, cellStyle.Font, new Point(cellBounds.Location.X + 20, cellBounds.Location.Y), cellStyle.SelectionForeColor ,TextFormatFlags.EndEllipsis);

}

}

  • You can just hook up to the CellFormattingEvent in the datagridview and do your formatting there. Or, if you're inherting from the DataGridView, you can just override the OnCellFormatting method. The code would look something like this:

                if (e.ColumnIndex == 1)
                {
                    string val = (string)e.Value;
                    e.Value = String.Format("   {0}", val);
                    e.FormattingApplied = true;
                }
    

    Just some rough code, but you get the idea.

    sbeskur : BFree, I want to avoid prepending the text with blank spaces. If I can indent a number of pixels the rendering effect will not be lost when font is changed. Also want to handle this at the Cell level. This possible with other grid products just wondering how I can achieve this with DataGridView.
    From BFree
  • Well I think I have it. In case anyone is interested here is the code below:

    public class MyTextBoxCell : DataGridViewTextBoxCell{ ....
            private static readonly int INDENTCOEFFICIENT = 5;
            protected override Size GetPreferredSize(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex, Size constraintSize) {
                int indent = ((MyRow)OwningRow).Indent * INDENTCOEFFICIENT;
                Size s =  base.GetPreferredSize(graphics, cellStyle, rowIndex, constraintSize);
                int textWidth = 2;  //arbitrary amount
                if (Value != null) {
                    string text = Value as string;
                    textWidth = TextRenderer.MeasureText(text, cellStyle.Font).Width;
                }
    
                s.Width += textWidth + indent;
                return s;
            }
    
            private static readonly StringFormat strFmt = new StringFormat(StringFormatFlags.NoWrap);
    
            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) {
    
                DataGridViewPaintParts pp = DataGridViewPaintParts.Background | DataGridViewPaintParts.Border | DataGridViewPaintParts.ContentBackground
                    | DataGridViewPaintParts.ErrorIcon;
    
                base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, pp);
    
                string text = formattedValue as string;
    
                int indent = ((MyRow)OwningRow).Indent * INDENTCOEFFICIENT;
                strFmt.Trimming = StringTrimming.EllipsisCharacter;
                Rectangle r = cellBounds;
                r.Offset(indent, 0);
                r.Inflate(-indent, 0);
                graphics.DrawString(text, cellStyle.Font, Brushes.Black, r, strFmt);
            }
    
    }
    

    If anyone has a better way I would love to see your solution.

    From sbeskur
  • If you are trying to auto-size the columns (depending on size of the cell contents) then you should look at Column.AutoSizeMode property and Column.DefaultCellStyle property.

    static const int INDENTCOEFF = 5;
    DataGridViewCellStyle cellStyle = new DataGridViewCellStyle();
    
    cellStyle.Padding = 
             new Padding(INDENTCOEFF , 5, INDENTCOEFF , 5); //left,top,right,bottom
    MyColumn.DefaultCellStyle = cellStyle;
    MyColumn.AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells;
    
    sbeskur : Vivek, Thanks for pointing out the Padding property, I feel silly for missing it in the first place. I can simplify my code quite a bit using this technique.
    Vivek : DataGridView Control is the most complex control I've seen. It is easy to overlook some of its neat features.
    From Vivek

0 comments:

Post a Comment