2009-06-03 18 views
61

Quiero ser capaz de establecer una propiedad con un EventTrigger, hay una serie de problemas con esto.Establecer una propiedad con un EventTrigger

1) EventTriggers solo admite acciones, por lo que debo usar un storyboard para establecer mis propiedades.

2) Una vez que utilizo un guión gráfico, tengo dos opciones:

  • Stop: Una vez que la animación se ha detenido el valor vuelve a ser antes de la animación comenzó
  • HoldEnd: Esto bloquea la propiedad, por lo que ni el código ni la interacción del usuario pueden cambiar la propiedad que está conteniendo la animación.

En el siguiente ejemplo, quiero establecer la propiedad IsChecked en False cuando se hace clic en el botón y quiero que el usuario sea capaz de cambiar el IsChecked y/o quiero ser capaz de cambiar la propiedad de código.

Ejemplo:

<EventTrigger 
    SourceName="myButton" 
    RoutedEvent="Button.Click"> 
    <EventTrigger.Actions> 
     <BeginStoryboard> 
      <Storyboard> 
       <BooleanAnimationUsingKeyFrames 
        Storyboard.TargetName="myCheckBox" 
        Storyboard.TargetProperty="IsChecked" 
        FillBehavior="Stop"> 
        <DiscreteBooleanKeyFrame 
         KeyTime="00:00:00" 
         Value="False" /> 
       </BooleanAnimationUsingKeyFrames> 
      </Storyboard> 
     </BeginStoryboard> 
    </EventTrigger.Actions> 
</EventTrigger> 

Soy consciente de que puedo utilizar el evento "Completado" después del guión gráfico completa para establecer el valor False. Sin embargo, en este caso, quiero incluir la lógica dentro del XAML, ya que esta lógica se usará en un control personalizado y solo será específica para la UI.

Respuesta

6

Detener el guion gráfico se puede hacer en el código subyacente, o el xaml, dependiendo de dónde provenga la necesidad.

Si EventTrigger se mueve fuera del botón, podemos seguir adelante y apuntarlo con otro EventTrigger que le dirá al guión gráfico que se detenga. Cuando el guión gráfico se detiene de esta manera, no volverá al valor anterior.

Aquí he movido el botón. Haga clic en EventTrigger en un StackPanel circundante y agregue un EventTrigger nuevo en el CheckBox. Haga clic para detener el guión gráfico del botón cuando se hace clic en el CheckBox. Esto nos permite comprobar y desmarcar el CheckBox cuando se hace clic en él y también nos proporciona el comportamiento de desmarque deseado del botón.

<StackPanel x:Name="myStackPanel"> 

     <CheckBox x:Name="myCheckBox" 
        Content="My CheckBox" /> 

     <Button Content="Click to Uncheck" 
       x:Name="myUncheckButton" /> 

     <Button Content="Click to check the box in code." 
       Click="OnClick" /> 

     <StackPanel.Triggers> 

      <EventTrigger RoutedEvent="Button.Click" 
          SourceName="myUncheckButton"> 
       <EventTrigger.Actions> 
        <BeginStoryboard x:Name="myBeginStoryboard"> 
         <Storyboard x:Name="myStoryboard"> 
          <BooleanAnimationUsingKeyFrames Storyboard.TargetName="myCheckBox" 
                  Storyboard.TargetProperty="IsChecked"> 
           <DiscreteBooleanKeyFrame KeyTime="00:00:00" 
                 Value="False" /> 
          </BooleanAnimationUsingKeyFrames> 
         </Storyboard> 
        </BeginStoryboard> 
       </EventTrigger.Actions> 
      </EventTrigger> 

      <EventTrigger RoutedEvent="CheckBox.Click" 
          SourceName="myCheckBox"> 
       <EventTrigger.Actions> 
        <StopStoryboard BeginStoryboardName="myBeginStoryboard" /> 
       </EventTrigger.Actions> 
      </EventTrigger> 

     </StackPanel.Triggers> 
    </StackPanel> 

Para detener el guión gráfico en el código subyacente, tendremos que hacer algo ligeramente diferente. El tercer botón proporciona el método donde detendremos el guión gráfico y restableceremos la propiedad IsChecked a True a través del código.

No podemos llamar a myStoryboard.Stop() porque no comenzamos el guión gráfico a través del código que establece el parámetro isControllable. En cambio, podemos eliminar el Storyboard. Para hacer esto necesitamos el FrameworkElement en el que existe el guión gráfico, en este caso nuestro StackPanel. Una vez que se elimina el guión gráfico, una vez más podemos configurar la propiedad IsChecked persistiendo en la interfaz de usuario.

