I hope someone is able to assist here, either to advise how to fix what I have or to suggest a better way of achieving what I'm after.
I have a simple custom control on which I want to be able to start/stop/pause/resume an opacity animation. I've looked at various solutions and figured that using EventTrigger with a custom RoutedEvent was the way forward. I can get the following to work with a standard event (eg MouseDown) but am unable to get my custom event to start the storyboard.
I tried raising the event in the constructor prior to doing it in a loaded event, but neither makes any difference.
I don't think it has any relevant impact but this is also being hosted in an ElementHost for winforms.
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>
Code behind
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);
}
}
}
Two issues.
EventTrigger.SourceName="fraxCanvas", but the event is raised somewhere else (parentUserControl).EventTrigger.SourceNameis a filter property, which allows to exclusively handle a Routed Event of a specific element. If you don't setEventTrigger.SourceName, the trigger will handle the specified event, no matter the source.FraxCanvaselement, because a parent raised it.RoutingStrategy.Bubble: event travels from event source, theFraxIconXelement, to the visual tree root e.g.,Window.RoutingStrategy.Tunnel: event travels from the tree root e.g.,Windowand stops at the event source, theFraxIconXelement.The solution is to remove the
EventTrigger.SourceNameattribute and move the trigger to the event source (theUserControl) or to any of the event source's parent element: