2

I have a frame. I switch pages with this line:

FrameName.Content = new PageName();

I want a storyboard to begin when the page has changed, and I want to do it in XAML, and not in code-behind. I have tried the following code:

<Frame.Triggers>
    <EventTrigger RoutedEvent="ContentChanged">
        <BeginStoryboard Storyboard="{StaticResource storyboardName}" />
    </EventTrigger>
</Frame.Triggers>

After searching a bit I realized there is no built-in routed event of that nature. The first answer here suggests that

The most dynamic approach is to simply derive your own label control that provides a ContentChanged event.

I have tried to implement the code in this answer:

using System.Windows;
using System.Windows.Controls;

namespace ContentChangedTest
{
    class MyFrame : Frame
    {
        public event DependencyPropertyChangedEventHandler ContentChanged;

        static MyFrame()
        {
            ContentProperty.OverrideMetadata(typeof(MyFrame), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnContentChanged)));
        }

        private static void OnContentChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
        {
            MyFrame frame = dp as MyFrame;

            if (frame.ContentChanged != null)
            {
                DependencyPropertyChangedEventArgs args = new DependencyPropertyChangedEventArgs(ContentProperty, e.OldValue, e.NewValue);
                frame.ContentChanged(frame, args);
            }
        }
    }
}

In XAML I use it look like this:

<local:MyFrame ContentChanged="MyFrame_ContentChanged" />

The problem is that eventually I need to create an event handler MyFrame_ContentChanged in the code-behind. Is there a way to do this in pure XAML? For example - can I convert the ContentChanged dependency property to some sort of routed event?

Michael Haddad
  • 4,085
  • 7
  • 42
  • 82
  • I am very new to WPF, so it is more than likely that this is a stupid question. However, I would be grateful for a serious answer. – Michael Haddad Apr 03 '16 at 10:37
  • 1
    In order to use it in `EventTrigger` you need to use a `RoutedEvent`. Routed events are defined similarly to dependency properties. A good starting point for you would be this tutorial: [How to: Create a Custom Routed Event](https://msdn.microsoft.com/en-us/library/ms752288(v=vs.100).aspx). – Grx70 Apr 03 '16 at 11:04
  • @Grx70, Thanks. Could you please demonstrate how it works and how can I integrate that with the code above? – Michael Haddad Apr 03 '16 at 11:22

1 Answers1

4

In order to use events with EventTriggers they should be routed events. Routed events are defined in a way similar to dependency properties. Here's a quick tutorial on how to get started: How to: Create a Custom Routed Event.

Here's an example of a class deriving from ContentControl which defines a ContentChanged event:

public class MyContentControl : ContentControl
{
    public static readonly RoutedEvent ContentChangedEvent 
        = EventManager.RegisterRoutedEvent(
            "ContentChanged",
            RoutingStrategy.Bubble,
            typeof(RoutedEventHandler), 
            typeof(MyContentControl));

    public event RoutedEventHandler ContentChanged
    {
        add { AddHandler(ContentChangedEvent, value); }
        remove { RemoveHandler(ContentChangedEvent, value); }
    }

    protected override void OnContentChanged(object oldContent, object newContent)
    {
        base.OnContentChanged(oldContent, newContent);
        RaiseEvent(new RoutedEventArgs(ContentChangedEvent, this));
    }
}

I'm not yet sure why, but while testing this line worked inside a Style, but threw an exception while used in the control's triggers collection directly:

<EventTrigger RoutedEvent="ContentChanged">...</EventTrigger>

In order to make it work in this situation I had to specify a fully qualified event path:

<EventTrigger RoutedEvent="local:MyContentControl.ContentChanged">...</EventTrigger>
Grx70
  • 10,041
  • 1
  • 40
  • 55