Pregunta

Tengo un DataGridView independiente (en VS 2008), de los cuales una columna contiene una ruta de archivo. Me gustaría formatear la cadena usando la clase TextRenderer en el evento ColumnWidthChanged sin modificar realmente el valor subyacente. El problema es que el contenido de la tabla se guarda cuando se cierra el formulario y no quiero guardar el valor formateado. Creo que estoy demasiado profundo para ver la solución obvia, así que dependo de ustedes para señalarlo :-).

La idea es mostrar esto:

C: \ Archivos de programa \ Microsoft Visual Studio 8 \ SDK \ v2.0 \ Bin \ gacutil.exe

... así (según el ancho de la columna):

C: \ Archivos de programa \ Microso… \ gacutil.exe


Parece que hablé demasiado pronto. Estoy obteniendo algunos resultados muy extraños de TextRenderer.MeasureText (). Si codifico el valor de la ruta como " C: \ Documents and Settings \ jluce \ My Documents \ Downloads " viene termina como C: \ Documents and Settings \ jluce \ M ... \ Downloads \ 0wnloads " ;. Si no lo codifico (como a continuación) se corrompe más cada vez que redimensiono la columna.

Esto es lo que parece después de que una pareja cambia de tamaño: Captura de pantalla

Esto es lo que estoy haciendo actualmente.

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

¡Esto sigue siendo cada vez más raro!

Me las arreglé para solucionar el problema de la cadena mutilada iterando a través de cada char y eliminando las malas. Sin embargo, ahora tengo un problema aún más loco. Una variable local que estoy asignando en el controlador de eventos está reteniendo su valor entre llamadas.

Aquí está el código relevante:

     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;

Columna de cambio de tamaño por primera vez (consulte # 's en los comentarios anteriores):

  1. Después de esta línea, la ruta contiene " " ;.
  2. Después de esta línea, la ruta contiene una cadena tal como aparece arriba.
  3. La ruta contiene la ruta del archivo truncada como debería (es decir, " C: \ Documents and Setti ... \ Downloads ")

Cambio de tamaño por segunda vez:

  1. Después de esta línea, la ruta contiene " " ;, como debería.
  2. Después de esta línea, la ruta contiene " C: \ Documents and Set ... \ Downloads \ 0 Documents \ Downloads " ;, que era el valor no válido de la iteración anterior antes de eliminar los caracteres no válidos (que se ve aquí como '\ 0 ') !!
  3. Ahora el camino es FUBAR porque comencé con una cuerda descompuesta y sigue empeorando.

¿Por qué se asignaría a Path el valor no válido de la llamada a la función anterior (después de asignar correctamente una cadena vacía) cuando le estoy asignando un valor explícitamente? !!!!!

¿Fue útil?

Solución

TextRenderer.MeasureText es un método desagradable : cambia la cadena real pasada como parámetro, por lo que está cambiando la cadena real a la que hace referencia DataGridView. En realidad, hace que una cadena .Net sea mutable .

También parece que este método ridículo no cambia el Length real de la cadena, sino que simplemente sobrescribe uno de los caracteres con \ 0 para indicar el final de la cadena (como cadenas terminadas en nulo en C simple). ¡Eso es algo divertido!

Esto puede tener un impacto grave en la estabilidad de su aplicación. Si tiene en cuenta que .Net utiliza interning de cadena , puedes comenzar a obtener todo tipo de resultados extraños, ya que notaste que tus constantes de cadena ya no parecen constantes.

El primer paso es crear una copia de tu cadena (una nueva instancia con los mismos caracteres):

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

en lugar de

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

Esto asegurará que no importa lo que haga TextRenderer , la cadena original permanecerá sin cambios.

Después de eso, debes deshacerte de los caracteres nulos en la cadena modificada.

Al hacer esto:

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

creará una nueva instancia de una cadena limpia y modificada (dejando nuestra copia temporal Path sin referencia para la recolección de basura).

Otros consejos

Debe usar el evento CellFormatting para CAMBIAR el valor dado antes de imprimirlo (el valor del objeto original no se modificará). En su caso, puede verificar si es la columna correcta verificando la variable e.ColumnIndex y cambiar el texto de e.Value como lo hice a continuación:

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; }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top