Search code examples
c#wpftriggerscontentpresenter

how to set ContentPresenter Contents Background


I have a ListBox which is Bound to an ObesvableCollection of dynamically created UserControls.

<ListBox x:Name="myListBox">
   <ListBox.Style>
      <Style TargetType="{x:Type ListBox}">
            <Setter Property="ItemsSource" Value="{Binding userControlsCollection}"/>
         ....
      </Style>
   </LIstBox.Style>
    <ListBox.ItemContainerStyle>
                <Style TargetType="{x:Type ListBoxItem}">
                    <EventSetter Event="Selector.Selected" Handler="ListBox_Selected"/>
                    <EventSetter Event="Selector.Unselected" Handler="ListBox_UnSelected"/>
                    <Setter Property="Background" Value="{DynamicResource DefaultBackground}" />
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type ListBoxItem}">

                                <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                                      VerticalAlignment="{TemplateBinding VerticalContentAlignment}" 
                                                      SnapsToDevicePixels="true"
                                                      Width="{Binding ActualWidth,RelativeSource={RelativeSource FindAncestor,AncestorType=ListBoxItem,AncestorLevel=1}}"
                                                      Height="{Binding ActualHeight,RelativeSource={RelativeSource FindAncestor,AncestorType=ListBoxItem, AncestorLevel=1}}"                                                      
                                                      />                                
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                 </Style>
</ListBox>

I would like to set the Background of the selected control should be something like that

<Style.Triggers>
    <Trigger Property="IsSelected" Value="True">
        <Setter Property="Background" Value="{DynamicResource SelectedBackground}"/>
    </Trigger>
</Style.Triggers>

but that way I set the ListBoxItem Background and it doesn't propagate the the UserControls Background...

the way I solve it now is using the Selector.Selected/UnSelected event handlers like this

private void ListBox_Selected(object sender, RoutedEventArgs e)
    {

        var item = e.Source as ListBoxItem;
        var ctrl= item.Content as myControl;

        if (ctrl!= null)
        {
            ctrl.Background = new SolidColorBrush(DefaultSelectedBackground);

        }           
    }

Any Idea would be greatly appriciated


Solution

  • Try to keep your ItemContainerStyle simple. If you need to mess with the Template of the item, use ItemTemplate or RelativeSource bindings to achieve what you need.

    To get your requirement with RelativeSource Binding, I'd just have the ItemContainerStyle as something like:

    <ListBox.ItemContainerStyle>
      <Style TargetType="{x:Type ListBoxItem}">
        <Setter Property="Background"
                Value="BurlyWood" />
        <Setter Property="HorizontalContentAlignment"
                Value="Stretch" />
        <Setter Property="VerticalContentAlignment"
                Value="Stretch" />
        <Style.Triggers>
          <Trigger Property="IsSelected"
                    Value="True">
            <Setter Property="Background"
                    Value="Tomato" />
          </Trigger>
          <Trigger Property="IsMouseOver"
                    Value="True">
            <Setter Property="Background"
                    Value="CadetBlue" />
          </Trigger>
        </Style.Triggers>
      </Style>
    </ListBox.ItemContainerStyle>
    

    Now to get this Background in the UserControl, my UserControl xaml would be like:

    <UserControl ...
                 Background="{Binding RelativeSource={RelativeSource FindAncestor,
                                                                     AncestorType={x:Type ListBoxItem}},
                                      Path=Background}">
    

    That's it, we're sorted.

    You can get the demo of this from: Here

    Update:

    If you're creating the UserControl from code-behind(not sure why you need to but anyways), Assign the Binding in code-behind too. Something like:

    public UserControl CreateUserControl(string text) {
      Binding binding = new Binding {
        Path = new PropertyPath(BackgroundProperty),
        RelativeSource = new RelativeSource() {
          Mode = RelativeSourceMode.FindAncestor,
          AncestorType = typeof(ListBoxItem)
        }
      };
      var uc = new UserControl {
        Content = new TextBlock {
          Text = text,
          FontSize = 24,
          FontWeight = FontWeights.Bold,
          HorizontalAlignment = HorizontalAlignment.Center,
          VerticalAlignment = VerticalAlignment.Center
        }
      };
    
      BindingOperations.SetBinding(uc, BackgroundProperty, binding);
      return uc;
    }
    

    and the ItemContainerStyle would remain the same as before and you should be done.

    Demo for this approach: Here