Click or drag to resize
DigitalRuneWindows Forms, WPF, UWP Interoperability

This topic gives a brief overview on interoperability with Windows Forms, the Windows Presentation Foundation (WPF) and the Universal Windows Platform (UWP).

This topic contains the following sections:

Interoperability overview

The namespace DigitalRune.Graphics.Interop provides controls that allow to present 3D graphics in regular Windows Forms, WPF and UWP applications. The interoperability classes were specifically designed to enable the following scenarios:

  • The application is a regular Windows Forms, WPF or UWP project. 3D graphics can be presented in one or several controls in the application.
  • The application is an XNA project. The game can run normally inside the default game window. The user can switch between windowed (desktop) and full-screen mode. From the running game, the user can switch to a game editor which is hosted inside a Windows Forms or WPF window. The game editor allows one or more independent views on the game world. While the game editor is visible, the game can keep running uninterrupted.
Tip Tip

For more details take a look at the various Interop samples for Windows Forms, WPF and UWP.

Launching a Windows Forms or WPF window

The following examples show how to start an external window, such as a game editor, from an XNA game.

The following example shows how to open a Windows Form.

C#
// Create a new Windows Forms window.
var form = new MyForm() { GraphicsServices = _graphicsManager };

// Make the window visible.
form.Show();

The following example shows to open a WPF window.

C#
var window = new WpfWindow { GraphicsService = _graphicsManager };

// Allow WPF window to receive keyboard events from Windows Forms.
ElementHost.EnableModelessKeyboardInterop(window);

window.Show();
Presenting 3D graphics inside a Windows Forms, WPF or UWP control

A control that shows a 3D scene of the XNA game is called a presentation target. The namespace includes following presentation target controls which can be added inside Windows Forms, WPF or UWP windows.

Presentation targets need to be registered in the graphics service (see property PresentationTargets). The following examples shows how to register and unregister presentation targets of a WPF window.

C#
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
using DigitalRune.Graphics;
using DigitalRune.Graphics.Interop;

namespace InteropSample
{
  // A WPF window with a presentation target for 3D graphics.
  public partial class MyWindow
  {
    public IGraphicsService GraphicsService { get; set; }

    public WpfWindow()
    {
      InitializeComponent();
      Loaded += OnLoaded;
    }

    private void OnLoaded(object sender, RoutedEventArgs eventArgs)
    {
      // Register render targets.
      if (GraphicsService != null)
        GraphicsService.PresentationTargets.Add(PresentationTarget);
    }

    protected override void OnClosing(CancelEventArgs eventArgs)
    {
      // Unregister render targets.
      if (GraphicsService != null)
        GraphicsService.PresentationTargets.Remove(PresentationTarget);

      base.OnClosing(eventArgs);
    }
  }
}
XAML
<Window x:Class="MyApplication.MyWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:dr="http://schemas.digitalrune.com/windows">
  <Grid>
      <!--  A WPF presentation target into which XNA graphics can be rendered:  -->
      <dr:ElementPresentationTarget x:Name="PresentationTarget" />
    </Grid>
  </Grid>
</Window>

The graphics service can render a scene into the registered presentation targets. In XNA game projects this is usually done in the Draw-method of the XNA Game class.

C#
protected override void Draw(GameTime gameTime)
{
  // Update graphics service.
  _graphicsManager.Update(gameTime.ElapsedGameTime);

  foreach (var presentationTarget in _graphicsManager.PresentationTargets)
  {
    // Prepare scene (camera, etc.) for presentation target.
    ...

    // Render scene into presentation target.
    _graphicsManager.Render(presentationTarget);
  }

  // Render scene into back buffer.
  _graphicsManager.Render(false);

  base.Draw(gameTime);
}

In regular Windows Forms, WPF or UWP applications a custom "game loop" can be created which handles the rendering of 3D graphics. Have a look at the included Interop samples for more details.

Caveats

The XNA Mouse class does not work in some interoperability scenarios. In this case you can read normal Windows Forms, WPF or UWP mouse events to handle mouse input.

Class WpfEnvironment
Note Note

The class WpfEnvironment is obsolete. It is not required anymore to use WpfEnvironment for WPF interoperability.

The class WpfEnvironment is a helper class that provides an execution environment for a WPF window that runs on a separate thread inside a Windows Forms application (such as the XNA game).

The following examples shows to open a WPF window using the WpfEnvironment.

C#
// The WpfEnvironment class create a separate thread which runs the WPF windows.
// (Redundant calls of Startup() are no problem.)
WpfEnvironment.Startup(Window.Handle);

// From the WpfEnvironment class we can get the Dispatcher. We can use it to 
// execute actions on the WPF thread. 
WpfEnvironment.Dispatcher.Invoke((Action)(() =>
{
  var window = new MyWindow() { GraphicsServices = _graphicsManager };
  window.Show();
}));

When the WpfEnvironment is used, Windows Forms and XNA runs on one thread, WPF runs on another thread. Care needs to be taken to avoid any cross-thread issues. All operations which manipulates the WPF controls must be executed on the WPF thread using WpfEnvironmentDispatcher, as shown in the example above. All operations which manipulate the state of Windows Forms controls or XNA must be executed on the Windows Forms thread. The following examples shows how to register and unregister presentation targets of a WPF window. The operations are explicitly invoked on the Windows Forms thread.

C#
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
using DigitalRune.Graphics;
using DigitalRune.Graphics.Interop;

namespace InteropSample
{
  // A WPF window with a presentation target for 3D graphics.
  public partial class MyWindow
  {
    public IGraphicsService GraphicsService { get; set; }

    public WpfWindow()
    {
      InitializeComponent();
      Loaded += OnLoaded;
    }

    private void OnLoaded(object sender, RoutedEventArgs eventArgs)
    {
      // Register render targets.
      WpfEnvironment.Form.BeginInvoke((Action)(() =>
      {
        if (GraphicsService != null)
          GraphicsService.PresentationTargets.Add(PresentationTarget);
      }));
    }

    protected override void OnClosing(CancelEventArgs eventArgs)
    {
      // Unregister render targets.
      WpfEnvironment.Form.BeginInvoke((Action)(() =>
      {
        if (GraphicsService != null)
          GraphicsService.PresentationTargets.Remove(PresentationTarget);
      }));

      base.OnClosing(eventArgs);
    }
  }
}