Format DataGridView Cell without changing base value?

I have an unbound DataGridView (in VS 2008), of which one column contains the file path. I would like to format a string using the TextRenderer class on the ColumnWidthChanged event without changing the underlying value. The problem is that the contents of the table are saved when the form is closed and I don't want to keep the formatted value. I think I'm in too deep to see the obvious solution, so depending on you guys, I'm pointing this out :-).

The idea is to display this:

C: \ Program Files \ Microsoft Visual Studio 8 \ SDK \ v2.0 \ Bin \ gacutil.exe

... like this (depending on the column width):

C: \ Program Files \ Microso ... \ gacutil.exe


I think I spoke too early. I am getting very strange results from TextRenderer.MeasureText (). If I hardcode the path value as "C: \ Documents and Settings \ jluce \ My Documents \ Downloads" it ends up as C: \ Documents and Settings \ jluce \ M ... \ Downloads \ 0wnloads ". If I dont encode it (like below), it gets corrupted every time the column is resized.

This is what it looks like after the pair is resized: Screenshot

Here's what I'm doing now.

  if (e.ColumnIndex == 1)
  {
    foreach (DataGridViewRow Row in mappingsDataGrid.Rows)
    {
      string Path = (string)Row.Cells[1].Value;
      Path = Path.Trim();

      TextRenderer.MeasureText(Path, e.CellStyle.Font,
        new Size(mappingsDataGrid.Columns[e.ColumnIndex].Width, Row.Height),
          TextFormatFlags.ModifyString | TextFormatFlags.PathEllipsis);

      e.Value = Path;
    }
  }

      


It just gets weirder !!

I managed to fix the problem of malformed string, iterating through each char and removing bad ones. However, I now have an even crazier problem. The local variable that I assign in the event handler retains its value between calls.

Here's the relevant code:

     string Path = ""; // <-- #1
     Path = "C:\\Documents and Settings\\jluce\\My Documents\\Downloads"; // <-- #2

      TextRenderer.MeasureText(Path, Row.Cells[1].Style.Font,
        new Size((mappingsDataGrid.Columns[e.Column.Index].Width), Row.Height),
          TextFormatFlags.ModifyString | TextFormatFlags.PathEllipsis);

      // Left out code that strips invalid chars

      Row.Cells[1].Value = Path; // <-- #3
      Path = null;

      

In the first resize column (see above in comments):

  • After this line, the Path contains "".
  • After this line, Path contains the line as displayed above.
  • The path contains the truncated file path as it should (ie "C: \ Documents and Setti ... \ Downloads")

Second time change:

  • After this line, Path contains "", as it should.
  • After this line, "Path" contains "C: \ Documents and Set ... \ Downloads \ 0 Documents \ Downloads", which was an invalid value from the previous iteration before I removed the invalid characters (see here for "\ 0")! !
  • Now the way is FUBAR, because I started with a broken string and it just keeps getting worse.

Why should Path have to be assigned an invalid value from a previous function call (after correctly assigning an empty string!) When I explicitly assign a value to it? !!!!!

+2


source to share


2 answers


TextRenderer.MeasureText

the method is disgusting - it changes the actual row passed as a parameter, so it changes the actual row that the DataGridView is referring to. This actually makes the .Net string mutable .

It also seems that this ridiculous method does not change the actual string of the Length

string, but simply overwrites one of the characters \0

to indicate the end of the string (like a null terminated string in plain C). Something funny!

This can seriously affect the stability of your application. If you take into account that .Net uses string interning , you can start getting all sorts of weird results since you noticed that your string constants no longer seem to be constant.

The first step is to create a copy of your string (a new instance with the same characters):

string Path = String.Copy(e.Value as string ?? "");

      

instead



string Path = (string)Row.Cells[1].Value;

      

This ensures that no matter what it does TextRenderer

, the original string remains the same.

After that, you need to get rid of the null character in the modified string.

By doing this:

if (Path.IndexOf('\0') >= 0)
   e.Value = Path.Substring(0, Path.IndexOf('\0'));
else
   e.Value = Path;

      

you will create a new instance of a clean modified string (leaving a temporary copy Path

not tied to garbage collection).

+2


source


You need to use the CellFormatting event to CHANGE the given value before printing it (the object's original value will not be changed). In your case, you can check that the column is correct by checking the e.ColumnIndex variable and changing the e.Value text as I did below:



public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        dataGridView1.DataSource = new List<Person>(new Person[] { new Person() { Name = "André", Adress = "Brazil" } });
    }

    private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
    {
        e.Value = e.Value + " modified";
    }
}

class Person
{
    public String Name { get; set; }
    public String Adress { get; set; }
}

      

+2


source







All Articles