C# DataGridView: Long Text Truncated with “…” on the Left Side When the Column is Right-Aligned

后端 未结 3 563
北海茫月
北海茫月 2020-12-10 20:24

I have a question about the cell truncation (replaced with \"...\"):

How to display the replacement \"...\" on the left side of a cell when the column is right-align

相关标签:
3条回答
  • 2020-12-10 21:06

    It's definitely an unusual thing to do - but (like anything else) it can be done. It's a question of measuring the size of the string and comparing it with the size of the cell. (Note that I assume that the data is entered by a user. If you're databinding you basically have to consume other events.)

    This works but might need some fine tuning:

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            dataGridView1.Columns.Add("col1", "col1");
            dataGridView1.Columns[0].CellTemplate.Style.Alignment = DataGridViewContentAlignment.MiddleRight;
            dataGridView1.Columns.Add("col2", "col2");
            dataGridView1.Columns.Add("col3", "col3");
    
            dataGridView1.Rows.Add();
            dataGridView1.CellEndEdit += new DataGridViewCellEventHandler(dataGridView1_CellEndEdit);
            dataGridView1.ColumnWidthChanged += new DataGridViewColumnEventHandler(dataGridView1_ColumnWidthChanged);              
        }
    
        void dataGridView1_ColumnWidthChanged(object sender, DataGridViewColumnEventArgs e)
        {
            if (e.Column.Index == 0)
            {
                // We need to truncate everything again when the width changes
                foreach (DataGridViewRow row in dataGridView1.Rows)
                {
                    RightTruncateText(row.Cells[0]);
                }
            }
        }
    
        void RightTruncateText(DataGridViewCell cell)
        {                        
            // check if the content is too long:
            using (Graphics g = Graphics.FromHwnd(this.Handle))
            {
                SizeF size = g.MeasureString((string)cell.Tag, dataGridView1.Font); // NOTE: using the tag
    
                if (size.Width > cell.Size.Width)
                {
                    StringBuilder truncated = new StringBuilder((string)cell.Tag);
    
                    truncated.Insert(0, "...");
    
                    // Truncate the string until small enough (NOTE: not optimized in any way!)                        
                    while (size.Width > cell.Size.Width)
                    {
                        truncated.Remove(3, 1);
                        size = g.MeasureString(truncated.ToString(), dataGridView1.Font);
                    }
                    cell.Value = truncated.ToString();
                }
                else
                {
                    cell.Value = cell.Tag;
                }
            }
        }
    
        void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)
        {
            if (e.ColumnIndex == 0)
            {
                // Save the value in the tag but show the truncated value
                DataGridViewCell cell = dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex];
                cell.Tag = cell.Value; // Saving the actual state
                RightTruncateText(cell);
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-10 21:23

    I have made an workaround, it's working except the "..." (truncation works well)

        protected override void OnCellPainting(DataGridViewCellPaintingEventArgs e)
        {
            base.OnCellPainting(e);
    
            if (e.RowIndex >= 0 && e.ColumnIndex >= 0 &&
                CustomRightToLeftColumnNames.Contains(this.Columns[e.ColumnIndex].Name))
            {
                // Method 2:
                e.PaintBackground(e.CellBounds, true);
    
                if (e.FormattedValue != null)
                {
                    TextFormatFlags flags = TextFormatFlags.RightToLeft         |
                                            TextFormatFlags.VerticalCenter      |
                                            TextFormatFlags.Right               |
                                            TextFormatFlags.LeftAndRightPadding;// |
                                            //TextFormatFlags.EndEllipsis;
                    TextRenderer.DrawText
                    (
                        e.Graphics,
                        e.FormattedValue.ToString(),
                        e.CellStyle.Font,
                        e.CellBounds,
                        e.CellStyle.ForeColor,
                        flags
                    );
                }
    
                e.Handled = true;
            }
        }
    

    The only problem with this solution is I don't know how to Set the TextFormatFlags to get the right behavior I want, exactly same as when DataGridView.RightToLeft = Yes.

    If I turn on TextFormatFlags.EndEllipsis, the three dots "..." will appear on the left side, but it truncates from the right end of the string.

    I'm not sure which flag of TextFormatFlags enumeration to turn on.

    0 讨论(0)
  • 2020-12-10 21:26

    I've ended up implementing this by creating my own DataGridViewLeftCropTextBoxCell. Unfortunately DataGridViewTextBoxCell::Paint is bit a complicated method Reference Source .NET Framework 4.5.2 which uses many .NETs internal methods.

    But at first I let base class draw background and borders (and if there's no sensible foreground, just leave it).

    Then measure text and shrink it until it fits value bounds.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Drawing;
    
    namespace Project
    {
        public class DataGridViewLeftCropTextBoxCell : DataGridViewTextBoxCell
        {
    
            /// <summary>
            /// Paints contents
            /// </summary>
            /// <param name="graphics"></param>
            /// <param name="clipBounds"></param>
            /// <param name="cellBounds"></param>
            /// <param name="rowIndex"></param>
            /// <param name="cellState"></param>
            /// <param name="value"></param>
            /// <param name="formattedValue"></param>
            /// <param name="errorText"></param>
            /// <param name="cellStyle"></param>
            /// <param name="advancedBorderStyle"></param>
            /// <param name="paintParts"></param>
            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 )
            {
                string formattedString = formattedValue as string;
    
                // Nothing to draw
                if (String.IsNullOrEmpty( formattedString )) {
                    base.Paint( graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts );
                    return;
                }
    
                // Draw parently without foreground
                base.Paint( graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts & ~DataGridViewPaintParts.ContentForeground );
    
                // No foreground?
                if ((paintParts & DataGridViewPaintParts.ContentForeground) == DataGridViewPaintParts.None) {
                    return;
                }
    
                // Calculate value bounds
                Rectangle borderWidths = BorderWidths( advancedBorderStyle );
                Rectangle valBounds = cellBounds;
                valBounds.Offset( borderWidths.X, borderWidths.Y );
                valBounds.Width -= borderWidths.Right;
                valBounds.Height -= borderWidths.Bottom;
    
                bool cellSelected = (cellState & DataGridViewElementStates.Selected) != 0;
    
                // Prepare text flags
                TextFormatFlags flags = ComputeTextFormatFlagsForCellStyleAlignment( this.DataGridView.RightToLeft == RightToLeft.Yes, cellStyle.Alignment, cellStyle.WrapMode );
                if ((flags & TextFormatFlags.SingleLine) != 0) {
                    flags |= TextFormatFlags.EndEllipsis;
                }
    
                // Prepare size of text
                Size s = TextRenderer.MeasureText( graphics,
                    formattedString,
                    cellStyle.Font
                );
    
                // Text fits into bounds, just append
                if (s.Width < valBounds.Width) {
                    TextRenderer.DrawText( graphics,
                        formattedString,
                        cellStyle.Font,
                        valBounds,
                        cellSelected ? cellStyle.SelectionForeColor : cellStyle.ForeColor,
                        flags );
                    return;
                }
    
                // Prepare 
                StringBuilder truncated = new StringBuilder( formattedString );
                truncated.Insert( 0, "..." );
    
                // Truncate the string until it's small enough 
                while ((s.Width > valBounds.Width) && (truncated.Length > 5)) {
                    truncated.Remove( 3, 1 );
                    formattedString = truncated.ToString();
                    s = TextRenderer.MeasureText( graphics,
                        formattedString,
                        cellStyle.Font
                    );
                }
    
                TextRenderer.DrawText( graphics,
                    formattedString,
                    cellStyle.Font,
                    valBounds,
                    cellSelected ? cellStyle.SelectionForeColor : cellStyle.ForeColor,
                    flags
                );
            }
        }
    }
    

    And you can also create your own column type:

    class DataGridViewLeftCropTextBoxColumn : DataGridViewTextBoxColumn
    {
        public override DataGridViewCell CellTemplate
        {
            get { return new DataGridViewLeftCropTextBoxCell(); }
            set { base.CellTemplate = value; }
        }
    }
    

    I've borrowed text shrinking from steinar's answer and TextFormatFlags ComputeTextFormatFlagsForCellStyleAlignment from .NET Framework Reference Source.

    0 讨论(0)
提交回复
热议问题