Подтвердить что ты не робот

Как дать условие для EventTrigger?

Можно ли дать условие в EventTrigger?? Я написал следующую кнопку EventTrigger (Mouse.MouseLeave) для радио. Я хочу, чтобы это не должно запускаться для элемента, который находится в состоянии "Проверено" (IsChecked = True).

<EventTrigger RoutedEvent="Mouse.MouseLeave" SourceName="border">                                 
      <BeginStoryboard Name="out_BeginStoryboard" Storyboard="{StaticResource out}" />
      <RemoveStoryboard BeginStoryboardName="over_BeginStoryboard" />
</EventTrigger>

Пожалуйста, дайте мне знать, как я могу это достичь?

Спасибо заранее.

4b9b3361

Ответ 1

Вы не можете использовать EventTrigger таким образом. WPF RoutedEventHandler, который вызывает EventTriggers, не предоставляет никакого механизма для создания условного триггера, и вы не можете исправить это путем подклассификации TriggerAction, потому что для переопределения не требуется действие Invoke() или Execute().

Однако это можно сделать довольно легко с помощью пользовательского класса. Вот как это будет использоваться:

<Border>
  <my:ConditionalEventTrigger.Triggers>
    <my:ConditionalEventTriggerCollection>
      <my:ConditionalEventTrigger RoutedEvent="Mouse.MouseLeave"
                                  Condition="{Binding IsChecked, ElementName=checkbox}">
        <BeginStoryboard Name="out_BeginStoryboard" Storyboard="{StaticResource out}" />               
        <RemoveStoryboard BeginStoryboardName="over_BeginStoryboard" />               
      </my:ConditionalEventTrigger>               
    </my:ConditionalEventTriggerCollection>
  </my:ConditionalEventTrigger.Triggers>
  ...

И вот как это будет реализовано:

[ContentProperty("Actions")] 
public class ConditionalEventTrigger : FrameworkContentElement
{ 
  public RoutedEvent RoutedEvent { get; set; } 
  public List<TriggerAction> Actions { get; set; }

  // Condition
  public bool Condition { get { return (bool)GetValue(ConditionProperty); } set { SetValue(ConditionProperty, value); } }
  public static readonly DependencyProperty ConditionProperty = DependencyProperty.Register("Condition", typeof(bool), typeof(ConditionalEventTrigger));

  // "Triggers" attached property
  public static ConditionalEventTriggerCollection GetTriggers(DependencyObject obj) { return (ConditionalEventTriggerCollection)obj.GetValue(TriggersProperty); }
  public static void SetTriggers(DependencyObject obj, ConditionalEventTriggerCollection value) { obj.SetValue(TriggersProperty, value); }
  public static readonly DependencyProperty TriggersProperty = DependencyProperty.RegisterAttached("Triggers", typeof(ConditionalEventTriggerCollection), typeof(ConditionalEventTrigger), new PropertyMetadata 
  { 
    PropertyChangedCallback = (obj, e) => 
    { 
      // When "Triggers" is set, register handlers for each trigger in the list 
      var element = (FrameworkElement)obj; 
      var triggers = (List<ConditionalEventTrigger>)e.NewValue;
      foreach(var trigger in triggers)
        element.AddHandler(trigger.RoutedEvent, new RoutedEventHandler((obj2, e2) =>
          trigger.OnRoutedEvent(element)));
    } 
  });

  public ConditionalEventTrigger()
  {
    Actions = new List<TriggerAction>();
  }

  // When an event fires, check the condition and if it is true fire the actions 
  void OnRoutedEvent(FrameworkElement element) 
  { 
    DataContext = element.DataContext;  // Allow data binding to access element properties
    if(Condition) 
    { 
      // Construct an EventTrigger containing the actions, then trigger it 
      var dummyTrigger = new EventTrigger { RoutedEvent = _triggerActionsEvent }; 
      foreach(var action in Actions) 
        dummyTrigger.Actions.Add(action); 

      element.Triggers.Add(dummyTrigger); 
      try 
      { 
        element.RaiseEvent(new RoutedEventArgs(_triggerActionsEvent)); 
      } 
      finally 
      { 
        element.Triggers.Remove(dummyTrigger); 
      } 
    } 
  } 

