Форматировать ячейку DataGridView без изменения базового значения?

У меня есть несвязанный DataGridView (в VS 2008), один столбец которого содержит путь к файлу. Я хотел бы отформатировать строку, используя класс TextRenderer в событии ColumnWidthChanged без фактического изменения базового значения. Проблема в том, что содержимое таблицы сохраняется, когда форма закрыта, и я не хочу сохранять отформатированное значение. Я думаю, что я слишком глубоко, чтобы увидеть очевидное решение, поэтому я полагаюсь на вас, ребята, чтобы указать на это:-).

Идея состоит в том, чтобы отобразить это:

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

... как это (в зависимости от ширины столбца):

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


Похоже, я говорил слишком рано. Я получаю некоторые очень странные результаты от TextRenderer.MeasureText(). Если я жестко закодировал значение пути как "C:\Documents and Settings\jluce\ Мои документы \ Загрузки", он получится как C:\Documents and Settings\jluce\M...\Downloads\0wnloads". Если я не т жестко закодировать его (как показано ниже), он будет поврежден каждый раз, когда я изменяю размер столбца.

Вот как это выглядит после изменения размеров пары: Скриншот

Вот что я сейчас делаю.

  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;
    }
  }

Это становится все страннее!

Мне удалось решить проблему искаженной строки, просматривая каждый символ и удаляя плохие. Однако теперь у меня есть еще более безумная проблема. Локальная переменная, которую я назначаю в обработчике событий, сохраняет свое значение между вызовами.

Вот соответствующий код:

     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;

Колонка первого изменения размера (см. # В комментариях выше):

  1. После этой строки Path содержит "".
  2. После этой строки Path содержит строку, как показано выше.
  3. Путь содержит усеченный путь к файлу, как и должно быть (например, "C:\Documents and Setti...\Downloads")

Изменение размера во второй раз:

  1. После этой строки Path содержит "", как и должно быть.
  2. После этой строки Path содержит "C:\Documents and Set...\Downloads\0 Documents\Downloads", что было недопустимым значением из предыдущей итерации, перед тем как я удалил недопустимые символы (обозначенные здесь как "\ 0")!!
  3. Теперь путь FUBAR, потому что я начал с испорченной нити, и это только ухудшается.

Почему Path присваивается неверное значение из предыдущего вызова функции (после правильного назначения пустой строки!), Когда я явно присваиваю ему значение?!!!!!

2 ответа

Решение

TextRenderer.MeasureText Метод является неприятным - он изменяет фактическую строку, переданную в качестве параметра, поэтому он изменяет фактическую строку, на которую ссылается DataGridView. Это фактически делает строку.Net изменчивой.

Также кажется, что этот нелепый метод не меняет Length строки, но просто перезаписывает один из символов \0 указывать конец строки (как строки с нулевым символом в конце в простом C). Это забавные вещи!

Это может серьезно повлиять на стабильность вашего приложения. Если принять во внимание, что.Net использует интернирование строк, вы можете начать получать всевозможные странные результаты, так как вы заметили, что ваши строковые константы больше не кажутся постоянными.

Первый шаг - создать копию вашей строки (новый экземпляр с такими же символами):

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

вместо

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

Это гарантирует, что независимо от того, что TextRenderer делает, оригинальная строка останется неизменной.

После этого вам нужно избавиться от нулевого символа в измененной строке.

Делая это:

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

вы создадите новый экземпляр чистой, измененной строки (оставив наш временный Path скопировать ссылку на сборщик мусора).

Вам необходимо использовать событие CellFormatting, чтобы ИЗМЕНИТЬ заданное значение до его печати (исходное значение объекта не будет изменено). В вашем случае вы можете проверить, является ли это правильный столбец, проверив переменную e.ColumnIndex и изменить текст e.Value, как я сделал ниже:

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; }
}
Другие вопросы по тегам