1
votes

I want to to use my custom ListBoxItem in a custom ListBox and want to be able to bind my own properties in the ListBox DataTemplate while binding to an ItemSource.

I found nothing about this. The combinations i found are always in Xaml declared ItemContainerStyle or ItemTemplate Styles but nothing with custom Classes close to my situation. Maybe i do something terrible wrong?

The custom ListBox Class:

public class WPM_ListBox : ListBox
{
    protected override bool IsItemItsOwnContainerOverride(object item)
    {
        return (item is WPM_ListBoxItem);
    }

    protected override DependencyObject GetContainerForItemOverride()
    {
        return new WPM_ListBoxItem();
    }

    public WPM_ListBox()
    {
        this.DefaultStyleKey = typeof(WPM_ListBox);
    }

    static WPM_ListBox()
    {

    }
}

This is the short version of the WPM_ListBoxItem Class:

    public class WPM_ListBoxItem : ListBoxItem
{
    public WPM_ListBoxItem()
    {
        this.DefaultStyleKey = typeof(WPM_ListBoxItem);
    }
    static WPM_ListBoxItem()
    {
        BackgroundProperty.OverrideMetadata(typeof(WPM_ListBoxItem), 
            new FrameworkPropertyMetadata((SolidColorBrush)(new BrushConverter().ConvertFrom("#66767A"))));
    }

    public string WPM_HeaderContent
    {
        get { return (string)GetValue(WPM_HeaderContentProperty); }
        set { SetValue(WPM_HeaderContentProperty, value); }
    }
    public static readonly DependencyProperty WPM_HeaderContentProperty =
        DependencyProperty.Register("WPM_HeaderContent", typeof(string), 
        typeof(WPM_ListBoxItem), new PropertyMetadata("WPM_HeaderContent"));
}

Here is the Generic.xaml:

