【问题标题】:Format DataGridView Cell without changing underlying value?在不更改基础值的情况下格式化 DataGridView 单元格?
【发布时间】:2009-10-22 18:11:38
【问题描述】:

我有一个未绑定的 DataGridView(在 VS 2008 中),其中一列包含文件路径。我想在 ColumnWidthChanged 事件上使用 TextRenderer 类格式化字符串,而无需实际修改基础值。问题是表格的内容是在表格关闭时保存的,我不想保存格式化的值。我想我太深了,看不到明显的解决方案,所以我要靠你们指出来:-)。

这个想法是显示这个:

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\My Documents\Downloads”,它最终会变成 C:\Documents and Settings\jluce\M...\Downloads\0wnloads"。如果我不对其进行硬编码(如下所示),则每次调整列大小时都会进一步损坏。

以下是几次调整大小后的样子: Screenshot

这是我目前正在做的事情。

  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;

第一次调整列大小(参考上面 cmets 中的#):

  1. 此行之后路径包含“”。
  2. 在此行之后,路径包含字符串,如上所示。
  3. 路径包含应截断的文件路径(即“C:\Documents and Setti...\Downloads”)

第二次调整大小:

  1. 在此行之后路径包含“”,因为它应该。
  2. 在此行之后路径包含“C:\Documents and Set...\Downloads\0 Documents\Downloads”,这是在我去除无效字符之前的上一次迭代中的无效值(此处显示为 '\0 ')!!
  3. 现在的路径是 FUBAR,因为我一开始是一根搞砸的绳子,而且情况越来越糟。

当我明确地为它分配一个值时,为什么会为 Path 分配上一个函数调用中的无效值(在正确分配一个空字符串之后!)?!!!!!!

【问题讨论】:

  • 有关该问题的更多具体细节,请参阅下面的“答案”...

标签: .net datagridview formatting ellipsis textrenderer


【解决方案1】:

TextRenderer.MeasureText 方法是nasty one——它改变了作为参数传递的实际字符串,因此它改变了DataGridView 引用的实际字符串。它实际上使 .Net 字符串可变

似乎这种荒谬的方法并没有改变字符串的实际Length,而只是用\0覆盖其中一个字符以指示字符串的结尾(就像纯C中的以null结尾的字符串一样)。这是一些有趣的东西!

这会对您应用的稳定性产生严重影响。如果您考虑到 .Net 使用 string interning,您可能会开始得到各种奇怪的结果,因为您确实注意到您的字符串常量似乎不再是恒定的。

第一步是创建字符串的副本(具有相同字符的新实例):

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 副本未被引用以进行垃圾回收)。

【讨论】:

  • 可以,但仍然存在 MeasureText 损坏数据的问题。即使我将您所指的行替换为硬编码字符串,也会发生这种情况。
  • 谢谢,这似乎有效!我同意,这种方法很奇怪。很少的文档和非常不可预测的结果。
【解决方案2】:

您需要在打印之前使用 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; }
}

【讨论】:

  • 我感觉这很简单。谢谢!!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-20
  • 1970-01-01
  • 2015-01-06
  • 2012-07-10
相关资源
最近更新 更多