Форматировать ячейку DataGridView без изменения базового значения? - PullRequest
2 голосов
/ 22 октября 2009

У меня есть несвязанный 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 ]

2 голосов
/ 23 октября 2009

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 копию без ссылки для сборки мусора).

2 голосов
/ 23 октября 2009

Вам необходимо использовать событие 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; }
}
...