private void OnClick(object sender, RoutedEventArgs e) 
    { 
     myStoryboard.Remove(myStackPanel); 
     myCheckBox.IsChecked = true; 
    } 
30

Simplemente crea tu propia acción.

namespace WpfUtil 
{ 
    using System.Reflection; 
    using System.Windows; 
    using System.Windows.Interactivity; 


    /// <summary> 
    /// Sets the designated property to the supplied value. TargetObject 
    /// optionally designates the object on which to set the property. If 
    /// TargetObject is not supplied then the property is set on the object 
    /// to which the trigger is attached. 
    /// </summary> 
    public class SetPropertyAction : TriggerAction<FrameworkElement> 
    { 
     // PropertyName DependencyProperty. 

     /// <summary> 
     /// The property to be executed in response to the trigger. 
     /// </summary> 
     public string PropertyName 
     { 
      get { return (string)GetValue(PropertyNameProperty); } 
      set { SetValue(PropertyNameProperty, value); } 
     } 

     public static readonly DependencyProperty PropertyNameProperty 
      = DependencyProperty.Register("PropertyName", typeof(string), 
      typeof(SetPropertyAction)); 


     // PropertyValue DependencyProperty. 

     /// <summary> 
     /// The value to set the property to. 
     /// </summary> 
     public object PropertyValue 
     { 
      get { return GetValue(PropertyValueProperty); } 
      set { SetValue(PropertyValueProperty, value); } 
     } 

     public static readonly DependencyProperty PropertyValueProperty 
      = DependencyProperty.Register("PropertyValue", typeof(object), 
      typeof(SetPropertyAction)); 


     // TargetObject DependencyProperty. 

     /// <summary> 
     /// Specifies the object upon which to set the property. 
     /// </summary> 
     public object TargetObject 
     { 
      get { return GetValue(TargetObjectProperty); } 
      set { SetValue(TargetObjectProperty, value); } 
     } 

     public static readonly DependencyProperty TargetObjectProperty 
      = DependencyProperty.Register("TargetObject", typeof(object), 
      typeof(SetPropertyAction)); 


     // Private Implementation. 

     protected override void Invoke(object parameter) 
     { 
      object target = TargetObject ?? AssociatedObject; 
      PropertyInfo propertyInfo = target.GetType().GetProperty(
       PropertyName, 
       BindingFlags.Instance|BindingFlags.Public 
       |BindingFlags.NonPublic|BindingFlags.InvokeMethod); 

      propertyInfo.SetValue(target, PropertyValue); 
     } 
    } 
} 

En este caso estoy vinculando a una propiedad llamada DialogResult en mi viewmodel.

<Grid> 

    <Button> 
     <i:Interaction.Triggers> 
      <i:EventTrigger EventName="Click"> 
       <wpf:SetPropertyAction PropertyName="DialogResult" TargetObject="{Binding}" 
             PropertyValue="{x:Static mvvm:DialogResult.Cancel}"/> 
      </i:EventTrigger> 
     </i:Interaction.Triggers> 
     Cancel 
    </Button> 

</Grid> 

5

modifiqué solución de neutrinos para hacer el XAML se ven menos detallado al especificar el valor:

Lo siento por no hay fotos de la xaml rendido, simplemente imaginar una [=] botón de hamburguesa en el que hace clic y se convierte en [< -] un botón de retroceso y también alterna la visibilidad de una cuadrícula.

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 

... 

<Grid> 
    <Button x:Name="optionsButton"> 
     <i:Interaction.Triggers> 
      <i:EventTrigger EventName="Click"> 
       <local:SetterAction PropertyName="Visibility" Value="Collapsed" /> 
       <local:SetterAction PropertyName="Visibility" TargetObject="{Binding ElementName=optionsBackButton}" Value="Visible" /> 
       <local:SetterAction PropertyName="Visibility" TargetObject="{Binding ElementName=optionsPanel}" Value="Visible" /> 
      </i:EventTrigger> 
     </i:Interaction.Triggers> 

     <glyphs:Hamburger Width="10" Height="10" /> 
    </Button> 

    <Button x:Name="optionsBackButton" Visibility="Collapsed"> 
     <i:Interaction.Triggers> 
      <i:EventTrigger EventName="Click"> 
       <local:SetterAction PropertyName="Visibility" Value="Collapsed" /> 
       <local:SetterAction PropertyName="Visibility" TargetObject="{Binding ElementName=optionsButton}" Value="Visible" /> 
       <local:SetterAction PropertyName="Visibility" TargetObject="{Binding ElementName=optionsPanel}" Value="Collapsed" /> 
      </i:EventTrigger> 
     </i:Interaction.Triggers> 

     <glyphs:Back Width="12" Height="11" /> 
    </Button> 
