WPF - настроить пользовательское событие для запуска раскадровки начала на пользовательском элементе управления - PullRequest
2 голосов
/ 10 марта 2020

Я надеюсь, что кто-то сможет здесь помочь, либо посоветовать, как исправить то, что у меня есть, либо предложить лучший способ достижения того, что мне нужно.

У меня есть простой пользовательский элемент управления, на котором я хотите иметь возможность запускать / останавливать / останавливать / возобновлять анимацию непрозрачности. Я посмотрел на различные решения и понял, что использование EventTrigger с пользовательским RoutedEvent - это путь вперед. Я могу настроить следующее для работы со стандартным событием (например, MouseDown), но не могу получить свое собственное событие для запуска раскадровки.

Я пытался вызвать событие в конструкторе, прежде чем сделать это в загруженное событие, но оно не имеет никакого значения.

Я не думаю, что оно имеет какое-либо существенное влияние, но оно также размещается в ElementHost для 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>

Код позади

    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 Ответ

2 голосов
/ 11 марта 2020

Две проблемы.

  1. Вы слушаете исключительно EventTrigger.SourceName="fraxCanvas", но событие вызывается где-то еще (родитель UserControl).
    EventTrigger.SourceName - это свойство фильтра, которое позволяет исключительно обрабатывать маршрутизируемое событие определенного элемента c. Если вы не установите EventTrigger.SourceName, триггер будет обрабатывать указанное событие, независимо от источника.
  2. Маршрутизируемое событие никогда не достигнет элемента FraxCanvas, потому что родительский объект вызвал его.
    RoutingStrategy.Bubble: событие перемещается из источника события , элемента FraxIconX, в визуальное дерево root например, Window.
    RoutingStrategy.Tunnel: событие перемещается из дерева root например, Window и останавливается у источника события , элемента 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>
...