WPF列表框:鼠标悬停时在ItemTemplate中显示按钮

37

我有一个包含图片和按钮的列表框。默认情况下,按钮是隐藏的。我想在悬停在列表框中的项目上时使按钮可见。 我使用的XAML如下。谢谢

<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>
5个回答

55

好的,在您的按钮声明中尝试使用以下代码:

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

所以我正在使用一个带有触发器的样式,向上查找可视树,直到找到一个ListBoxItem,当其IsMouseOver属性变为True时,我将button的可见性设置为Visible

看看它是否接近你想要的。


在我的情况下,我将样式移动到Window.Resources并进行了编辑,如下所示:x:Key="ShowHideButton" TargetType="Button"每个需要此功能的按钮只需添加以下内容: Style="{StaticResource ShowHideButton} - Stonetip
1
似乎 PropertyName 应该是 Property?+1 - Peter

16

这个Style可以满足你的需求。鼠标悬停时,只有当指针位于ListBoxItem上方时,按钮才变得可见。特殊的技巧是绑定到TemplatedParent以达到IsMouseOver并在Setter上使用TargetName来仅影响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>

1
我的问题是:哪一种方法更高效?是更改按钮的可见性,还是在鼠标悬停触发器中创建它?因为我认为创建10000个按钮然后隐藏它们并不像在鼠标悬停触发器中创建具体的按钮那样高效。 - theSpyCry
CPU时间或内存使用上高效,在初始化或鼠标移动时?这对编程复杂度会有什么影响?尝试拼凑一些有意义的测试用例并进行测量! - David Schmitt

5

@David 展示了正确的方法, 但我对你的 XAML 架构有一个建议。如果 Button 上没有任何 DataBinding,最好将其放在 ListBoxItem 样式中而不是 DataTemplate 中,如下所示。

  <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>

这看起来不错,因为它摆脱了RelativeSource。但我总是不愿意改变ControlTemplate... - David Schmitt

0
只是想知道,如果我们使用上面的技术,我们如何确定按钮点击的是哪个项目?
为了回答Brian的问题,在按钮点击处理程序中,您可以沿着可视树向上查找包含该按钮的项目:
        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.
        }

0
一种查找被点击项的解决方案是添加以下事件设置器:
XAML
C# void ListBoxItem_MouseEnter(object sender, MouseEventArgs e) { _memberVar = (sender as ListBoxItem).Content; }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接