59

I have a listbox, and I have the following ItemTemplate for it:

<DataTemplate x:Key="ScenarioItemTemplate">
    <Border Margin="5,0,5,0"
            Background="#FF3C3B3B"
            BorderBrush="#FF797878"
            BorderThickness="2"
            CornerRadius="5">
        <DockPanel>
            <DockPanel DockPanel.Dock="Top"
                       Margin="0,2,0,0">
                <Button HorizontalAlignment="Left"
                        DockPanel.Dock="Left"
                        FontWeight="Heavy"
                        Foreground="White" />
                <Label Content="{Binding Path=Name}"
                       DockPanel.Dock="Left"
                       FontWeight="Heavy"
                       Foreground="white" />
                <Label HorizontalAlignment="Right"
                       Background="#FF3C3B3B"
                       Content="X"
                       DockPanel.Dock="Left"
                       FontWeight="Heavy"
                       Foreground="White" />
            </DockPanel>
            <ContentControl Name="designerContent"
                            Visibility="Collapsed"
                            MinHeight="100"
                            Margin="2,0,2,2"
                            Content="{Binding Path=DesignerInstance}"
                            Background="#FF999898">
            </ContentControl>
        </DockPanel>
    </Border>
</DataTemplate>

As you can see the ContentControl has Visibility set to collapsed.

I need to define a trigger that causes the Visibility to be set to "Visible"

when the ListItem is selected, but I can't figure it out.

Any ideas?

UPDATE: Of course I could simply duplicate the DataTemplate and add triggers to the ListBox in question to use either one or the other, but I want to prevent duplicating this code.

JasonMArcher
  • 14,195
  • 22
  • 56
  • 52
TimothyP
  • 21,178
  • 26
  • 94
  • 142

2 Answers2

128

You can style your ContentControl such that a trigger fires when its container (the ListBoxItem) becomes selected:

<ContentControl 
    x:Name="designerContent"
    MinHeight="100"
    Margin="2,0,2,2"
    Content="{Binding Path=DesignerInstance}"
    Background="#FF999898">
    <ContentControl.Style>
        <Style TargetType="{x:Type ContentControl}">
            <Setter Property="Visibility" Value="Collapsed"/>
            <Style.Triggers>
                <DataTrigger
                        Binding="{Binding
                            RelativeSource={RelativeSource
                                Mode=FindAncestor,
                                AncestorType={x:Type ListBoxItem}},
                                Path=IsSelected}"
                        Value="True">
                    <Setter Property="Visibility" Value="Visible"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ContentControl.Style>
</ContentControl>

Alternatively, I think you can add the trigger to the template itself and reference the control by name. I don't know this technique well enough to type it from memory and assume it'll work, but it's something like this:

<DataTemplate x:Key="ScenarioItemTemplate">
    <DataTemplate.Triggers>
        <DataTrigger
                Binding="{Binding
                    RelativeSource={RelativeSource
                        Mode=FindAncestor,
                        AncestorType={x:Type ListBoxItem}},
                        Path=IsSelected}"
                Value="True">
            <Setter
                TargetName="designerContent"
                Property="Visibility"
                Value="Visible"/>
        </DataTrigger>
    </DataTemplate.Triggers>

    ...
</DataTemplate>
Ryan Lundy
  • 204,559
  • 37
  • 180
  • 211
Matt Hamilton
  • 200,371
  • 61
  • 386
  • 320
  • 1
    Dude! Thank you so much! I wasn't having this issue but the FindAncestor helped me solve a big pain I had been banging my head for 2 hours! Thanks Again. Jason – Jason Stevenson Jul 26 '09 at 15:27
  • 1
    What's designerContent??? I am trying to set the selected listboxitem's background (I use an inline datatemplate). I want, that when the user selects an item it's background shouldn't become blue but should have the same color as unselected. – Shimmy Weitzhandler Aug 27 '09 at 16:57
  • Just wanted to add that the DataTrigger Binding in the second example helped me in a big way. Thanks! – NigelTufnel Jul 22 '10 at 19:19
  • 2
    The DataTemplate.Triggers example was really useful! Thanks! Pro-tip: You've got to declare the controls being referenced by "TargetName=" before the element. – Wes Mar 31 '15 at 00:45
  • As I read, using 'FindAncestor' is bad practice, since it will run searches everytime where a more direct connection should be possible. (At the moment I can't name you the better solution) – Mechandrius Sep 22 '20 at 12:54
3

@Matt, Thank you!!!

Just had to add a trigger for IsSelected == false as well, and now it works like a charm!

<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
    <Setter Property="Visibility" Value="Collapsed"/>
    <Style.Triggers>
        <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBoxItem}},Path=IsSelected}" Value="True">
            <Setter Property="Visibility" Value="Visible"/>
        </DataTrigger>
        <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBoxItem}},Path=IsSelected}" Value="False">
            <Setter Property="Visibility" Value="Collapsed"/>
        </DataTrigger>
    </Style.Triggers>
</Style>

TimothyP
  • 21,178
  • 26
  • 94
  • 142
  • 2
    You shouldn't have to have a separate trigger for the "False" case. That's handled by the Visibility Setter outside of the Triggers collection. – Matt Hamilton Oct 29 '08 at 22:29
  • 9
    Just specify one value as the default on the target itself, and use the trigger to specify the alternative value. – Drew Noakes Mar 02 '09 at 15:01