#HoloLens : launch a 3D (Holograms) app from a 2D #XAML app and going back to it #UWP

, , 13 Comments

In this blog post I will show you how to create a 2D #XAML app which will be able to display a 3D view (holograms) and then how to go back to the 2D view from the 3D view.

We wil do this using DirectX and Unity.

As you can see in this video, the “context” of the leaved Windows is kept : the scroll position does not change.

Opening another Windows from a n UWP app

Since a long time now, you can open a new Windows from a Windows Store app (UWP or Universal). For instance, the Mail app does this to let you edit an email in another dedicated window.

To perform this, you have to ask the SDK to create a new View (CoreApplicationView) which maps to a Window (CoreWindow) and an associated Dispatcher. This last point is interesting because you have to be very careful, when you share your ViewModels between windows, to be on the good Dispatcher when raising INotifyPropertyChanged event or doing some UI-related work.

Here is the code to create a Window :

// récupération de l'id courant pour usage ultérieur
var appViewId = ApplicationView.GetForCurrentView().Id;

//Create a new view \o/
CoreApplicationView newCoreAppView = CoreApplication.CreateNewView();

await newCoreAppView.Dispatcher.RunAsync(
    Windows.UI.Core.CoreDispatcherPriority.Low,
     () =>
     {
         //Get the created Windows
         Window window = Window.Current;
         ApplicationView newAppView = ApplicationView.GetForCurrentView();

         // create a new frame and navigate to the page
         var secondFrame = new Frame();
         window.Content = secondFrame;
         secondFrame.Navigate(typeof(MainPage));

         // activate the new Window
         window.Activate();

         // make the new window standalone
         ApplicationViewSwitcher.TryShowAsStandaloneAsync(newAppView.Id,
             ViewSizePreference.UseMore, appViewId, ViewSizePreference.Default);
     });

By providing no argument to the CreateNewView method, we ask the XAML framework to create and manage a XAML UI.

We could also provide an argument of type IFrameworkViewSource to be able to have our own Window managed by our code. This is what DirectX does and it will let us create holograms !

Displaying a 3D DirectX view from the 2D view

By using the “HolographicDirectXApp” Visual Studio sample, I have all I need to create and display a 3D rotating cube by. The generated code creates an instance of IFrameworkView using DirectX. The sample use SharpDX, some C# classes and shaders that I can simply copy/Paste directly in a new XAML UWP project.

Capture

I then only have to use the previous snippet and ask it to use the DirectX AppView.

I have to carefully :

  • keep an instance of my AppViewSource : if it’s garbage collected, my 3D view will disappear.
  • Activate the created Window for the DirectX view.

Of course, as all 3D holographic view, my start screen and the 2D View will disappear to let only the 3D objects in my space.

Capture2

Displaying a 3D Unity view from the 2D view

An Unity app being an UWP app the code to write will be very similar. We will only have to customize the generated Unity code to stay in a 2D World instead of going directly to the 3D exclusive View.

To be in the right configuration, I generate a XAML project instead of a Direct3D player in Unity. I then have this :

  • Unity initialization is done in the App class.
  • A MainPage XAML app which finish the initialisation and is the first page navigated to.

To have a “standard” Xaml project, I then perform these modifications :

  • I create a XAML “BlankPage” named StartupPage.
  • I navigate to this page instead of MainPAge. I then stay in a 2D classic XAML context and I can build my application around it.
  • I move the use of the AppCallbacks class from the App.cs class to the existing MainPage.

 

The next steps are easy : I create a new Window, add a frame in it and navigate to the MainPage. I use exactly the same snippet as before and I only have to register an event handler to the activation of the created Window to be able to initialize Unity then. I also store the main view’s Id for further use.

private async Task CreateNewHoloWindowAsync()
{
    var appViewId = MainAppViewId = ApplicationView.GetForCurrentView().Id;
 
    var _newCoreAppView = CoreApplication.CreateNewView();
 
    await _newCoreAppView.Dispatcher
        .RunAsync(CoreDispatcherPriority.Low,
      async () =>
      {
          var frame = new Frame();
          Window.Current.Content = frame;
          frame.Navigate(typeof(MainPage));
 
          var res = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(
              ApplicationView.GetForCurrentView().Id,
              ViewSizePreference.Default, appViewId,
              ViewSizePreference.Default);
 
          _newCoreAppView.CoreWindow.Activated += WindowActivated;
 
          _newCoreAppView.CoreWindow.Activate();
      });
}
 
