2009-03-23 25 views
13

Sono abbastanza nuovo per WPF e sto cercando di capire come estendere il controllo ListBox. Come esperienza di apprendimento, ho voluto creare un ListBox il cui ListBoxItem s visualizza uno CheckBox invece del solo testo. Ho capito che funziona in maniera elementare usando lo ListBox.ItemTemplate, impostando esplicitamente i nomi delle proprietà che volevo fare con il database. Un esempio vale più di mille parole, quindi ...Come rendere ListBox.ItemTemplate riutilizzabile/generico

Ho un oggetto personalizzato per l'associazione dati:

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

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

(. Costruisco un elenco di quelli e impostare ListBox.ItemsSource a quella lista) e il mio XAML assomiglia a questo:

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

Questo funziona. Ma voglio rendere questo template riutilizzabile, cioè voglio legare ad altri oggetti con proprietà diverse da "Checked" e "DisplayName". Come posso modificare il mio modello in modo tale da renderlo una risorsa, riutilizzarlo su più istanze ListBox e, per ogni istanza, associare IsChecked e Content a nomi di proprietà arbitrari?

+0

Non credo sia possibile. Affinché l'associazione "verificata" sia flessibile, dovrai creare una sottoclasse di ListBox e aggiungere CheckedMemberPath come DisplayMemberPath. Non posso farlo solo con un modello. –

+0

Ecco di cosa avevo paura. Avevo iniziato a esaminare la ListBox delle sottoclassi, ma continuavo a leggere cose su come sono incredibili i modelli e su come dovresti evitare le sottoclassi se è possibile realizzare ciò che vuoi con un modello. Immagino che questo potrebbe essere un buon caso per una sottoclasse. Grazie! –

risposta

14

Il modo più semplice è probabilmente quello di mettere il DataTemplate come risorsa da qualche parte nella vostra applicazione con un TargetType di MyDataItem come questo

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

Probabilmente dovrete anche includere un xmlns alla vostra assemblea locale e riferirlo attraverso quello. Quindi, ogni volta che si utilizza un ListBox (o qualsiasi altra cosa che utilizza uno MyDataItem in un ContentPresenter o ItemsPresenter) verrà utilizzato questo DataTemplate per visualizzarlo.

+0

Affascinante! Non mi ero reso conto che potevi applicare il modello all'oggetto dati stesso. (A proposito, ho trovato che "TargetType" dovrebbe in realtà essere "DataType" su un DataTemplate.) Lo svantaggio di questo, però, è che dovrei definire modelli per ogni tipo di origine dati ... che stavo cercando di evitare. –

+0

Grazie, ho corretto l'errore. Non importa cosa dovrai dire all'interfaccia utente quale campo è IsChecked e quale è il contenuto. Devi solo decidere dove ciò accade. Io tendo a preferirlo sul livello DataTemplate perché è stato il posto più facile da mantenere nella mia esperienza. –

16

Creare il DataTemplate come risorsa e quindi fare riferimento a esso utilizzando la proprietà ItemTemplate di 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

Penso che l'interrogante stia chiedendo come impostare i parametri "Controllato" e "DisplayName" che vengono forniti al modello, in modo che lo stesso aspetto possa essere usato altrove, ma possibilmente con un'associazione diversa. –

+0

Hmm, sì capisco che hai ragione, non sono sicuro che sia possibile, aspetterò una risposta migliore – MrTelly

+0

Jonathan ha ragione; Mi piacerebbe essere in grado di associare altri tipi di oggetti allo stesso modello. Grazie per il tentativo, però! –

2

Se si voleva un display modo allora si potrebbe utilizzare un convertitore:

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

Poi il XAML sarebbe simile a questa:

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

Questo modello di dati che si potrebbe fare generico come sopra. Il binding in due modi sarebbe un po 'più difficile.

Penso che sia meglio fare in modo che le classi base implementino un'interfaccia ICheckedItem che espone le proprietà generiche alle quali si desidera associare i datatemplates?

+0

Approccio interessante - grazie! Avrò bisogno di un binding a due vie, quindi probabilmente eviterò il percorso del convertitore. Tuttavia, ICheckedItem apre nuove possibilità a cui non avevo pensato. Le interfacce potrebbero essere la strada da percorrere. –