SilverLight – ListBox mit multiplen ListBoxItem Templates
Manchmal macht es Sinn die ListBoxItems in der ListBox unterschiedlich darzustellen. Um dies zu machen müsste man das Template beim Erstellen eines ListBoxItems zuweisen. Das ganze kann man leicht mit einem einfachen ValueConverter erreichen.
Angenommen wir haben in unseren Model eine Eigenschaft anhand deren wir das Template wählen möchten. Falls man Objekte mit unterschiedlichen Typen bindet und anhand des Typen das Template wählen möchte dann bindet man das ganze Objekt an die Style Eigenschaft des ListBoxItems und passt den Converter dem entsprechend an.
public enum ItemType { Default, Editable } public class ItemViewModel : INotifyPropertyChanged { private ItemType _type; public ItemType ItemType { get { return _type; } set { if (value != _type) { _type = value; NotifyPropertyChanged("ItemType"); } } } ...
Damit man aus dem ValueConverter der kein FrameworkElement ist trozdem Ressourcen findet brauchen wir eine Helper Klasse die nach Ressourcen sucht. Man könnte es auch weglassen aber ich finde die Klasse ziemlich hilfreich.
public static class ResourceHelper { public static object FindResource(string name) { if (App.Current.Resources.Contains(name)) return App.Current.Resources[name]; else { FrameworkElement root = App.Current.RootVisual as FrameworkElement; return root.FindResource(name); } } internal static object FindResource(this FrameworkElement root, string name) { if (root != null && root.Resources.Contains(name)) return root.Resources[name]; else { try { return root.FindName(name); } catch { } } return null; } }
Der ValueConverter konvertiert den gebundenen Wert in die Ressource. Die ConvertBack Methode braucht man in diesem Fall nicht.
public class ListBoxItemTemplateConverter : IValueConverter { public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture) { return ResourceHelper.FindResource(string.Format("ListBoxItem{0}Style", value.ToString())); } public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new System.NotImplementedException(); } }
Jetzt muss man die eigentlichen Ressourcen definieren, es sind Styles mit dem TargetType ListBoxItem
<local:ListBoxItemTemplateConverter x:Key="ListBoxItemTemplateConverter"/> <Style x:Key="ListBoxItemDefaultStyle" TargetType="ListBoxItem"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ListBoxItem"> <StackPanel Margin="0,0,0,17" Width="432" > <TextBlock Text="{Binding LineOne}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/> <TextBlock Text="{Binding LineTwo}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/> </StackPanel> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style x:Key="ListBoxItemEditableStyle" TargetType="ListBoxItem"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ListBoxItem"> <StackPanel Margin="0,0,0,17" Width="432" > <TextBox Text="{Binding LineOne, Mode=TwoWay}" /> <TextBlock Text="{Binding LineTwo}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/> </StackPanel> </ControlTemplate> </Setter.Value> </Setter> </Style>
Und jetzt kommt der eigentliche Trick, man erstellt ein DataTemplate in der ein ListBoxItem ist dessen Style Eigenschaft über den ValueConverter an die Eigenschaft des Models gebunden ist. Der ValueConverter gibt dann einfach die passende Ressource zurück. Dieser Trick klappt ohne Probleme in Blend.
<ListBox x:Name="MainListBox" Margin="0,0,-12,0" ItemsSource="{Binding Items}" SelectionChanged="MainListBox_SelectionChanged"> <ListBox.ItemTemplate> <DataTemplate> <!-- is blendable if you use sample data --> <ListBoxItem Style="{Binding Path=ItemType, Converter={StaticResource ListBoxItemTemplateConverter}}" /> <!-- Bind a item --> <!--<ListBoxItem Style="{Binding Converter={StaticResource ListBoxItemTemplateConverter}}" /> --> <!-- Works only after compiling --> <!--<ListBoxItem Style="{Binding Path=ListBoxItemStyle}" /> --> </DataTemplate> </ListBox.ItemTemplate> </ListBox>





