WPF - 配置自定义事件以触发用户控件上的开始 Storyboard

标签 wpf canvas storyboard eventtrigger elementhost

我希望有人能够在这里提供帮助,要么建议如何解决我所遇到的问题,要么提出更好的方法来实现我所追求的目标。

我有一个简单的自定义控件,我希望能够在其上启动/停止/暂停/恢复不透明动画。我研究了各种解决方案,并认为将 EventTrigger 与自定义 RoutedEvent 结合使用是前进的方向。我可以让以下内容与标准事件(例如 MouseDown)一起使用,但无法让我的自定义事件启动 Storyboard 。

我尝试在加载事件中执行该事件之前在构造函数中引发该事件,但这两者都没有任何区别。

我认为它没有任何相关影响,但这也托管在 winforms 的 ElementHost 中。

xaml:

<UserControl x:Class="Fraxinus.BedManagement.WpfControls.FraxIconX"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Fraxinus.BedManagement.WpfControls"
             mc:Ignorable="d" 
             d:DesignHeight="16" d:DesignWidth="16"
             Loaded="FraxIconX_Loaded">
    <!--Height="16" Width="16" Background="{Binding Path=Background, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Control}, AncestorLevel=1}}" -->
    <UserControl.Resources>
        <ResourceDictionary>
            <Border x:Key="RoundedBorderMask" Name="iconBorder" CornerRadius="4" Background="White" BorderThickness="2" Height="16" Width="16" HorizontalAlignment="Center" VerticalAlignment="Center"/>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Dictionary.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </UserControl.Resources>
    <Border BorderThickness="2" CornerRadius="4" Height="16" Width="16" HorizontalAlignment="Center" VerticalAlignment="Center">
        <local:FraxCanvas x:Name="fraxCanvas" Height="16" Width="16" HorizontalAlignment="Center" VerticalAlignment="Center">
            <local:FraxCanvas.Background>
                <LinearGradientBrush StartPoint="0,0" EndPoint="0,1" GradientStops="{Binding GradientStops}" />
            </local:FraxCanvas.Background>
            <local:FraxCanvas.Triggers>
                <EventTrigger RoutedEvent="local:FraxCanvas.OnStartStoryboard" SourceName="fraxCanvas">
                    <BeginStoryboard x:Name="storyboard">
                        <Storyboard>
                            <DoubleAnimation Storyboard.TargetProperty="Opacity" From="1" To="0.25" AutoReverse="True" Duration="0:0:1" RepeatBehavior="Forever"></DoubleAnimation>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
            </local:FraxCanvas.Triggers>
            <local:FraxCanvas.OpacityMask>
                <!--https://wpf.2000things.com/2012/05/11/556-clipping-to-a-border-using-an-opacity-mask/-->
                <VisualBrush Visual="{StaticResource RoundedBorderMask}"></VisualBrush>
            </local:FraxCanvas.OpacityMask>
            <Image HorizontalAlignment="Center" VerticalAlignment="Center" Height="16" Width="16" Source="{DynamicResource BitmapClockPng}" />
        </local:FraxCanvas>
    </Border>
</UserControl>

隐藏代码

    public partial class FraxIconX : UserControl
    {

        public GradientStopCollection GradientStops { get; set; }

        public FraxIconX() { }

        public FraxIconX(Color backColour, int width = 16, int height = 16)
        {
            Width = width;
            Height = height;
            GradientStops = new GradientStopCollection { new GradientStop(Colors.AliceBlue, 0.0), new GradientStop(Colors.Navy, 1.0) };
            Background = new SolidColorBrush(backColour);
            DataContext = this;
            InitializeComponent();
        }
        public void FraxIconX_Loaded(object sender, RoutedEventArgs args)
        {
            RaiseEvent(new RoutedEventArgs(FraxCanvas.StartStoryboardEvent));
        }
    }

FraxCanvas:

    public class FraxCanvas : Canvas
    {
        public static readonly RoutedEvent StartStoryboardEvent = EventManager.RegisterRoutedEvent("OnStartStoryboard", RoutingStrategy.Tunnel, typeof(RoutedEventHandler), typeof(FraxCanvas));

        public event RoutedEventHandler OnStartStoryboard
        {
            add
            {
                this.AddHandler(StartStoryboardEvent, value);
            }

            remove
            {
                this.RemoveHandler(StartStoryboardEvent, value);
            }
        }
    }

最佳答案

两个问题。

  1. 您仅监听 EventTrigger.SourceName="fraxCanvas",但该事件是在其他地方(父 UserControl)引发的。
    EventTrigger.SourceName 是一个过滤器属性,它允许 专门处理特定元素的路由事件。如果您没有设置EventTrigger.SourceName, 触发器将处理指定的事件,无论 来源。
  2. 路由事件永远不会到达 FraxCanvas 元素,因为父级引发了它。
    RoutingStrategy.Bubble:事件从事件源(FraxIconX 元素)传输到 可视化树根,例如Window
    RoutingStrategy.Tunnel:事件从树根开始传播,例如窗口并在事件源停止FraxIconX 元素。

解决方案是删除 EventTrigger.SourceName 属性并将触发器移动到事件源(UserControl)或事件源的任何父元素:

<UserControl>    
  <UserControl.Triggers>
    <EventTrigger RoutedEvent="local:FraxCanvas.OnStartStoryboard">
      <BeginStoryboard x:Name="storyboard">
        <Storyboard>
          <DoubleAnimation Storyboard.TargetName="fraxCanvas" 
                           Storyboard.TargetProperty="Opacity" 
                           From="1" To="0.25" />
        </Storyboard>
      </BeginStoryboard>
    </EventTrigger>
  </UserControl.Triggers>

  <Border>
    <FraxCanvas x:Name="fraxCanvas" />
  </Border>
</UserControl>

关于WPF - 配置自定义事件以触发用户控件上的开始 Storyboard,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60620185/

相关文章:

c# - 如何使用 Databound Itemscontrol 自定义 DataGrid 的 header

使用我的界面时,WPF 依赖属性在 GUI 中崩溃

javascript - 如何使用 javascript 或 jquery 更改 Canvas 元素的 fillStyle?

iphone - 确定 segue 是否为 unwind segue?

c# - 如何在 ScaleTransform 动画后重置控件

c# - 如何将所有 ListBox 的项目绑定(bind)到同一个属性?

.net - 如何获得图像中的多数颜色?

javascript - 如何在 html canvas 中用颜色填充一定比例的圆圈区域?

c# - .clearRect(x,y,w,h) Canvas 动画

objective-c - 如何在viewcontroller中使用TableView?