<Style x:Key="ItemStyle" TargetType="local:WPM_ListBoxItem" >
    <Setter Property="VerticalContentAlignment" Value="Center"/>
    <Setter Property="VerticalAlignment" Value="Center"/>
    <Setter Property="FontFamily" Value="Arial"/>
    <Setter Property="Foreground" Value="#E9EEEE" x:Name="foreground"/>
    <Setter Property="IsHitTestVisible" Value="True"/>
    <Setter Property="Background" Value="Transparent" x:Name="Background_Property"/>
    <Setter Property="BorderBrush" Value="#67767A"/>
    <Setter Property="BorderThickness" Value="0"/>
    <Setter Property="FontWeight" Value="Bold"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:WPM_ListBoxItem" >
                <local:WPM_Border IsHitTestVisible="True" Background="Transparent" Margin="1" VerticalAlignment="Top" x:Name="border" Theme="grau" BorderThickness5="1 1 0 0" BorderThickness4="1 1 0 0" BorderThickness3="1 1 0 0" BorderThickness2="1 1 0 0" BorderThickness1="1 1 0 0" >
                    <local:WPM_Border.Data>
                        <Grid x:Name="grid"  IsHitTestVisible="True" Background="Transparent" Margin="0" >
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition MinWidth="45" Width="Auto"/>
                                <ColumnDefinition />
                            </Grid.ColumnDefinitions>

                            <Grid.RowDefinitions>
                                <RowDefinition x:Name="row1" />
                                <RowDefinition x:Name="row2" />
                            </Grid.RowDefinitions>

                            <TextBlock VerticalAlignment="Top" FontFamily="Arial" FontWeight="Normal" FontSize="{TemplateBinding WPM_HeaderContenFontSize}" Margin="{TemplateBinding WPM_HeaderContentMargin}" Text="{TemplateBinding WPM_HeaderContent}" Foreground="{TemplateBinding WPM_HeaderContentBrush}" Padding="0 2 0 0" x:Name="headercontent" Grid.ColumnSpan="2" ScrollViewer.CanContentScroll="False"/>
                            <!--<ContentPresenter />-->

                            <StackPanel Margin="3 3 0 0" Grid.Row="1" x:Name="stackpanel1" >
                                <TextBlock HorizontalAlignment="Right" VerticalAlignment="Top" FontWeight="Normal" FontSize="{TemplateBinding WPM_LabelDescriptionFontSize}" Margin="{TemplateBinding WPM_LabelDescriptionMargin}" Text="{TemplateBinding WPM_LabelDescription1}" Foreground="{TemplateBinding WPM_LabelDescriptionBrush1}"/>
                                <TextBlock HorizontalAlignment="Right" VerticalAlignment="Top" FontWeight="Normal" FontSize="{TemplateBinding WPM_LabelDescriptionFontSize}" Margin="{TemplateBinding WPM_LabelDescriptionMargin}" Text="{TemplateBinding WPM_LabelDescription2}" Foreground="{TemplateBinding WPM_LabelDescriptionBrush2}"/>
                            </StackPanel>

                            <StackPanel Margin="3 3 0 0" Grid.Row="1" Grid.Column="1" x:Name="stackpanel2" >
                                <TextBlock VerticalAlignment="Top" FontWeight="Normal" FontSize="{TemplateBinding WPM_LabelContentFontSize}" HorizontalAlignment="Left" Margin="{TemplateBinding WPM_LabelContentMargin}" Text="{TemplateBinding WPM_LabelContent1}" Foreground="{TemplateBinding WPM_LabelContentBrush1}"/>
                                <TextBlock VerticalAlignment="Top" FontWeight="Normal" FontSize="{TemplateBinding WPM_LabelContentFontSize}" HorizontalAlignment="Left" Margin="{TemplateBinding WPM_LabelContentMargin}" Text="{TemplateBinding WPM_LabelContent2}" Foreground="{TemplateBinding WPM_LabelContentBrush2}"/>
                            </StackPanel>

                        </Grid>
                    </local:WPM_Border.Data>
                </local:WPM_Border>

                <ControlTemplate.Triggers>
                    <Trigger Property="IsSelected" Value="true">
                        <Setter TargetName="row2" Property="Height" Value="Auto"/>
                        <Setter TargetName="border" Property="Height" Value="Auto"/>
                        <Setter Property="Height" Value="Auto"/>
                        <Setter TargetName="border" Property="BorderThickness1" Value="1 1 0 0"/>
                        <Setter TargetName="border" Property="BorderThickness2" Value="1 1 0 0"/>
                        <Setter TargetName="border" Property="BorderThickness3" Value="1 1 0 0"/>
                        <Setter TargetName="border" Property="BorderThickness4" Value="1 1 0 0"/>
                        <Setter TargetName="border" Property="BorderThickness5" Value="1 1 0 0"/>
                        <Setter TargetName="headercontent" Property="Padding" Value="3 2 0 0"/>
                    </Trigger>
                    <Trigger Property="IsSelected" Value="false">
                        <Setter TargetName="row2" Property="Height" Value="0"/>
                        <Setter TargetName="border" Property="Height" Value="35"/>
                        <Setter Property="Height" Value="35"/>
                        <Setter TargetName="border" Property="BorderThickness1" Value="0"/>
                        <Setter TargetName="border" Property="BorderThickness2" Value="0"/>
                        <Setter TargetName="border" Property="BorderThickness3" Value="0"/>
                        <Setter TargetName="border" Property="BorderThickness4" Value="0"/>
                        <Setter TargetName="border" Property="BorderThickness5" Value="0"/>
                        <Setter TargetName="headercontent" Property="Padding" Value="0 2 0 0"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<Style x:Key="BoxStyle" TargetType="local:WPM_ListBox">
    <Setter Property="Padding" Value="1"/>
    <Setter Property="HorizontalContentAlignment" Value="Left"/>
    <Setter Property="VerticalContentAlignment" Value="Top"/>
    <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
    <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
    <Setter Property="BorderThickness" Value="1"/>
    <Setter Property="ScrollViewer.CanContentScroll" Value="True"/>
    <Setter Property="KeyboardNavigation.TabNavigation" Value="Once"/>
    <Setter Property="ItemsPanel">
        <Setter.Value>
            <ItemsPanelTemplate>
                <VirtualizingStackPanel/>
            </ItemsPanelTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:WPM_ListBox">
                <Grid>
                    <ScrollViewer x:Name="PART_ScrollViewer"
                            IsTabStop="False"
                            Margin="0"
                            >
                        <ItemsPresenter/>
                    </ScrollViewer>
                    <ContentPresenter x:Name="PART_DropVisualPlaceholder" Visibility="Collapsed" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="ItemContainerStyle" Value="{StaticResource ItemStyle}"></Setter>
