views:

4187

answers:

2

I want to be able to set a property with an EventTrigger, there's a number of problems with this.

1) EventTriggers only support Actions, so I must use a storyBoard to set my properties.

2) Once I use a storyboard, I have two options:

  • Stop: Once the animation has stopped the value reverts back to before the animation started
  • HoldEnd: This locks the property, so that neither code, nor user interaction can change the property that the animation is holding.

In the below example, I want to set the IsChecked property to False when the button is clicked and I want the user to be able to change the IsChecked and/or I want to be able to change the property in code.

Example:

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

I realize that I can use the "Completed" event after the storyboard completes to set the value to False. However, in this instance I want to contain the logic within the XAML, as this logic will be used on a custom control and is only specific to the UI.

+1  A: 

As much as I love XAML, for this kinds of tasks I switch to code behind. Attached behaviors are a good pattern for this. Keep in mind, Expression Blend 3 provides a standard way to program and use behaviors. There are a few existing ones on the Expression Community Site.

Sergey Aldoukhov
A: 

Stopping the Storyboard can be done in the code behind, or the xaml, depending on where the need comes from.

If the EventTrigger is moved outside of the button, then we can go ahead and target it with another EventTrigger that will tell the storyboard to stop. When the storyboard is stopped in this manner it will not revert to the previous value.

Here I've moved the Button.Click EventTrigger to a surrounding StackPanel and added a new EventTrigger on the the CheckBox.Click to stop the Button's storyboard when the CheckBox is clicked. This lets us check and uncheck the CheckBox when it is clicked on and gives us the desired unchecking behavior from the button as well.

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

To stop the storyboard in the code behind, we will have to do something slightly different. The third button provides the method where we will stop the storyboard and set the IsChecked property back to true through code.

We can't call myStoryboard.Stop() because we did not begin the Storyboard through the code setting the isControllable parameter. Instead, we can remove the Storyboard. To do this we need the FrameworkElement that the storyboard exists on, in this case our StackPanel. Once the storyboard is removed, we can once again set the IsChecked property with it persisting to the UI.

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