检测是否将鼠标悬停在ViewModel中的ListBoxItem上

时间:2014-03-14 11:17:56

标签: c# wpf xaml data-binding mvvm

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

但我无法弄清楚如何让DataTriggerDataContext上设置属性,因此我的ViewModel可以对鼠标悬停做出反应。

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

1 个答案:

答案 0 :(得分:3)

我最终使用附加行为解决了这个问题。这有点麻烦,但确实有效。基本上,您可以在ViewModel上放置ICommand,其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>