2009-03-23 7 views
13

Ich bin ziemlich neu in WPF und versuche zu verstehen, wie man die ListBox Kontrolle am besten erweitert. Als Lernerfahrung wollte ich eine ListBox bauen, deren ListBoxItem s eine CheckBox anstelle von nur Text anzeigen. Ich habe das auf eine einfache Art und Weise mit der ListBox.ItemTemplate funktioniert, die Namen der Eigenschaften, die ich zu Databind explizit festlegen wollte. Ein Beispiel dafür ist mehr als tausend Worte, so ...Wie man eine ListBox.ItemTemplate wiederverwendbar/generisch machen

ich ein benutzerdefiniertes Objekt für Datenbindung haben:

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

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

(. Ich baue eine Liste dieser und setzen ListBox.ItemsSource dieser Liste) Und meine XAML sieht so aus:

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

Das funktioniert. Aber ich möchte diese Vorlage wiederverwendbar machen, d. H. Ich möchte an andere Objekte mit anderen Eigenschaften als "Checked" und "DisplayName" binden. Wie kann ich meine Vorlage so ändern, dass ich sie zu einer Ressource machen, sie auf mehreren Instanzen ListBox wiederverwenden und für jede Instanz IsChecked und Content an beliebige Eigenschaftsnamen binden kann?

+0

Ich glaube nicht, dass das möglich ist. Um die "Checked" -Bindung flexibel zu gestalten, müssen Sie die ListBox ableiten und einen CheckedMemberPath wie den DisplayMemberPath hinzufügen. Kann das nicht mit nur einer Vorlage tun. –

+0

Das war, wovor ich Angst hatte. Ich habe angefangen ListBox zu untersuchen, aber ich las weiter darüber, wie unglaublich Templates sind und wie man Unterklassen vermeiden sollte, wenn es überhaupt möglich ist, mit einer Vorlage das zu erreichen, was man will. Ich denke, das ist ein guter Fall für eine Unterklasse. Vielen Dank! –

Antwort

14

Der einfachste Weg ist wahrscheinlich in Ihrer Anwendung irgendwo die DataTemplate als Ressource setzen mit einem TargetType von MyDataItem wie diese

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

Sie werden wahrscheinlich auch eine xmlns auf Ihre lokale Montage umfassen und referenziere es dadurch. Dann, wenn Sie eine ListBox (oder irgendetwas anderes, das eine MyDataItem in einer ContentPresenter oder ItemsPresenter verwendet) verwenden, wird es diese DataTemplate verwenden, um es anzuzeigen.

+0

Faszinierend! Ich hatte nicht erkannt, dass Sie die Vorlage auf das Datenobjekt selbst anwenden können. (BTW, ich fand, dass "TargetType" tatsächlich "DataType" auf einem DataTemplate sein sollte.) Der Nachteil ist jedoch, dass ich Vorlagen für jede Art von Datenquelle definieren müsste ... die ich vermeiden wollte. –

+0

Danke, ich habe den Fehler behoben. Egal, was Sie der Benutzeroberfläche mitteilen müssen, welches Feld ist IsChecked und welches ist der Inhalt. Sie müssen nur entscheiden, wo das passiert. Ich bevorzuge es auf der DataTemplate-Ebene, da es meiner Erfahrung nach am einfachsten zu pflegen ist. –

16

Erstellen Sie Ihr DataTemplate als Ressource und verweisen Sie dann mit der ItemTemplate-Eigenschaft der ListBox darauf. 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

Ich denke, der Fragesteller fragt, wie man die Parameter 'Checked' und 'DisplayName' an die Vorlage liefert, so dass die gleiche Darstellung an anderer Stelle verwendet werden kann, aber möglicherweise mit einer anderen Bindung. –

+0

Hmm, yep Ich sehe, du hast Recht, ich bin mir nicht sicher, ob das möglich ist, ich werde eine bessere Antwort erwarten – MrTelly

+0

Jonathan hat Recht; Ich möchte in der Lage sein, andere Arten von Objekten an dieselbe Vorlage zu binden. Danke für den Versuch, obwohl! –

2

Wenn Sie eine Möglichkeit, Anzeige wollte dann könnte man einen Konverter verwenden:

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

Dann wird der XAML etwas würde wie folgt aussehen:

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

Das Datenvorlage könnten Sie machen generisch wie oben. Zwei-Wege-Bindung wäre ein bisschen schwieriger.

Ich denke, Sie sind besser dran, Ihre Basisklassen eine ICheckedItem-Schnittstelle zu implementieren, die die generischen Eigenschaften verfügbar macht, an die die Datatemplates binden sollen?

+0

Interessanter Ansatz - danke! Ich brauche eine beidseitige Bindung, daher werde ich wahrscheinlich die Konverterroute vermeiden. Das ICheckedItem eröffnet jedoch neue Möglichkeiten, an die ich nicht gedacht hatte. Schnittstellen können am Ende der Weg sein. –