</Style>

<Style TargetType="local:WPM_ListBoxItem" BasedOn="{StaticResource ItemStyle}"/>
<Style TargetType="local:WPM_ListBox" BasedOn="{StaticResource BoxStyle}"/>

And in Xaml i want to use it like this:

<local:WPM_ListBox ItemsSource="{Binding RoutenListe}" Grid.Row="1" HorizontalContentAlignment="Stretch" HorizontalAlignment="Stretch" BorderThickness="0" Margin="0" Padding="0" Background="#32557B" ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Auto" >
                <local:WPM_ListBox.ItemTemplate  >
                    <DataTemplate >
                        <local:WPM_ListBoxItem WPM_HeaderContent="{Binding Name}" 
                                               WPM_LabelContent1="{Binding Startort}"  WPM_LabelContent2="{Binding Zielort}" 
                                               Background="Transparent" WPM_HeaderContentMargin="6 2 0 0" WPM_HeaderContenFontSize="24" WPM_LabelContentMargin="3 -4 0 3" 
                                               WPM_LabelDescription1="Start:"  WPM_LabelDescription2="Ziel:" WPM_LabelDescriptionMargin="10 -4 0 3" />
                    </DataTemplate>
                </local:WPM_ListBox.ItemTemplate>
            </local:WPM_ListBox>

I know i can replace the declared controls with a ContentPresenter and then simply use a Label or something in the DataTemplate. But thats not what the Dependencies are for?

If i use a standard ListBox and use my custom ListBoxItem in the DataTemplate my Data (Binded Data) is shown but then the "IsSelected" Trigger does not work anymore. If i use my custom ListBox with the custom ListBoxItem declared in the DataTemplate then just only my declared Properties are shown (StandardValues from the declared DependencyProperties) without the Bindings but the IsSelected Property works.

I even tried to set all the Background Properties to transparent that the HitTest doesnt fail.

Do i miss something?

1
What do you mean with "my declared Properties are shown without the Bindings"?almulo
Then my custom control will not use the bindings, instead it uses the standard values from the declared DependencyProperties.Thorsten Houy
Gotcha. Check my answer (option 1, edit 2 lol)almulo

1 Answers

0
votes

You're using ItemTemplate wrong. ItemTemplate is used to provide a template for the CONTENTS of the ListBoxItem, hence it shouldn't CONTAIN a ListBoxItem.

OPTION 1

Remove the ItemTemplate from your view:

<local:WPM_ListBox ItemsSource="{Binding RoutenListe}" 
                   Grid.Row="1" 
                   HorizontalContentAlignment="Stretch" 
                   HorizontalAlignment="Stretch" 
                   BorderThickness="0" Margin="0" Padding="0" 
                   Background="#32557B" 
                   ScrollViewer.CanContentScroll="True" 
                   ScrollViewer.VerticalScrollBarVisibility="Auto" />

Since you've already made it so WPM_ListBox uses WPM_ListBoxItem as its default item container, and you've set the ItemContainerStyle, it'll generate them automatically and the Style will be applied to them automatically.

EDIT 2: The problem is that your WPM_ListBox has a series of properties that nobody's setting in this case, so they remain with their default values. In order to set these properties, you can use ItemContainerStyle once again, and bind the properties to the values in your DataContext using Bindings:

