VisualState 故事板未在路径 'MouseOver' 状态下激活

VisualState storyboard not activating on path 'MouseOver' state

我正在尝试在鼠标悬停时更改路径控件的颜色,其中路径数据始终通过数据绑定进行更改。它应该从黄色变为红色,但是当我将鼠标移到它上面时,路径上没有颜色变化,它仍然是黄色。

我的看法:

MainWindow.xaml

<reactiveui:ReactiveWindow
    x:Class="TestWpfAnim.MainWindow"
    x:TypeArguments="local:MainViewModel"

    xmlns:reactiveui="http://reactiveui.net" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:TestWpfAnim"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">
<Window.Resources>

    <local:PointsToPathNormalConv x:Key="MyConv" />


</Window.Resources>
<Grid>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="CommonStates">
                <VisualStateGroup.Transitions>
                    <!--Take one half second to transition to the MouseOver state.-->
                    <VisualTransition To="MouseOver" GeneratedDuration="0:0:0.5" />
                </VisualStateGroup.Transitions>
                <VisualState x:Name="Normal"/>
                <VisualState x:Name="MouseOver">
                    <Storyboard>
                        <ColorAnimation Storyboard.TargetName="MyAnimatedBrush" Storyboard.TargetProperty="Color" To="Red" Duration="0:0:0.5"/>
                    </Storyboard>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
    <Path 
        x:Name="MainPath" StrokeThickness="4"
        Data="{Binding Poly, Converter={StaticResource MyConv}}">
        <Path.Stroke>
            <SolidColorBrush Color="Yellow" x:Name="MyAnimatedBrush" />
        </Path.Stroke>
    </Path>
</Grid>

MainWindow.xaml.cs

public partial class MainWindow : ReactiveWindow<MainViewModel>
{
    public MainWindow()
    {
        InitializeComponent();
        ViewModel = new MainViewModel();
        DataContext = ViewModel;

        var timer = new DispatcherTimer(DispatcherPriority.Normal, Dispatcher.CurrentDispatcher)
        {
            Interval = TimeSpan.FromSeconds(2)
        };
        int x = 100;
        int y = 100;

        timer.Tick += (o, e) =>
        {
            ViewModel.Add(x++, y++ * 2);
        };

        timer.Start();


    }
}

转换器

[ValueConversion(typeof(IEnumerable<System.Windows.Point>), typeof(Geometry))]
public class PointsToPathNormalConv : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is IEnumerable<System.Windows.Point> points && points.Any())
        {
            List<LineSegment> segments = new List<LineSegment>();

            foreach (var p in points)
            {
                segments.Add(new LineSegment(p, true));
            }

            PathFigure figure = new PathFigure(points.First(), segments, false); //true if closed
            PathGeometry geometry = new PathGeometry();
            geometry.Figures.Add(figure);

            return geometry;
        }

        return null;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

我的视图模型: MainViewModel.cs

public class MainViewModel : AbstractNotifyPropertyChanged, IDisposable
{
    public MainViewModel()
    {
        Poly = new ObservableCollection<System.Windows.Point>();
    }

    public void Add(int x, int y)
    {
        var newlist = Poly.ToList();
        newlist.Add(new System.Windows.Point(x, y));
        Poly = new ObservableCollection<System.Windows.Point>(newlist);
    }

    public void Dispose()
    {
    }
    private ObservableCollection<System.Windows.Point> _poly;

    public ObservableCollection<System.Windows.Point> Poly
    {
        get => _poly;
        set => this.SetAndRaise(ref _poly, value);
    }
}

根据 documentation,VisualTransition 本身不能仅仅因为名称是 "MouseOver" 而触发。它仍然需要某种事件处理程序来执行转换。您必须为 Path 触发的 MouseOver 事件添加事件处理程序。

我发现我的错误是根据我最初了解使用 VisualStateManager 的各种来源对 pre-existing 视觉状态名称做出假设。

将以下代码添加到我的视图中按预期运行:

MainWindow.xaml.cs

    public void Path_MouseEnter(object sender, MouseEventArgs e)
    {
        if (sender is FrameworkElement fe)
            VisualStateManager.GoToElementState(fe, "MouseEnter", true);
    }

    public void Path_MouseLeave(object sender, MouseEventArgs e)
    {
        if (sender is FrameworkElement fe)
            VisualStateManager.GoToElementState(fe, "MouseLeave", true);
    }

MainWindow.xaml

将状态管理器代码移动到 Path 元素中。在将更改状态的路径上指定 MouseEnterMouseLeave 操作。

<Path 
    x:Name="MainPath" StrokeThickness="4"
    Data="{Binding Poly, Converter={StaticResource MyConv}}" 
    MouseEnter="Path_MouseEnter" 
    MouseLeave="Path_MouseLeave">
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="CommonStates">
            <VisualState x:Name="MouseLeave"/>
            <VisualState x:Name="MouseEnter">
                <Storyboard>
                    <ColorAnimation Storyboard.TargetName="MyAnimatedBrush" Storyboard.TargetProperty="Color" To="Red" Duration="0:0:0.5"/>
                </Storyboard>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
    <Path.Stroke>
        <SolidColorBrush Color="Yellow" x:Name="MyAnimatedBrush" />
    </Path.Stroke>
</Path>