tags:

views:

10981

answers:

6

I have a listbox containing and image and a button. By default the button is hidden. I want to make the button visible whenever I hover over an item in the listbox. The XAML I am using is below. Thanks

<Window.Resources>
        <Style TargetType="{x:Type ListBox}">
    <Setter Property="ItemTemplate">
                    <Setter.Value>
                        <DataTemplate>
                            <Border BorderBrush="Black" BorderThickness="1" Margin="6">
                                <StackPanel Orientation="Horizontal">
                                    <Image Source="{Binding Path=FullPath}" Height="150" Width="150"/>
                                    <Button x:Name="sideButton" Width="20" Visibility="Hidden"/>
                                </StackPanel>
                            </Border>
                        </DataTemplate>
                    </Setter.Value>
                </Setter>
        </Style>
    </Window.Resources>
+2  A: 

Ok, try this in your button declaration:

<Button x:Name="sideButton" Width="20">
    <Button.Style>
        <Style TargetType="{x:Type Button}">
            <Setter PropertyName="Visibility" Value="Hidden" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBoxItem}},Path=IsMouseOver}" Value="True">
                    <Setter PropertyName="Visibility" Value="Visible" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Button.Style>
</Button>

So I'm using a style with a trigger to look back up the visual tree until I find a ListBoxItem, and when its IsMouseOver property flips over to True I set the button's visibility to Visible.

See if it's close to what you want.

Matt Hamilton
+7  A: 

This Style does what you need. On mouse over, the button becomes only visible when the pointer is over the ListBoxItem. The special trick is to bind to the TemplatedParent for reaching IsMouseOver and use TargetName on the Setter to only affect the Button.

<Style TargetType="{x:Type ListBoxItem}">
    <Setter Property="ContentTemplate">
        <Setter.Value>
            <DataTemplate>
                <Border BorderBrush="Black"
                        BorderThickness="1"
                        Margin="6">
                    <StackPanel Orientation="Horizontal">
                        <Image Source="{Binding Path=FullPath}"
                               Height="150"
                               Width="150" />
                        <Button x:Name="sideButton"
                                Width="20"
                                Visibility="Hidden" />
                    </StackPanel>
                </Border>
                <DataTemplate.Triggers>
                    <DataTrigger Binding="{Binding IsMouseOver,RelativeSource={RelativeSource TemplatedParent}}"
                                 Value="True">
                        <Setter Property="Visibility"
                                TargetName="sideButton"
                                Value="Visible" />
                    </DataTrigger>
                </DataTemplate.Triggers>
            </DataTemplate>
        </Setter.Value>
    </Setter>
</Style>
David Schmitt
My question is: what is more efficient? To change the Button's visibility, or to create it in the MouseOver Trigger? Because I think that creating 10000 Buttons and hiding them is not that efficient that creating the concrete one in the MouseOver Trigger.
PaN1C_Showt1Me
Efficient in CPU time or memory, on initialization or when mousing around? What a difference will it make in programming complexity? Try to cobble together a somewhat meaningful test case and measure it!
David Schmitt
+2  A: 

@David is showing the right way, But I have one suggestion to your XAML architecture. If you don't have any DataBinding on the Button it is better to put that in to the ListBoxItem style than the DataTemplate as bellow.

  <Style TargetType="{x:Type ListBoxItem}">
        <Setter Property="ContentTemplate">
            <Setter.Value>
                <DataTemplate>
                    <Border BorderBrush="Black"
                    BorderThickness="1"
                    Margin="6">
                        <StackPanel Orientation="Horizontal">
                            <Image Source="{Binding Path=FullPath}"
                           Height="150"
                           Width="150" />                             
                        </StackPanel>
                    </Border>
                </DataTemplate>
            </Setter.Value>
        </Setter>            
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ListBoxItem}">
                    <Grid Background="Transparent">
                        <Button x:Name="sideButton" Width="20" HorizontalAlignment="Right" Visibility="Hidden" />
                        <ContentPresenter/>                            
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger  Property="IsMouseOver" Value="True">
                            <Setter Property="Visibility"
                            TargetName="sideButton"
                            Value="Visible" />
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>            
    </Style>
Jobi Joy
That looks good because it does away with the RelativeSource. I'm always reluctant to change the ControlTemplate though...
David Schmitt
A: 

Just wondering, if we use the technique above, how do we determine what item the button was clicked on?

A: 

One solution to find what item was clicked is to add the following Event setter

XAML

C# void ListBoxItem_MouseEnter(object sender, MouseEventArgs e) { _memberVar = (sender as ListBoxItem).Content; }

A: 

Just wondering, if we use the technique above, how do we determine what item the button was clicked on?

To answer Brian's question, in the button click handler you can walk up the visual tree to find the item that contains the button:

        DependencyObject dep = (DependencyObject)e.OriginalSource;
        while ((dep != null) && !(dep is ListBoxItem))
        {
            dep = VisualTreeHelper.GetParent(dep);
        }

        if (dep != null)
        {
            // TODO: do stuff with the item here.
        }
donovan