  static RoutedEvent _triggerActionsEvent = EventManager.RegisterRoutedEvent("", RoutingStrategy.Direct, typeof(EventHandler), typeof(ConditionalEventTrigger)); 

} 

// Create collection type visible to XAML - since it is attached we cannot construct it in code 
public class ConditionalEventTriggerCollection : List<ConditionalEventTrigger> {} 

Наслаждайтесь!

Ответ 2

Это то, что сработало для меня...

Я хотел выполнить анимацию на основе мыши, зависающей над элементом пользовательского интерфейса, и связанный с ним элемент пользовательского интерфейса, который активирован (т.е. включен, чтобы заставить игрока перемещаться).

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

Пример:

<MultiDataTrigger>
    <MultiDataTrigger.Conditions>
        <Condition Binding="{Binding RelativeSource={RelativeSource self}, Path=IsMouseOver}" Value="True" />
        <Condition Binding="{Binding Path=IsPlayer1Active}" Value="True" />
    </MultiDataTrigger.Conditions>
    <MultiDataTrigger.EnterActions>
        <BeginStoryboard>
            <Storyboard>
                <ColorAnimation Storyboard.TargetProperty="Background.GradientStops[0].Color" To="#FF585454" Duration="0:0:.25"/>
                <ColorAnimation Storyboard.TargetProperty="Background.GradientStops[1].Color" To="Black" Duration="0:0:2"/>
            </Storyboard>
        </BeginStoryboard>
    </MultiDataTrigger.EnterActions>
</MultiDataTrigger>

Ответ 3

Здесь моя измененная версия Ray answer, которая создает и прикрепляет фиктивные события только тогда, когда установлены триггеры источника, а не каждый раз. Я думал, что это будет лучше для моего сценария, поскольку я поднимаю событие на сотни предметов, а не один или два:

[ContentProperty("Actions")]
public class ConditionalEventTrigger : FrameworkContentElement
{
    static readonly RoutedEvent DummyEvent = EventManager.RegisterRoutedEvent(
        "", RoutingStrategy.Direct, typeof(EventHandler), typeof(ConditionalEventTrigger));

    public static readonly DependencyProperty TriggersProperty = DependencyProperty.RegisterAttached(
        "Triggers", typeof(ConditionalEventTriggers), typeof(ConditionalEventTrigger),
        new FrameworkPropertyMetadata(RefreshTriggers));

    public static readonly DependencyProperty ConditionProperty = DependencyProperty.Register(
        "Condition", typeof(bool), typeof(ConditionalEventTrigger)); // the Condition is evaluated whenever an event fires

    public ConditionalEventTrigger()
    {
        Actions = new List<TriggerAction>();
    }

    public static ConditionalEventTriggers GetTriggers(DependencyObject obj)
    { return (ConditionalEventTriggers)obj.GetValue(TriggersProperty); }

    public static void SetTriggers(DependencyObject obj, ConditionalEventTriggers value)
    { obj.SetValue(TriggersProperty, value); }

    public bool Condition
    {
        get { return (bool)GetValue(ConditionProperty); }
        set { SetValue(ConditionProperty, value); }
    }

    public RoutedEvent RoutedEvent { get; set; }
    public List<TriggerAction> Actions { get; set; }

    // --- impl ----

    // we can't actually fire triggers because WPF won't let us (stupid sealed internal methods)
    // so, for each trigger, make a dummy trigger (on a dummy event) with the same actions as the real trigger,
    // then attach handlers for the dummy event
    public static void RefreshTriggers(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        var targetObj = (FrameworkElement)obj;
        // start by clearing away the old triggers
        foreach (var t in targetObj.Triggers.OfType<DummyEventTrigger>().ToArray())
            targetObj.Triggers.Remove(t);

        // create and add dummy triggers
        foreach (var t in ConditionalEventTrigger.GetTriggers(targetObj))
        {
            t.DataContext = targetObj.DataContext; // set and Track DataContext so binding works
            // targetObj.GetDataContextChanged().WeakSubscribe(dc => t.DataContext = targetObj.DataContext);

            var dummyTrigger = new DummyEventTrigger { RoutedEvent = DummyEvent };
            foreach (var action in t.Actions)
                dummyTrigger.Actions.Add(action);

            targetObj.Triggers.Add(dummyTrigger);
            targetObj.AddHandler(t.RoutedEvent, new RoutedEventHandler((o, args) => {
                if (t.Condition) // evaluate condition when the event gets fired
                    targetObj.RaiseEvent(new RoutedEventArgs(DummyEvent));
            }));
        }
    }

