> ## Documentation Index
> Fetch the complete documentation index at: https://ahmed-walid.com/llms.txt
> Use this file to discover all available pages before exploring further.

# How to create your own NavigationTransitionInfo

> Create your own XAML page transitions

I always wondered how **NavigationTransitionInfo**-s work if they have no publicly exposed animation/navigation data.

Looking at the public definition of `NavigationTransitionInfo` WinRT class, it appears that it implements only 2 interfaces, `INavigationTransitionInfo` and `INavigationTransitionInfoOverrides`

`INavigationTransitionInfo` is totally empty

```csharp theme={null}
namespace Windows.UI.Xaml.Media.Animation
{    
    [ComImport]    
    [ExclusiveTo(typeof(NavigationTransitionInfo))]       
    [ContractVersion(typeof(UniversalApiContract), 65536u)]        
    [Windows.Foundation.Metadata.Guid(2846904465u, 44618, 17266, 134, 37, 33, 183, 168, 185, 140, 164)]    
    [WebHostHidden]
    internal interface INavigationTransitionInfo
    {
	
    }
}
```

`INavigationTransitionInfoOverrides` has 2 members

```csharp theme={null}
namespace Windows.UI.Xaml.Media.Animation
{    
    [ComImport]    
    [ExclusiveTo(typeof(NavigationTransitionInfo))]    
    [ContractVersion(typeof(UniversalApiContract), 65536u)]    
    [Windows.Foundation.Metadata.Guid(3645996650u, 43472, 19447, 157, 176, 70, 51, 166, 157, 175, 242)]    
    [WebHostHidden]    
    internal interface INavigationTransitionInfoOverrides    
    {        
        string GetNavigationStateCore();        
        void SetNavigationStateCore([In] string navigationState);    
    }
}
```

However, none of these 2 function seem to hold the animation data

So I guessed that there must be another hidden interface used to hold the animation data, so I assumed that it must be called something like `INavigationTransitionInfoPrivate` or `INavigationTransitionInfoInternal`

So I started searching for the dll that implements the **NavigationTransitionInfo** WinRT class, after some search in Registry I found that it's in [***Windows.UI.Xaml.Phone***](http://Windows.UI.Xaml.Phone)***.dll***

I dropped that dll in IDA Pro and started searching for "`INavigationTransitionInfoPrivate`"

And *YES!* there was actually an interface with that name!

<img src="https://mintcdn.com/ahmedwalid/TEZFgSLe1CaUbGbq/images/IDA1.png?fit=max&auto=format&n=TEZFgSLe1CaUbGbq&q=85&s=1ebcca92050da30009a42b1b27699d46" alt="IDA1" width="1024" height="632" data-path="images/IDA1.png" />

