Search code examples
c#wpfxamlborderlistboxitem

Customizing ListBoxItem: Getting rid of border + clickable area


I'm customizing a ListBoxItem, and I'm struggling with a couple of issues.

  • The ItemTemplate is a StackPanel of vertical orientation, whose height is set to 120px. But only clicking the label or the image causes the ListBox to select the item, not the open area below the label. How do I get the open area to also cause a change in selection? I'm considering adding a styled Button as ListBoxItem, but I wonder if it can be done any easier.
  • There's a 1px line between the ListBox container and the ListBoxItem's that I'm not able to get rid of. How do I get rid of it? Here's a screenshot:

enter image description here

XAML:

<ListBox Grid.Row="1" ItemsSource="{Binding MenuButtonInfoList}"
        SelectedIndex="{Binding SelectedMainMenuIndex, Mode=TwoWay}">
    <ListBox.Resources>
        <Style TargetType="{x:Type ListBox}">
            <Setter Property="Background" Value="Transparent" />
            <Setter Property="Width" Value="160px" />
            <Setter Property="BorderBrush" Value="{StaticResource PrimaryColor}" />
            <Setter Property="BorderThickness" Value="0, 1, 0, 0" />
        </Style>
        <Style TargetType="{x:Type ListBoxItem}">
            <Setter Property="OverridesDefaultStyle" Value="True" />
            <Setter Property="SnapsToDevicePixels" Value="True" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListBoxItem}">
                        <Border x:Name="MenuButtonBorder" SnapsToDevicePixels="True"
                                BorderBrush="{StaticResource PrimaryColor}" BorderThickness="0, 0, 0, 1">
                            <StackPanel Orientation="Vertical" Height="120px">
                                <Image Source="{Binding ImageFilePath}" Height="30" Width="30" />
                                <Label x:Name="MenuButtonLabel" Content="{Binding Label}" 
                                        FontSize="{StaticResource Title1FontSize}"
                                        Foreground="{StaticResource PrimaryColor}" />
                            </StackPanel>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsFocused" Value="True">
                                <Setter TargetName="MenuButtonBorder" Property="Background" Value="{StaticResource PrimaryColor}" />
                                <Setter TargetName="MenuButtonLabel" Property="Foreground" Value="{StaticResource HighlightColor}" />
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ListBox.Resources>
</ListBox>

Solution

  • How do I get the open area to also cause a change in selection?

    Set Background="Transparent" on the StackPanel in your item template. That will make the entire panel hit testable.

    There's a 1px line between the ListBox container and the ListBoxItem's that I'm not able to get rid of. How do I get rid of it?

    Override the ListBox template to remove the 1px padding:

    <ControlTemplate TargetType="{x:Type ListBox}">
      <Border Name="Bd"
              Background="{TemplateBinding Background}"
              BorderBrush="{TemplateBinding BorderBrush}"
              BorderThickness="{TemplateBinding BorderThickness}"
              SnapsToDevicePixels="true">
         <!-- Padding="1" (removed) -->
        <ScrollViewer Padding="{TemplateBinding Padding}"
                      Focusable="false">
          <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
        </ScrollViewer>
      </Border>
      <ControlTemplate.Triggers>
        <Trigger Property="IsEnabled"
                  Value="false">
          <Setter TargetName="Bd"
                  Property="Background"
                  Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
        </Trigger>
        <MultiTrigger>
          <MultiTrigger.Conditions>
            <Condition Property="IsGrouping" Value="true" />
            <Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false" />
          </MultiTrigger.Conditions>
          <Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
        </MultiTrigger>
      </ControlTemplate.Triggers>
    </ControlTemplate>
    

    Place the above in a Setter for the Template property in your ListBox style.