    class DummyEventTrigger : EventTrigger { }
}

public class ConditionalEventTriggers : List<ConditionalEventTrigger> { }

Он используется следующим образом:

<Border>
  <local:ConditionalEventTrigger.Triggers>
    <local:ConditionalEventTriggers>
      <local:ConditionalEventTrigger RoutedEvent="local:ClientEvents.Flash" Condition="{Binding IsFlashing}">
        <BeginStoryboard Name="FlashAnimation">...

Линия

// targetObj.GetDataContextChanged().WeakSubscribe(dc => t.DataContext = targetObj.DataContext);

использует реактивную структуру и некоторые методы расширения, которые я написал, в основном нам нужно подписаться на событие .DataContextChanged целевого объекта, но нам нужно сделать это со слабой ссылкой. Если ваши объекты никогда не меняют свой datacontext, вам совсем не понадобится этот код

Ответ 4

в вашем случае вам нужно:

<EventTrigger RoutedEvent="Checked" SourceName="border">

EDIT: Основываясь на ваших комментариях, вы ищете мультитатригер.

   <MultiDataTrigger>
        <MultiDataTrigger.Conditions>
            <Condition SourceName="border" Property="IsMouseOver" Value="false" />                                            
        </MultiDataTrigger.Conditions>
        <MultiDataTrigger.EnterActions>
            <BeginStoryboard Name="out_BeginStoryboard" Storyboard="{StaticResource out}" />
            <RemoveStoryboard BeginStoryboardName="over_BeginStoryboard" />
        </MultiDataTrigger.EnterActions>
   </MultiDataTrigger>

Ответ 5

Я знаю, что это старый пост, но вот что-то, что сработало для меня, когда я оказался здесь для ответов. В принципе, мне нужна панель, которая будет анимироваться с правой стороны экрана при наведении мыши, а затем вернуться назад, когда мышь останется. Но, только когда панель не была закреплена. Свойство IsShoppingCartPinned присутствует в моей ViewModel. Что касается вашего сценария, вы можете заменить свойство IsShoppingCartPinned свойством checkbox IsChecked и запустить анимацию любого вида на EventTriggers.

Вот код:

<Grid.Style>
     <Style TargetType="{x:Type Grid}">
          <Setter Property="Margin" Value="0,20,-400,20"/>
          <Setter Property="Grid.Column" Value="0"/>
          <Style.Triggers>
               <MultiDataTrigger>
                    <MultiDataTrigger.Conditions>
                         <Condition Binding="{Binding IsShoppingCartPinned}" Value="False"/>
                         <Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}" Value="True"/>
                    </MultiDataTrigger.Conditions>
                    <MultiDataTrigger.EnterActions>
                         <BeginStoryboard Name="ExpandPanel">
                              <Storyboard>
                                   <ThicknessAnimation Duration="0:0:0.1" Storyboard.TargetProperty="Margin" To="0,20,0,20"/>
                              </Storyboard>
                         </BeginStoryboard>
                    </MultiDataTrigger.EnterActions>
                    <MultiDataTrigger.ExitActions>
                         <BeginStoryboard Name="HidePanel">
                              <Storyboard>
                                   <ThicknessAnimation Duration="0:0:0.1" Storyboard.TargetProperty="Margin" To="0,20,-400,20"/>
                              </Storyboard>
                         </BeginStoryboard>
                    </MultiDataTrigger.ExitActions>
               </MultiDataTrigger>
               <DataTrigger Binding="{Binding IsShoppingCartPinned}" Value="True">
                    <DataTrigger.EnterActions>
                         <RemoveStoryboard BeginStoryboardName="ExpandPanel"/>
                         <RemoveStoryboard BeginStoryboardName="HidePanel"/>
                    </DataTrigger.EnterActions>
                    <DataTrigger.Setters>
                         <Setter Property="Margin" Value="0"/>
                         <Setter Property="Grid.Column" Value="1"/>
                    </DataTrigger.Setters>
               </DataTrigger>
          </Style.Triggers>
     </Style>
</Grid.Style>