2
votes

I have a problem which will require a bit of your time. I need to add listboxes on multiple pages in my Windows phone 8 app, on each page normal/selected background for ListBoxItem is different(10-12 pages). One solution would be that i create different styles for each ListBoxItem for every page hard-coding color-codes in my stylesheet(which I dont want). So I have extended ListBoxItem class and added 2 dependency properties for normal and selected item background for a ListboxItem. so that i may just set these properties on each xaml page and dont have to create different styles for every page. here is my extended control

public class CustomListboxItem : ListBoxItem
{
    public static readonly DependencyProperty NormalBackgroundProperty =
        DependencyProperty.Register("NormalBackground", typeof(SolidColorBrush), typeof(CustomListboxItem), new PropertyMetadata(null));
    public static readonly DependencyProperty SelectedBackgroundProperty =
        DependencyProperty.Register("SelectedBackground", typeof(SolidColorBrush), typeof(CustomListboxItem), new PropertyMetadata(null));
    public SolidColorBrush NormalBackground
    {
        get { return (SolidColorBrush)GetValue(NormalBackgroundProperty); }
        set { base.SetValue(NormalBackgroundProperty, value); }
    }
    public SolidColorBrush SelectedBackground
    {
        get { return (SolidColorBrush)GetValue(SelectedBackgroundProperty); }
        set { base.SetValue(SelectedBackgroundProperty, value); }
    }
}

And here is How a use this (CyclesList is just list of strings):

<ListBox Grid.Row="1" ItemsSource="{Binding CyclesList}" ItemContainerStyle="{StaticResource FilterListBoxItemStyle}" >
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <controls:CustomListboxItem NormalBackground="Transparent" Content="{Binding}" SelectedBackground="Red"/>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>

And here is the style i am applying to make it generic.

<Style x:Key="FilterListBoxItemStyle" TargetType="controls:CustomListboxItem">
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="BorderThickness" Value="0"/>
    <Setter Property="Padding" Value="0"/>
    <Setter Property="HorizontalContentAlignment" Value="Left"/>
    <Setter Property="VerticalContentAlignment" Value="Top"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ListBoxItem">
                <Border x:Name="LayoutRoot" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}">
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Normal"/>
                            <VisualState x:Name="MouseOver"/>
                            <VisualState x:Name="Disabled"/>
                        </VisualStateGroup>
                        <VisualStateGroup x:Name="SelectionStates">
                            <VisualState x:Name="Unselected"/>
                            <VisualState x:Name="Selected">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="ContentContainer">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=SelectedBackground}"/>
                                    </ObjectAnimationUsingKeyFrames>
                                </Storyboard>
                            </VisualState>
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                    <ContentControl x:Name="ContentContainer" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
                                    Background="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=NormalBackground}"/>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

But my Application throws an exception here (full stack trace):

