在ViewModel中检测悬停在ListBoxItem上

本文关键字:ListBoxItem 悬停 检测 ViewModel | 更新日期: 2023-09-27 17:50:19

我有一个自定义项目模板的ListBox,我想当鼠标悬停在ListBox中的一个项目上时,在DataContext上设置一个属性(ListBoxItem绑定的项目)。

我可以很容易地改变一些视觉上的东西,比如当我用DataTrigger:

将鼠标悬停在上面时的背景颜色
<Style x:Key="MyListBoxItemStyle" TargetType="{x:Type ListBoxItem}">
    <Setter Property="SnapsToDevicePixels" Value="true"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ListBoxItem">
                <Border Name="Border">
                    <Image Source="{Binding ImageSource}" Width="32" Height="32" Margin="2,0,2,0"/>
                </Border>
                <ControlTemplate.Triggers>
                    <DataTrigger Binding="{Binding IsMouseOver,RelativeSource={RelativeSource Self}}" 
                     Value="True">
                        <Setter TargetName="Border" Property="Background" Value="Red" />
                    </DataTrigger>
                    <Trigger Property="IsSelected" Value="True">
                        <Setter TargetName="Border" Property="Background" Value="Gray" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

但是我不知道如何使DataTrigger在我的DataContext上设置属性,所以我的ViewModel可以对悬停做出反应。

不幸的是,在这个项目中,我不能使用通常推荐用于此类任务的Blend交互式DLL,因为我无法在我们的安装过程中引入新的依赖项。

在ViewModel中检测悬停在ListBoxItem上

我最终使用附加行为解决了这个问题。这有点麻烦,但确实有效。基本上,您可以将ICommand放在ViewModel上,其Execute方法在鼠标进入时被调用,true作为参数,false作为鼠标离开时被调用,允许您在视图模型中响应悬停。

public static class MouseOverHelpers
{
    public static readonly DependencyProperty MouseOverCommand =
        DependencyProperty.RegisterAttached("MouseOverCommand", typeof(ICommand), typeof(MouseOverHelpers),
                                                                new PropertyMetadata(null, PropertyChangedCallback));
    private static void PropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
    {
        var ui = dependencyObject as UIElement;
        if (ui == null) return;
        if (args.OldValue != null)
        {
            ui.RemoveHandler(UIElement.MouseLeaveEvent, new RoutedEventHandler(MouseLeave));
            ui.RemoveHandler(UIElement.MouseEnterEvent, new RoutedEventHandler(MouseEnter));
        }
        if (args.NewValue != null)
        {
            ui.AddHandler(UIElement.MouseLeaveEvent, new RoutedEventHandler(MouseLeave));
            ui.AddHandler(UIElement.MouseEnterEvent, new RoutedEventHandler(MouseEnter));
        }
    }
    private static void ExecuteCommand(object sender, bool parameter)
    {
        var dp = sender as DependencyObject;
        if (dp == null) return;
        var command = dp.GetValue(MouseOverCommand) as ICommand;
        if (command == null) return;
        if (command.CanExecute(parameter))
        {
            command.Execute(parameter);
        }
    }
    private static void MouseEnter(object sender, RoutedEventArgs e)
    {
        ExecuteCommand(sender, true);
    }
    private static void MouseLeave(object sender, RoutedEventArgs e)
    {
        ExecuteCommand(sender, false);
    }
    public static void SetMouseOverCommand(DependencyObject o, ICommand value)
    {
        o.SetValue(MouseOverCommand, value);
    }
    public static ICommand GetMouseOverCommand(DependencyObject o)
    {
        return o.GetValue(MouseOverCommand) as ICommand;
    }
}

它是这样使用的:

<ControlTemplate TargetType="ListBoxItem">
    <Border Name="Border"
           my:MouseOverHelpers.MouseOverCommand="{Binding MouseOverCommand}">
        <Image Source="{Binding ImageSource}" Width="32" Height="32" Margin="2,0,2,0"/>
    </Border>
</ControlTemplate>