private void WindowActivated(object sender, WindowActivatedEventArgs e)
{
    if (e.WindowActivationState == CoreWindowActivationState.CodeActivated
        || e.WindowActivationState == CoreWindowActivationState.PointerActivated)
    {
        AppCallbacks.Instance.SetInitialViewActive();
        // Only need to mark initial activation once so unregister ourself
        CoreWindow coreWindowSender = sender as CoreWindow;
        coreWindowSender.Activated -= WindowActivated;
    }
}

Going back to the 2D View

To go back to the 2D XAML view, you have to use the ApplicationViewSwitcher class and ask it to go switch back to the main Window.

I provide my Unity’s code an Action (GoBackToEarth.CloseThisHolographicView) it can call when needed.

// let's capture the dispatcher of the current view
var dispatcher = Dispatcher;
 
GoBackToEarth.CloseThisHolographicView = () =>
{
    // be sure to be on the Window's Dispatcher
    dispatcher.RunIdleAsync(async _ =>
    {
        // go back to the main Window
        await ApplicationViewSwitcher.SwitchAsync(StartupPage.MainAppViewId);
 
        // we close the 3D holographic Window
        Window.Current.Close();
    });
};

Happy coding !

 

13 Responses

  1. Andy

    29/07/2016 17 h 30 min

    Hi, thanks for your post. Can you provide a little more info on going back to the 2D view? You indicate that you "provide your Unity code with an action", but the code for CloseThisHoloGraphicView is not Unity compatible? I would think this needs to be in MainPage.xaml.cs, but then how do you get the Unity app to call this? Thanks in advance.

  2. Andy

    02/08/2016 16 h 19 min

    Hi, thanks for a great post. This works great indeed. It only works once, however, meaning you can go from 2D to 3D and back, but then trying to go to the 3D view once more simply crashes the app. Do you have the same behaviour?

  3. cindy

    22/09/2016 7 h 19 min

    hello, it's so cool function, but can you share me with code?thank you so much:)

  4. Kirk

    07/10/2016 17 h 59 min

    Exactly what I been looking for and want to do. Had a conversation with a Unity dev long ago and the conclusion was that the shelling would be at best slow. What I'm still not sure of is the project type to use. Should it be a C# Holographic project template to start or the Unity XAML one? When I tried the Unity XAML one my Bing API async/await calls would produce object not set errors. Was thinking about a wrapper class for a workaround.

  5. Kirk

    09/10/2016 18 h 35 min

    Jonathan any chance we could get a Visual Studio solution showing how and where you put this together?

    Thanks,
    Kirk

  6. thewhiteambit

    28/10/2016 15 h 49 min

    ApplicationView.GetForCurrentView() has no overload for IFrameworkViewSource, so this is simply not possible as stated. Can you provide a working example of opening a view for IFrameworkViewSource from Xaml. Using CoreApplication.CreateNewView(IFrameworkViewSource) throws STA Thread exceptions.

  7. Vinothkumar

    17/11/2016 14 h 37 min

    Thanks for this awesome article. You saved my day.

    I'm able to switch from 2D XAML page to Unity scene. But I'm not able to switch back to 2D view from Unity's 3D view. Can you please tell me where should I place the code mentioned under "Going back to the 2D View" section?

    I understand this code should be placed in unity project. When I try to write the above code, unity is throwing this error.

    "The name 'Dispatcher' does not exist in the current context."

    Please help me in fixing this.

  8. BOUDY

    28/12/2016 16 h 42 min

    Hello,
    Tout d'abord merci pour ton post.
    Débutante en C# et sur Visual Studio un détail qui est certainement évident pour tous doit m'échapper pour réussir à implementer ce switch 2D – 3D dans mon projet sur Hololens. Aurais-tu un exemple de projet simple permettant d'afficher un objet 3D classique Unity avec seulement 2 boutons pour passer d'une vue UWP à une vue Unity ? peut-être via Github ?
    Merci par avance,

  9. Manivel

    02/01/2017 12 h 18 min

    Hi,

    Thanks for your article. It used me lot.
    I have some observations then below

    1. Navigate to 2D XAML to 3D page
    2. I can navigate from 3D to 2D XAML page, then I can see the 2D XAML page.
    3. Again i am trying to navigate from 2D XAML to 3D page.

    But it is not working properly.
    Please can you give some solution.

  10. Manivel

    03/01/2017 6 h 17 min

    Hi,

    I have implemented the above code in my application. it is working fine.
    But when I navigate from 2D XAML to 3D second time, app is crashing

  11. BOUDY

    03/01/2017 11 h 40 min

    Hello,
    How use the code "GoBackToEarth.CloseThisHolographicView" in Unity to going back to the 2D View ?
    Thanks to you reponse.

Comments are closed.