<local:WPM_ListBox ItemsSource="{Binding RoutenListe}" 
                   Grid.Row="1" 
                   HorizontalContentAlignment="Stretch" 
                   HorizontalAlignment="Stretch" 
                   BorderThickness="0" Margin="0" Padding="0" 
                   Background="#32557B" 
                   ScrollViewer.CanContentScroll="True" 
                   ScrollViewer.VerticalScrollBarVisibility="Auto">
    <local:WPM_ListBox.ItemContainerStyle>
        <Style TargetType="{x:Type local:WPM_ListBoxItem}"
               BasedOn="{StaticResource ItemStyle}">
            <Setter Property="WPM_HeaderContent"
                    Value="{Binding Name}" />
            <Setter Property="WPM_LabelContent1"
                    Value="{Binding Startort}" />

            <!-- Etc. -->

        </Style>
    </local:WPM_ListBox.ItemContainerStyle>
</local:WPM_ListBox>

(EDIT: If you want to customize the content of some specific item, you'll need to use a ContentPresenter in your WPM_ListBoxItem's template, and then use ItemTemplate to specify how you want data to be shown inside that ContentPresenter in that specific column)

OPTION 2

Instead of overriding the default item container of the ListBox, use your WPM_ListBoxItem as a regular ContentControl or something like that, and add it to the ItemTemplate of your ListBox, changing your Triggers for DataTriggers bound to the IsSelected property of the actual ListBoxItem (which is the regular one generated by the ListBox, now).

WPM_ListBoxItem.cs:

public class WPM_ListBoxItem : ContentControl
{
    ...
}

Your WPM_ListBoxItem style Triggers:

<ControlTemplate.Triggers>
    <DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}" Value="true">
        <Setter TargetName="row2" Property="Height" Value="Auto"/>
        <Setter TargetName="border" Property="Height" Value="Auto"/>
        <Setter Property="Height" Value="Auto"/>
        <Setter TargetName="border" Property="BorderThickness1" Value="1 1 0 0"/>
        <Setter TargetName="border" Property="BorderThickness2" Value="1 1 0 0"/>
        <Setter TargetName="border" Property="BorderThickness3" Value="1 1 0 0"/>
        <Setter TargetName="border" Property="BorderThickness4" Value="1 1 0 0"/>
        <Setter TargetName="border" Property="BorderThickness5" Value="1 1 0 0"/>
        <Setter TargetName="headercontent" Property="Padding" Value="3 2 0 0"/>
    </Trigger>
    <DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}" Value="false">
        <Setter TargetName="row2" Property="Height" Value="0"/>
        <Setter TargetName="border" Property="Height" Value="35"/>
        <Setter Property="Height" Value="35"/>
        <Setter TargetName="border" Property="BorderThickness1" Value="0"/>
        <Setter TargetName="border" Property="BorderThickness2" Value="0"/>
        <Setter TargetName="border" Property="BorderThickness3" Value="0"/>
        <Setter TargetName="border" Property="BorderThickness4" Value="0"/>
        <Setter TargetName="border" Property="BorderThickness5" Value="0"/>
        <Setter TargetName="headercontent" Property="Padding" Value="0 2 0 0"/>
    </Trigger>
</ControlTemplate.Triggers>

Your view's XAML:

<ListBox ItemsSource="{Binding RoutenListe}" Grid.Row="1" HorizontalContentAlignment="Stretch" HorizontalAlignment="Stretch" BorderThickness="0" Margin="0" Padding="0" Background="#32557B" ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Auto">
    <local:WPM_ListBox.ItemTemplate>
        <DataTemplate>
            <local:WPM_ListBoxItem WPM_HeaderContent="{Binding Name}" 
                                   WPM_LabelContent1="{Binding Startort}"  WPM_LabelContent2="{Binding Zielort}" 
                                   Background="Transparent" WPM_HeaderContentMargin="6 2 0 0" WPM_HeaderContenFontSize="24" WPM_LabelContentMargin="3 -4 0 3" 
                                   WPM_LabelDescription1="Start:"  WPM_LabelDescription2="Ziel:" WPM_LabelDescriptionMargin="10 -4 0 3" />
        </DataTemplate>
    </local:WPM_ListBox.ItemTemplate>
</ListBox>