Here comes [ADeltaX](https://blog.adeltax.com/)'s big help, he showed me how to get the ***GUID*** and how to locate the ***vftable*** and the interface member functions

Big thanks to ADeltaX!

Now, we have the ***GUID*** (`1ab93e41-46d2-4a19-b20a-e8d042fe5740`) and member functions and their signatures:

```csharp theme={null}
CreateStoryboards([in] Windows::UI::Xaml::UIElement* pElement, 
[in] Windows::UI::Xaml::Media::Animation::NavigationTrigger trigger, 
[in] Windows::Foundation::Collections::IVector<Windows.UI::Xaml::Media::Animation::Storyboard*>* pStoryboards);
```

Then we had to find out what **NavigationTrigger** is, ADeltaX was able to find it in ***Windows.UI.Xaml*** public symbols:

```cpp theme={null}
enum Windows::UI::Xaml::Media::Animation::NavigationTrigger 
{
    NavigationTrigger_NavigatingAway = 0x0,
    NavigationTrigger_NavigatingTo = 0x1,
    NavigationTrigger_BackNavigatingAway = 0x2,
    NavigationTrigger_BackNavigatingTo = 0x3
}
```

So now we can write some code!

Wrote the com import and ***enum*** definition code:

```csharp theme={null}
public enum NavigationTrigger 
{
    NavigatingAway = 0x0,
    NavigatingTo = 0x1,
    BackNavigatingAway = 0x2,
    BackNavigatingTo = 0x3
};

[ComImport, Guid("1ab93e41-46d2-4a19-b20a-e8d042fe5740")]
[InterfaceType(ComInterfaceType.InterfaceIsIInspectable)]
public interface INavigationTransitionInfoPrivate 
{
    void CreateStoryboards(UIElement pElement, 
                           NavigationTrigger trigger, 
                           IList<Storyboard> pStoryboards);
}
```

Created custom **NavigationTransitionInfo** class called ***CustomPageTransitionTest*** that inherits from **NavigationTransitionInfo** and **INavigationTransitionInfoPrivate**:

```csharp theme={null}
public class CustomPageTransitionTest : NavigationTransitionInfo, INavigationTransitionInfoPrivate
{
    void INavigationTransitionInfoPrivate.CreateStoryboards(UIElement pElement, NavigationTrigger trigger, IList<Storyboard> pStoryboards)
    {
        switch (trigger)
        {
            case NavigationTrigger.NavigatingAway:
                break;
            case NavigationTrigger.NavigatingTo:
                break;
            case NavigationTrigger.BackNavigatingAway:
                break;
            case NavigationTrigger.BackNavigatingTo:
                break;
        }
    }

    //For some reasons, this is required in some cases...    
    protected override string GetNavigationStateCore()
    {
        return base.GetNavigationStateCore() ?? "0";
    }
}
```

Wrote simple animation code:

```csharp expandable theme={null}
void INavigationTransitionInfoPrivate.CreateStoryboards(UIElement pElement, NavigationTrigger trigger, IList<Storyboard> pStoryboards)
{
    switch (trigger)
    {
        case NavigationTrigger.NavigatingAway:
        {
            CompositeTransform transform = new CompositeTransform();
            pElement.RenderTransformOrigin = new Point(0.5, 0.5);
            pElement.RenderTransform = transform;

            Storyboard board = new Storyboard();

            DoubleAnimation anim1 = new DoubleAnimation();
            anim1.From = 0;
            anim1.To = 360;
            anim1.Duration = TimeSpan.FromSeconds(3);
            anim1.EnableDependentAnimation = false;
            Storyboard.SetTargetProperty(anim1, "Rotation");
            Storyboard.SetTarget(anim1, transform);
            board.Children.Add(anim1);

            DoubleAnimation anim2 = new DoubleAnimation();
            anim2.From = 1;
            anim2.To = 0;
            anim2.Duration = TimeSpan.FromSeconds(3);
            anim2.EnableDependentAnimation = false;
            Storyboard.SetTargetProperty(anim2, "ScaleX");
            Storyboard.SetTarget(anim2, transform);
            board.Children.Add(anim2);

            DoubleAnimation anim3 = new DoubleAnimation();
            anim3.From = 1;
            anim3.To = 0;
            anim3.Duration = TimeSpan.FromSeconds(3);
            anim3.EnableDependentAnimation = false;
            Storyboard.SetTargetProperty(anim3, "ScaleY");
            Storyboard.SetTarget(anim3, transform);
            board.Children.Add(anim3);

            DoubleAnimation anim4 = new DoubleAnimation();
            anim4.From = 1;
            anim4.To = 0;
            anim4.Duration = TimeSpan.FromSeconds(3);
            anim4.EasingFunction = new SineEase();
            anim4.EnableDependentAnimation = true;
            Storyboard.SetTargetProperty(anim4, "Opacity");
            Storyboard.SetTarget(anim4, pElement);
            board.Children.Add(anim4);

            pStoryboards.Add(board);
            break;
        }
        case NavigationTrigger.NavigatingTo:
            break;
        case NavigationTrigger.BackNavigatingAway:
            break;
        case NavigationTrigger.BackNavigatingTo:
            break;
    }
}
```

Everything looks perfect, right? ***No***

There's no exception and the navigation waits for the **Storyboard** to finish but... no animation shows on screen.

It turns out that the API doesn't animate the provided **UIElement** directly but another **IFrameworkElement** ***TargetElement*** (private property?) of **UIElement** returned using the helper `GetLogicalTargetElement` private function instead.

Unfortunately I wasn't able to RE the function because many information about it was stripped out from the public symbols.

But luckily some **NavigationTransitionInfo**-s use the ***PropertyPath*** `(UIElement.TransitionTarget)` instead so we can use that.

So using that ***PropertyPath*** I was able to rewrite the code to use it instead:

```csharp theme={null}
void INavigationTransitionInfoPrivate.CreateStoryboards(UIElement pElement, NavigationTrigger trigger, IList<Storyboard> pStoryboards)
{
    switch (trigger)
    {
        case NavigationTrigger.NavigatingAway:
        {
            Storyboard board = new Storyboard();

            PointAnimation pAnim = new PointAnimation();
            pAnim.From = new Point(0, 0);
            pAnim.To = new Point(0.5, 0.5);
            pAnim.Duration = TimeSpan.FromMilliseconds(1);
            pAnim.EnableDependentAnimation = true;
            Storyboard.SetTargetProperty(pAnim, "(UIElement.TransitionTarget).TransformOrigin");
            Storyboard.SetTarget(pAnim, pElement);
            board.Children.Add(pAnim);

            DoubleAnimation anim1 = new DoubleAnimation();
            anim1.From = 0;
            anim1.To = 360;
            anim1.Duration = TimeSpan.FromSeconds(3);
            anim1.EnableDependentAnimation = false;
            Storyboard.SetTargetProperty(anim1, "(UIElement.TransitionTarget).(TransitionTarget.CompositeTransform).Rotation");
            Storyboard.SetTarget(anim1, pElement);
            board.Children.Add(anim1);

            DoubleAnimation anim2 = new DoubleAnimation();
            anim2.From = 1;
            anim2.To = 0;
            anim2.Duration = TimeSpan.FromSeconds(3);
            anim2.EnableDependentAnimation = false;
            Storyboard.SetTargetProperty(anim2, "(UIElement.TransitionTarget).(TransitionTarget.CompositeTransform).ScaleX");
            Storyboard.SetTarget(anim2, pElement);
            board.Children.Add(anim2);

            DoubleAnimation anim3 = new DoubleAnimation();
            anim3.From = 1;
            anim3.To = 0;
            anim3.Duration = TimeSpan.FromSeconds(3);
            anim3.EnableDependentAnimation = false;
            Storyboard.SetTargetProperty(anim3, "(UIElement.TransitionTarget).(TransitionTarget.CompositeTransform).ScaleY");
            Storyboard.SetTarget(anim3, pElement);
            board.Children.Add(anim3);

            DoubleAnimation anim4 = new DoubleAnimation();
            anim4.From = 1;
            anim4.To = 0;
            anim4.Duration = TimeSpan.FromSeconds(3);
            anim4.EasingFunction = new SineEase();
            anim4.EnableDependentAnimation = true;
            Storyboard.SetTargetProperty(anim4, "(UIElement.TransitionTarget).Opacity");
            Storyboard.SetTarget(anim4, pElement);
            board.Children.Add(anim4);

            pStoryboards.Add(board);
            break;
        }
        case NavigationTrigger.NavigatingTo:
            break;
        case NavigationTrigger.BackNavigatingAway:
            break;
        case NavigationTrigger.BackNavigatingTo:
            break;
    }
```

### **The moment of the truth**

did it work? let's find out...

***YES!*** it did!

<video controls className="w-full aspect-video rounded-xl" src="https://user-images.githubusercontent.com/34550324/165122816-fe884695-be0a-4234-b658-3b3b5f3a4f6c.mp4" type="video/mp4" />
