2010-05-04 4 views
17

Est-il possible de donner la condition dans le EventTrigger ?? J'ai écrit un EventTrigger (Mouse.MouseLeave) suivant pour le bouton radio. Je veux que ceci ne soit pas déclenché pour un item qui est en condition Checked (IsChecked = True).Comment donner la condition pour EventTrigger?

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

S'il vous plaît laissez-moi savoir comment puis-je y parvenir?

Merci d'avance.

Répondre

0

dans votre cas, vous devez:

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

EDIT: Sur la base de vos commentaires, vous êtes à la recherche d'un multidatatrigger.

<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> 
+0

Merci pour la réponse. Ma requête actuelle est, l'animation devrait se produire pour tous les éléments lorsque Mouse est quitté du contrôle. Mais cela ne devrait pas arriver quand il est en état Checed. – Prabu

+0

J'ai mis à jour ma réponse initiale. Vous devez utiliser un déclencheur multidata et définir des conditions supplémentaires. –

+0

Le problème avec MultiDataTrigger est que l'un ou l'autre changement de condition peut provoquer l'animation. Par exemple, dans ce cas, si la souris de l'utilisateur est ailleurs dans l'interface utilisateur et qu'elle change de données, la case à cocher est vérifiée, l'animation est lue même si la souris n'est pas proche. (Notez également que vous avez omis la deuxième condition dans votre MultiDataTrigger, que je suppose être '') –

21

Vous ne pouvez pas utiliser EventTrigger de cette manière. Le RoutedEventHandler de WPF qui appelle EventTriggers ne fournit aucun mécanisme pour rendre le déclencheur conditionnel, et vous ne pouvez pas résoudre ce problème en sous-classant TriggerAction car il n'existe aucune action Invoke() ou Execute() protégée à redéfinir. Toutefois, cela peut être fait assez facilement en utilisant une classe personnalisée. Voici comment il serait utilisé:

<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> 
    ... 

Et voici comment il serait mis en œuvre:

[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> {} 

Amusez-vous!

+0

Pourriez-vous s'il vous plaît un portable code ici? Le que vous avez posté ne fonctionne pas. Il serait d'une grande aide si vous postez un exemple d'application pour cela. – Prabu

+2

Quoi, vous voulez un code de fonctionnement réel? Ce sera un supplément de 10 $ s'il vous plaît ;-) Ce que j'ai écrit auparavant était juste au sommet de ma tête et a eu quelques fautes de frappe et un bug. Je l'ai déposé dans Visual Studio tout à l'heure, j'ai corrigé les fautes de frappe et je l'ai testé. J'ai mis à jour la réponse avec le code de travail. –

+1

Que voulez-vous dire? J'ai fait. J'ai répondu à votre question le 12 mai de la tête. Vous avez demandé le code de travail réel le 17 mai et j'ai posté le code de travail quelques heures plus tard. Je sais que le code que j'ai posté le 17 mai fonctionne parce que je l'ai testé. Essayez-le. Si vous obtenez des erreurs, s'il vous plaît laissez-moi savoir ce qu'ils sont. –

4

Voici ma version modifiée de Ray's answer, qui crée et attache des événements fictifs uniquement lorsque les déclencheurs source sont définis, plutôt que de le faire à chaque fois. Je pensais que ce serait mieux pour mon scénario que je soulève un événement sur des centaines d'articles, pas seulement un ou deux:

[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> { } 

Il est utilisé comme ceci:

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

La ligne

utilise le cadre réactif et certaines méthodes d'extension que j'ai écrites, fondamentalement, nous devons nous abonner à l'événement .DataContextChanged de l'objet cible, mais nous devons le faire avec un faible référenc e. Si vos objets ne jamais changer leur datacontext, alors vous aurez pas besoin de ce code à tous

+0

Cette modification est plus efficace mais notez qu'elle ne peut être utilisée que lorsque: 1. Un seul ConditionalEventTrigger est en cours de définition, et 2. La collection Actions n'est jamais modifiée après son initialisation. Ces deux problèmes peuvent être résolus avec du code supplémentaire. –

+0

Code supplémentaire permettant d'utiliser plusieurs ConditionalEventTriggers sur un seul objet: Gérez un pool d'objets RoutedEvent statiques. Chaque fois qu'un trigger factice est construit, sélectionnez un événement du pool qui n'est utilisé dans aucun EventTrigger sur l'objet. Si aucun événement n'existe, enregistrez un nouvel événement et ajoutez-le au pool. –

+0

Code supplémentaire permettant de modifier la collection d'actions: Implémentez-la en tant que ObservableCollection. Stockez une référence à dummyTrigger dans l'objet ConditionalEventTrigger. Lorsque la collection Action notifie le changement de propriété, effacez et remplissez à nouveau la liste d'actions dans dummyTrigger. –

7

C'est ce qui a fonctionné pour moi ...

Je voulais exécuter une animation basée sur la souris planant au-dessus un élément d'interface utilisateur et le propriétaire associé de l'élément d'interface utilisateur étant actifs (c'est-à-dire activés pour faire bouger un joueur). Pour prendre en charge ces exigences, j'ai utilisé la liaison de source relative pour surmonter le manque de prise en charge des conditions de déclenchement d'événement.

Exemple:

<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> 
0

Je sais que c'est un ancien poste, mais voici quelque chose qui a fonctionné pour moi quand je suis arrivé ici pour trouver des réponses.Fondamentalement, je voulais un panneau qui animerait du côté droit de l'écran sur la souris, puis revenez lorsque la souris est partie. Mais, seulement lorsque le panneau n'a pas été épinglé. La propriété IsShoppingCartPinned est présente sur mon ViewModel. En ce qui concerne votre scénario, vous pouvez remplacer la propriété IsShoppingCartPinned par votre propriété de case à cocher IsChecked et exécuter toute sorte d'animations sur les EventTriggers.

Voici le code:

<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>