{System.Windows.Markup.XamlParseException: [Line: 0 Position: 0] ---> System.Windows.Markup.XamlParseException: [Line: 0 Position: 0] ---> System.Windows.Markup.XamlParseException: [Line: 0 Position: 0] ---> System.Windows.Markup.XamlParseException: [Line: 0 Position: 0] ---> System.Windows.Markup.XamlParseException: [Line: 0 Position: 0] ---> System.Windows.Markup.XamlParseException: [Line: 0 Position: 0] ---> System.Windows.Markup.XamlParseException: [Line: 0 Position: 0] at MS.Internal.XcpImports.CheckHResult(UInt32 hr) at MS.Internal.XcpImports.SetValue(IManagedPeerBase obj, DependencyProperty property, DependencyObject doh) at System.Windows.DependencyObject.SetValue(DependencyProperty property, DependencyObject doh) at System.Windows.Controls.ListBox.GetContainerForItemOverride() at System.Windows.Controls.ItemsControl.MS.Internal.Controls.IGeneratorHost.GetContainerForItem(Object item, DependencyObject recycledContainer) at System.Windows.Controls.ItemContainerGenerator.Generator.GenerateNext(Boolean stopAtRealized, Boolean& isNewlyRealized) at System.Windows.Controls.ItemContainerGenerator.System.Windows.Controls.Primitives.IItemContainerGenerator.GenerateNext(Boolean& isNewlyRealized) at System.Windows.Controls.VirtualizingStackPanel.MeasureOverride(Size constraint) at System.Windows.FrameworkElement.MeasureOverride(IntPtr nativeTarget, Double inWidth, Double inHeight, Double& outWidth, Double& outHeight) --- End of inner exception stack trace --- at MS.Internal.XcpImports.CheckHResult(UInt32 hr) at MS.Internal.XcpImports.FrameworkElement_MeasureOverride(FrameworkElement element, Size availableSize) at System.Windows.Controls.ScrollContentPresenter.MeasureOverride(Size constraint) at System.Windows.FrameworkElement.MeasureOverride(IntPtr nativeTarget, Double inWidth, Double inHeight, Double& outWidth, Double& outHeight) --- End of inner exception stack trace --- at MS.Internal.XcpImports.CheckHResult(UInt32 hr) at MS.Internal.XcpImports.UIElement_Measure(UIElement element, Size availableSize) at System.Windows.UIElement.Measure(Size availableSize) at System.Windows.Controls.ScrollViewer.MeasureOverride(Size constraint) at System.Windows.FrameworkElement.MeasureOverride(IntPtr nativeTarget, Double inWidth, Double inHeight, Double& outWidth, Double& outHeight) --- End of inner exception stack trace --- at MS.Internal.XcpImports.CheckHResult(UInt32 hr) at MS.Internal.XcpImports.FrameworkElement_MeasureOverride(FrameworkElement element, Size availableSize) at System.Windows.FrameworkElement.MeasureOverride(Size availableSize) at System.Windows.FrameworkElement.MeasureOverride(IntPtr nativeTarget, Double inWidth, Double inHeight, Double& outWidth, Double& outHeight) --- End of inner exception stack trace --- at MS.Internal.XcpImports.CheckHResult(UInt32 hr) at MS.Internal.XcpImports.FrameworkElement_MeasureOverride(FrameworkElement element, Size availableSize) at System.Windows.FrameworkElement.MeasureOverride(Size availableSize) at System.Windows.FrameworkElement.MeasureOverride(IntPtr nativeTarget, Double inWidth, Double inHeight, Double& outWidth, Double& outHeight) --- End of inner exception stack trace --- at MS.Internal.XcpImports.CheckHResult(UInt32 hr) at MS.Internal.XcpImports.FrameworkElement_MeasureOverride(FrameworkElement element, Size availableSize) at Microsoft.Phone.Controls.PhoneApplicationFrame.MeasureOverride(Size availableSize) at System.Windows.FrameworkElement.MeasureOverride(IntPtr nativeTarget, Double inWidth, Double inHeight, Double& outWidth, Double& outHeight) --- End of inner exception stack trace ---}

From Stack-Trace it may appear if i am missing Width/height of the control, but i have tried to set these also, but nothing worked. Is there something i am doing wrong? or can you suggest a better way of making generic CustomListBoxItem?

Thanks in anticipation,

regards,

2

2 Answers

2
votes

The resource dictionary key of your CustomListboxItem Style is FilterListBoxItemStyle:

<Style x:Key="FilterListBoxItemStyle" TargetType="controls:CustomListboxItem">

but you use FilterListBoxStyle to refer to the Style resource:

<ListBox ... ItemContainerStyle="{StaticResource FilterListBoxStyle}">

Change the above declaration to

<ListBox ... ItemContainerStyle="{StaticResource FilterListBoxItemStyle}">

Update: Having said this, there is another thing to mention. In order to have a custom derived ListBoxItem class, you won't simply use that in a DataTemplate in ItemTemplate. Instead you would also create a custom derived ListBox class which overrides the GetContainerForItemOverride method:

public class CustomListBox : ListBox
{
    protected override DependencyObject GetContainerForItemOverride()
    {
        return new CustomListboxItem();
    }
}

The custom ListBox does no longer declare an ItemTemplate:

<controls:CustomListBox ...
    ItemContainerStyle="{StaticResource FilterListBoxItemStyle}" />

You would also set the NormalBackground and SelectedBackground properties in the CustomListboxItem Style.

0
votes

Dispense w/ your derived class. Edit your FilterListBoxItemStyle as follows:

  1. Change TargetType to ListBoxItem

  2. Make SelectedBackground and NormalBackground dynamic resources (e.g.: Background="{DynamicResource NormalBackground}")

  3. Remove Storyboard.TargetName="ContentContainer" from the animation.

    ....

Then on each page (or whatever scope you need) define the dynamic resources as you like:

<ListBox Grid.Row="1" ItemsSource="{Binding CyclesList}" ItemContainerStyle="{StaticResource FilterListBoxItemStyle}" >
    <ListBox.Resources>
        <SolidColorBrush Color="Transparent"
                     x:Key="NormalBackground" />
        <SolidColorBrush Color="Red"
                     x:Key="SelectedBackground" />
    </ListBox.Resources>
</ListBox>