2009-03-23 14 views
13

Soy bastante nuevo en WPF y trato de entender la mejor manera de extender el control ListBox. Como una experiencia de aprendizaje, quería construir un ListBox cuyo ListBoxItem s muestra un CheckBox en lugar de solo texto. Lo conseguí trabajando de manera básica usando el ListBox.ItemTemplate, estableciendo explícitamente los nombres de las propiedades a las que quería enlazar datos. Un ejemplo vale más que mil palabras, así que ...Cómo hacer un ListBox.ItemTemplate reutilizable/genérico

Tengo un objeto personalizado para el enlace de datos:

public class MyDataItem { 
    public bool Checked { get; set; } 
    public string DisplayName { get; set; } 

    public MyDataItem(bool isChecked, string displayName) { 
     Checked = isChecked; 
     DisplayName = displayName; 
    } 
} 

(. Construyo una lista de las personas y establecer ListBox.ItemsSource a esa lista) Y mi XAML se ve así:

<ListBox Name="listBox1"> 
     <ListBox.ItemTemplate> 
      <DataTemplate> 
       <CheckBox IsChecked="{Binding Path=Checked}" Content="{Binding Path=DisplayName}" /> 
      </DataTemplate> 
     </ListBox.ItemTemplate> 
    </ListBox> 

Esto funciona. Pero quiero que esta plantilla sea reutilizable, es decir, querré vincularme a otros objetos con propiedades distintas de "Comprobado" y "Nombre de visualización". ¿Cómo puedo modificar mi plantilla de manera que pueda convertirla en un recurso, volver a utilizarla en múltiples instancias ListBox y, para cada instancia, vincular IsChecked y Content a nombres de propiedades arbitrarios?

+0

No creo que esto sea posible. Para que el enlace "Controlado" sea flexible, deberá subclasificar el ListBox y agregar un CheckedMemberPath como DisplayMemberPath. No se puede hacer esto solo con una plantilla. –

+0

Eso es lo que temía. Empecé a buscar en subclases de ListBox, pero seguí leyendo cosas sobre cómo son las plantillas increíbles y cómo se deben evitar las subclases si es posible lograr lo que se desea con una plantilla. Supongo que este puede ser un buen caso para una subclase. ¡Gracias! –

Respuesta

14

La forma más sencilla es probablemente para poner el DataTemplate como un recurso algún lugar de su aplicación con un TargetType de MyDataItem como esto

<DataTemplate DataType="{x:Type MyDataItem}"> 
    <CheckBox IsChecked="{Binding Path=Checked}" Content="{Binding Path=DisplayName}" /> 
</DataTemplate> 

Es posible que también tiene que incluir un xmlns a su asamblea local y haz referencia a eso a través de eso. Luego, siempre que use un ListBox (o cualquier otra cosa que use un MyDataItem en un ContentPresenter o ItemsPresenter) usará este DataTemplate para mostrarlo.

+0

¡Fascinante! No me había dado cuenta de que podía aplicar la plantilla al objeto de datos en sí. (Por cierto, encontré que "TargetType" en realidad debería ser "DataType" en una DataTemplate.) Sin embargo, el inconveniente de esto es que tendría que definir plantillas para cada tipo de fuente de datos ... que estaba tratando de evitar. –

+0

Gracias, he solucionado el error. No importa lo que tenga que decirle a la interfaz de usuario qué campo está verificado y cuál es el contenido. Solo tienes que decidir dónde sucede eso. Tiendo a preferirlo en el nivel de DataTemplate porque ha sido el lugar más fácil de mantener en mi experiencia. –

16

Cree su DataTemplate como recurso y luego hágalo referencia utilizando la propiedad ItemTemplate del ListBox. MSDN has a good example

<Windows.Resources> 
    <DataTemplate x:Key="yourTemplate"> 
    <CheckBox IsChecked="{Binding Path=Checked}" Content="{Binding Path=DisplayName}" /> 
    </DataTemplate> 
... 
</Windows.Resources> 

... 
<ListBox Name="listBox1" 
     ItemTemplate="{StaticResource yourTemplate}"/> 
+0

Creo que el autor de la pregunta pregunta cómo hacer los parámetros 'Comprobado' y 'Nombre de visualización' que se suministran a la plantilla, de modo que la misma apariencia se pueda usar en cualquier otro lugar, pero posiblemente con un enlace diferente. –

+0

Hmm, sí veo que tienes razón, no estoy seguro de que eso sea posible, esperaré una mejor respuesta – MrTelly

+0

Jonathan tiene razón; Me gustaría poder vincular otros tipos de objetos a la misma plantilla. ¡Gracias por intentarlo! –

2

Si querías una pantalla manera entonces usted podría utilizar un convertidor:

class ListConverter : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     return ((IList<MyDataItem>)value).Select(i => new { Checked = i.Checked2, DisplayName = i.DisplayName2 }); 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
    { 
     throw new NotImplementedException(); 
    } 
} 

Entonces el xaml sería algo como esto:

<Window.Resources> 
    <this:ListConverter x:Key="ListConverter" /> 
</Window.Resources> 
<ListBox ItemsSource="{Binding Path=Items, Converter={StaticResource ListConverter}}"> 
    <ListBox.ItemTemplate> 
     <DataTemplate> 
      <CheckBox IsChecked="{Binding Path=Checked, Mode=OneWay}" Content="{Binding Path=DisplayName, Mode=OneWay}" /> 
     </DataTemplate> 
    </ListBox.ItemTemplate> 
</ListBox> 

Esa plantilla de datos que podría hacer genérico como arriba. La unión bidireccional sería un poco más difícil.

Creo que es mejor que sus clases base implementen una interfaz ICheckedItem que expone las propiedades genéricas a las que desea que se vinculen las plantillas de datos?

+0

Enfoque interesante, ¡gracias! Necesitaré un enlace bidireccional, así que probablemente evite la ruta del convertidor. Sin embargo, ICheckedItem abre nuevas posibilidades en las que no había pensado. Las interfaces pueden terminar siendo el camino a seguir. –