El enlace no funciona cuando se utiliza un control personalizado en una columna de plantilla de cuadrícula de datos

programación


El enlace no funciona cuando se utiliza el control personalizado en un DataGridTemplateColumn

Mi cargo es proporcionar un control que se pueda utilizar para seleccionar varios elementos de una lista de elementos. Los elementos son etiquetas de clasificación. El objeto es una etiqueta. Tiene tres propiedades (ID, Nombre, Descripción). Aquí está el objeto:

public class Tag : ObservableObject
{

  private int tagID;
  private string tagName;
  private string description;

  #region Properties
  public int TagID
  {
    get => tagID;
		set => SetProperty(ref tagID, value);
  }
  public string Name
  {
    get => tagName; 
	set => SetProperty(ref tagName, value);
  }
  public string Description
  {
    get => description; 
    set => SetProperty(ref description, value);
  }

  public Tag()
  { }
}

He creado un CustomControl llamado TagSelect que toma una Lista en una Propiedad de Dependencia llamada SelectedTags. Estas son las etiquetas que deberían mostrarse como seleccionadas inicialmente. Luego crea una colección observable de todas las etiquetas posibles para que un usuario pueda seleccionar/deseleccionar las etiquetas relevantes. También gestiona la precisión de la lista de etiquetas seleccionadas original.

El problema con el que necesito ayuda es este. Cuando pruebo este control por sí solo (como un elemento en un Grid), funciona sin problemas. Sin embargo, el objetivo original era utilizarlo en DataGridColumn. Cuando construí un DataGridTemplateColumn y utilicé el control TagSelect en CellEditingTemplate, el DP para SelectedTags es nulo. Sé que es nulo porque muestra (== nulo) dentro del control TagSelect en un punto de interrupción en el método OnApplyTemplate.

He creado un pequeño proyecto para impulsar las pruebas de este control (TagBindExample). Se puede acceder a él en
https://github.com/hardoverton/TagBindExample
Algunas piezas relevantes de la Solución:

Así es como se usa el control cuando está solo en una cuadrícula:

<cc:TagSelect Width="175"
					SelectedTags="{Binding SomeTags}" />
<cc:TagSelect Width="175"
			  SelectedTags="{Binding OtherTags}" />

Así es como se define DataGrid:

<DataGrid x:Name="TestGrid"
				Grid.Row="3"
				AutoGenerateColumns="False"
				ItemsSource="{Binding DataEntries}"
				Margin="80,0,80,0"
				Grid.ColumnSpan="2">
  <DataGrid.Columns>
    <DataGridTextColumn Width="40"
						Binding="{Binding ID}"
						Header="ID" />
    <DataGridTextColumn Width="200"
						Binding="{Binding Name}"
						Header="Name" />
    <DataGridTemplateColumn Width="175"
							Header="Tags">
      <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
		  <TextBlock Text="{Binding TagString}" 
					Width="175"/>
		</DataTemplate>
	  </DataGridTemplateColumn.CellTemplate>
	  <DataGridTemplateColumn.CellEditingTemplate>
	    <DataTemplate>
		  <cc:TagSelect Width="175"
						SelectedTags="{Binding Tags}" />
		</DataTemplate>
	  </DataGridTemplateColumn.CellEditingTemplate>
	</DataGridTemplateColumn>
  </DataGrid.Columns>
</DataGrid>

Los datos de prueba para DataGrid son ObservableCollection DataEntries. Aquí están los datos de la prueba:

SomeTags List:
  3 - Boogie
  1 - Accord

OtherTags List:
  5 - Christmas Gifts
  6 - Data Science
  7 - Envoy

DataEntries Collection:
  0 - First Person, Tags:
        7 - Envoy
        2 - Bathroom Project
  1 - Second Person, Tags:
        5 - Christmas Gifts
        1 - Accord
        3 - Boogie
        8 - Fund-House
  2 - Third Person, Tags:
        5 - Christmas Gifts
        6 - Data Science
        7 - Envoy

Aquí está la definición de entrada de datos:

public class DataEntry : ObservableObject
{
	private int id;
	public int ID
    {
      get => id;
	  set => SetProperty(ref id, value);
    }

    private string? name;
    public string? Name
    {
	  get => name;
	  set => SetProperty(ref name, value);
    }

    private List<Tag> tags;
    public List<Tag> Tags
    {
			get => tags;
			set => SetProperty(ref tags, value);
    }

  private string tagString;
  public string TagString
  {
    get => tagString;
	set => SetProperty(ref tagString, value);	
  }

public DataEntry()
{
  tags = new List<Tag>();
}

Aquí está la definición de la propiedad de dependencia de SelectedTags en el control TagSelect:

C#
public List<Tag> SelectedTags
	{
		get { return (List<Tag>)GetValue(SelectedTagsProperty); }
		set { SetValue(SelectedTagsProperty, value); }
	}
	public static readonly DependencyProperty SelectedTagsProperty =
			DependencyProperty.Register("SelectedTags", typeof(List<Tag>), typeof(TagSelect),
					new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

Solo cuando se usa en DataGridTemplateColumn, el DP es nulo. No entiendo porque. Por alguna razón, no ve el DataContext de DataGridRow. Los datos de la celda en el registro son una Lista tal como cuando se usa de forma independiente. Sé que es nulo para cada uno de los tres registros de prueba porque lo veo en la siguiente ubicación para cada instancia del control. No se muestran fallas vinculantes.:

C#
public override void OnApplyTemplate()
{
  base.OnApplyTemplate();

  if (SelectedTags == null) SelectedTags = new List<Tag>();  <- break here
....
....

  FullTagSet = TagData.TagList;
  LoadSelections();
}

Lo que he probado:

Probé todas las sugerencias razonables que encontré al investigar este problema a través de StackOverFlow, CodeProject, GitHub y cualquier otra referencia de los motores de búsqueda.

Agregué el enlace TagString para usarlo en CellTemplate solo para ver si podía hacer referencia a DataGridRow correctamente. Funciona como se esperaba.

¿Qué estoy haciendo mal en CellEditingTemplate que hace que no pueda
acceder a las etiquetas List en cada registro?

¿Estoy intentando hacer algo que DataGridTemplateColumn no permite?

Si es así, ¿existe otro enfoque para el requisito que debería considerar?

コメント

タイトルとURLをコピーしました