</Grid> 

... 

<Grid Grid.RowSpan="2" x:Name="optionsPanel" Visibility="Collapsed"> 

</Grid> 

También puede especificar los valores de esta manera al igual que en la solución de Neutrinos:

<Button x:Name="optionsButton"> 
    <i:Interaction.Triggers> 
     <i:EventTrigger EventName="Click"> 
      <local:SetterAction PropertyName="Visibility" Value="{x:Static Visibility.Collapsed}" /> 
      <local:SetterAction PropertyName="Visibility" TargetObject="{Binding ElementName=optionsBackButton}" Value="{x:Static Visibility.Visible}" /> 
      <local:SetterAction PropertyName="Visibility" TargetObject="{Binding ElementName=optionsPanel}" Value="{x:Static Visibility.Visible}" /> 
     </i:EventTrigger> 
    </i:Interaction.Triggers> 

    <glyphs:Hamburger Width="10" Height="10" /> 
</Button> 

Y aquí está el código.

using System; 
using System.ComponentModel; 
using System.Reflection; 
using System.Windows; 
using System.Windows.Interactivity; 

namespace Mvvm.Actions 
{ 
    /// <summary> 
    /// Sets a specified property to a value when invoked. 
    /// </summary> 
    public class SetterAction : TargetedTriggerAction<FrameworkElement> 
    { 
     #region Properties 

     #region PropertyName 

     /// <summary> 
     /// Property that is being set by this setter. 
     /// </summary> 
     public string PropertyName 
     { 
      get { return (string)GetValue(PropertyNameProperty); } 
      set { SetValue(PropertyNameProperty, value); } 
     } 

     public static readonly DependencyProperty PropertyNameProperty = 
      DependencyProperty.Register("PropertyName", typeof(string), typeof(SetterAction), 
      new PropertyMetadata(String.Empty)); 

     #endregion 

     #region Value 

     /// <summary> 
     /// Property value that is being set by this setter. 
     /// </summary> 
     public object Value 
     { 
      get { return (object)GetValue(ValueProperty); } 
      set { SetValue(ValueProperty, value); } 
     } 

     public static readonly DependencyProperty ValueProperty = 
      DependencyProperty.Register("Value", typeof(object), typeof(SetterAction), 
      new PropertyMetadata(null)); 

     #endregion 

     #endregion 

     #region Overrides 

     protected override void Invoke(object parameter) 
     { 
      var target = TargetObject ?? AssociatedObject; 

      var targetType = target.GetType(); 

      var property = targetType.GetProperty(PropertyName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance); 
      if (property == null) 
       throw new ArgumentException(String.Format("Property not found: {0}", PropertyName)); 

      if (property.CanWrite == false) 
       throw new ArgumentException(String.Format("Property is not settable: {0}", PropertyName)); 

      object convertedValue; 

      if (Value == null) 
       convertedValue = null; 

      else 
      { 
       var valueType = Value.GetType(); 
       var propertyType = property.PropertyType; 

       if (valueType == propertyType) 
        convertedValue = Value; 

       else 
       { 
        var propertyConverter = TypeDescriptor.GetConverter(propertyType); 

        if (propertyConverter.CanConvertFrom(valueType)) 
         convertedValue = propertyConverter.ConvertFrom(Value); 

        else if (valueType.IsSubclassOf(propertyType)) 
         convertedValue = Value; 

        else 
         throw new ArgumentException(String.Format("Cannot convert type '{0}' to '{1}'.", valueType, propertyType)); 
       } 
      } 

      property.SetValue(target, convertedValue); 
     } 

     #endregion 
    } 
} 
+0

TargetObject siempre es nula cuando el uso de '' – huoxudong125

+0

@Neutrino Tengo una pregunta sobre 'TargetedTriggerAction' https://stackoverflow.com/questions/40478172/why-the-targetedtriggeractions-targetobject-is-always-null – huoxudong125

Cuestiones relacionadas