how to set ContentPresenter Contents Background

6.3k views Asked by At

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

3

There are 3 answers

5
Viv On BEST ANSWER

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

0
Nitin On

Try setting the style trigger like this:

                <Style.Triggers> 
                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsSelectedProperty}" Value="True"> 
                        <Setter Property="Control.Background" Value="Red" /> 
                    </DataTrigger> 
                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsSelectedProperty}" Value="False"> 
                        <Setter Property="Control.Background" Value="Black" /> 
                    </DataTrigger> 
                </Style.Triggers> 

Thanks

0
Suresh On

To acheive what you are saying, you could wrap your ContentPresenter in a panel (e.g. Grid) and use TemplateBinding to set the background .

Sample Control Template:

<ControlTemplate TargetType="ListBoxItem">
    <Grid Background="{TemplateBinding Background}">
        <ContentPresenter Content="{Binding}" />
    </Grid>

    <ControlTemplate.Triggers>
        <Trigger Property="IsSelected" Value="True">
            <Setter Property="Background" Value="Fuchsia" />
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>