plantilla de datos dinámica con convertidor de valor
-
19-08-2019 - |
Pregunta
Quiero mostrar datos en una cuadrícula de datos wpftoolkit donde los datos son una colección de
public class Thing
{
public string Foo { get; set; }
public string Bar { get; set; }
public List<Candidate> Candidates { get; set; }
}
public class Candidate
{
public string Name { get; set; }
public CandidateType CandidateType { get; set; }
}
public enum CandidateType
{
Type1,
Type2,
Type42
}
donde el número de candidatos en la lista de candidatos es configurable en tiempo de ejecución.
El diseño de cuadrícula deseado se ve así
Por lo tanto, parece que no puedo crear un DataTemplate para los candidatos en xaml ya que la expresión de enlace cambiará.
Agrego columnas necesarias en el evento AutoGeneratedColumns de esta manera:
private void DataGrid_AutoGeneratedColumns(object sender, EventArgs e)
{
ViewModel vm = DataContext as ViewModel;
for (int i = 0; i < vm.LotsOfThings.First().Candidates.Count; i++)
{
string assName = Assembly.GetExecutingAssembly().GetName().Name;
ParserContext ctx = new ParserContext();
ctx.XamlTypeMapper = new XamlTypeMapper(new string[] { assName });
ctx.XamlTypeMapper.AddMappingProcessingInstruction("src", "WpfToolkitDataGridTester", assName);
ctx.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
ctx.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml");
ctx.XmlnsDictionary.Add("src", "clr-namespace:WpfToolkitDataGridTester;assembly=" + assName);
var template = XamlReader.Parse(@"<DataTemplate>
<DataTemplate.Resources>
<src:FooConverter x:Key='fooConverter' />
</DataTemplate.Resources>
<TextBlock
Foreground='{Binding Candidates[" + i + @"].CandidateType,Converter={StaticResource fooConverter}}'
Text='{Binding Candidates[" + i + @"].Name}' />
</DataTemplate>", ctx) as DataTemplate;
dg.Columns.Add(new DataGridTemplateColumn
{
Header = "Candidate " + (i + 1),
CellTemplate = template
});
}
}
Sin embargo, esto falla con la siguiente excepción: La etiqueta 'FooConverter' no existe en el espacio de nombres XML 'clr-namespace: WpfToolkitDataGridTester; assembly = WpfToolkitDataGridTester'. Línea '3' Posición '54'.
Cambiar el StaticResource a DynamicResource no hace ningún cambio.
¿Qué me estoy perdiendo?
FWIW: una plantilla de datos codificada
<DataTemplate x:Key="candidateTemplate">
<DataTemplate.Resources>
<src:FooConverter x:Key="fooConverter" />
</DataTemplate.Resources>
<TextBlock
Foreground="{Binding Candidates[0].CandidateType,Converter={StaticResource fooConverter}}"
Text="{Binding Candidates[0].Name}" />
</DataTemplate>
y la columna de plantilla definida así
<wpftk:DataGridTemplateColumn CellTemplate="{StaticResource candidateTemplate}" />
'funciona' pero obviamente no produce el resultado deseado ya que Candidates [0] está codificado.
Solución
Por alguna razón, funciona como se espera si me gusta esto ...
string assName = Assembly.GetExecutingAssembly().GetName().Name;
StringBuilder sb = new StringBuilder();
sb.Append("<DataTemplate ");
sb.Append("xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' ");
sb.Append("xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' ");
sb.Append("xmlns:src='clr-namespace:WpfToolkitDataGridTester;assembly=" + assName + "' >");
sb.Append("<DataTemplate.Resources>");
sb.Append("<src:FooConverter x:Key='fooConverter' />");
sb.Append("</DataTemplate.Resources>");
sb.Append("<TextBlock ");
sb.Append("Foreground='{Binding Candidates[" + i + "].CandidateType,Converter={StaticResource fooConverter}}' ");
sb.Append("Text='{Binding Candidates[" + i + @"].Name}' />");
sb.Append("</DataTemplate>");
var template = (DataTemplate)XamlReader.Parse(sb.ToString());
Otros consejos
Cuando los archivos XAML se compilan en BAML, hace referencia al ensamblaje no al origen en memoria. Dado que BAML se compila en el mismo ensamblaje, el tipo real aún no está disponible.
He descubierto que una solución temporal a corto plazo es comentar el estilo temporalmente, construir el proyecto y luego restaurar el estilo.
Sin embargo, la solución más permanente es mover el convertidor a otro ensamblaje.
¿Sería útil declarar el FooConverter
una vez a un nivel superior (tal vez como un recurso de DataGrid
) en lugar de en cada DataTemplate
?