Here’s an updated version
#region File Description
//-----------------------------------------------------------------------------
// GraphicsDeviceControl.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.Xna.Framework.Graphics;
namespace XnaHosting
{
// System.Drawing and the XNA Framework both define Color and Rectangle
// types. To avoid conflicts, we specify exactly which ones to use.
using Color = System.Drawing.Color;
using Rectangle = Microsoft.Xna.Framework.Rectangle;
/// <summary>
/// Custom control uses the XNA Framework GraphicsDevice to render onto
/// a Windows Form. Derived classes can override the Initialize and Draw
/// methods to add their own drawing code.
/// </summary>
abstract public class GraphicsDeviceControl : Control
{
// However many GraphicsDeviceControl instances you have, they all share
// the same underlying GraphicsDevice, managed by this helper service.
GraphicsDeviceService graphicsDeviceService;
SwapChainRenderTarget _renderTarget;
/// <summary>
/// Gets a GraphicsDevice that can be used to draw onto this control.
/// </summary>
public GraphicsDevice GraphicsDevice
{
get { return graphicsDeviceService.GraphicsDevice; }
}
public RenderTarget2D DefaultRenderTarget { get { return _renderTarget; } }
/// <summary>
/// Gets an IServiceProvider containing our IGraphicsDeviceService.
/// This can be used with components such as the ContentManager,
/// which use this service to look up the GraphicsDevice.
/// </summary>
public ServiceContainer Services
{
get { return services; }
}
ServiceContainer services = new ServiceContainer();
public GraphicsDeviceControl(): base()
{
}
/// <summary>
/// Initializes the control.
/// </summary>
protected override void OnCreateControl()
{
// Don't initialize the graphics device if we are running in the designer.
if (!DesignMode)
{
graphicsDeviceService = GraphicsDeviceService.AddRef(Handle,
ClientSize.Width,
ClientSize.Height);
int w = Math.Max(ClientSize.Width, 1);
int h = Math.Max(ClientSize.Height,1);
_renderTarget = new SwapChainRenderTarget(GraphicsDevice, Handle, w, h);
// SetKeyboardInput(true);
// Register the service, so components like ContentManager can find it.
//services.AddService<IGraphicsDeviceService>(graphicsDeviceService);
// Give derived classes a chance to initialize themselves.
Initialize();
//Set the XNA mouse handing to use this window
//Mouse.WindowHandle = Handle;
return;
}
base.OnCreateControl();
}
public void SetKeyboardInput(bool enable)
{
var keyboardType = typeof(Microsoft.Xna.Framework.Input.Keyboard);
var methodInfo = keyboardType.GetMethod("SetActive", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
methodInfo.Invoke(null, new object[] { enable });
}
/// <summary>
/// Disposes the control.
/// </summary>
protected override void Dispose(bool disposing)
{
if (graphicsDeviceService != null)
{
graphicsDeviceService.Release(disposing);
graphicsDeviceService = null;
}
if (_renderTarget != null)
{
_renderTarget.Dispose();
_renderTarget = null;
}
base.Dispose(disposing);
}
/// <summary>
/// Redraws the control in response to a WinForms paint message.
/// </summary>
protected override void OnPaint(PaintEventArgs e)
{
// If we have no graphics device, we must be running in the designer.
if (graphicsDeviceService == null)
{
PaintUsingSystemDrawing(e.Graphics, Text + "\n\n" + GetType());
return;
}
try
{
//call drawing
BeginDraw();
// Draw the control using the GraphicsDevice.
OnDraw(EventArgs.Empty);
EndDraw();
}
catch (Exception ex)
{
PaintUsingSystemDrawing(e.Graphics, ex.Message + "\n\n" + ex.StackTrace);
}
return;
}
/// <summary>
/// Attempts to begin drawing the control. Returns an error message string
/// if this was not possible, which can happen if the graphics device is
/// lost, or if we are running inside the Form designer.
/// </summary>
internal void BeginDraw()
{
// Make sure the graphics device is big enough, and is not lost.
HandleDeviceReset();
GraphicsDevice.SetRenderTarget(_renderTarget);
// Many GraphicsDeviceControl instances can be sharing the same
// GraphicsDevice. The device backbuffer will be resized to fit the
// largest of these controls. But what if we are currently drawing
// a smaller control? To avoid unwanted stretching, we set the
// viewport to only use the top left portion of the full backbuffer.
if (this.Viewport.Width == 0 || this.Viewport.Height == 0)
throw new Exception("Viewport size cannot be Zero.");
GraphicsDevice.Viewport = this.Viewport;
return;
}
public Viewport Viewport
{
get
{
int w = Math.Max(ClientSize.Width, 1);
int h = Math.Max(ClientSize.Height, 1);
return new Viewport(0, 0, w, h);
}
}
/// <summary>
/// Ends drawing the control. This is called after derived classes
/// have finished their Draw method, and is responsible for presenting
/// the finished image onto the screen, using the appropriate WinForms
/// control handle to make sure it shows up in the right place.
/// </summary>
internal void EndDraw()
{
try
{
Rectangle sourceRectangle = new Rectangle(0, 0, ClientSize.Width, ClientSize.Height);
GraphicsDevice.SetRenderTarget(null);
_renderTarget.Present();
}
catch
{
// Present might throw if the device became lost while we were
// drawing. The lost device will be handled by the next BeginDraw,
// so we just swallow the exception.
}
}
/// <summary>
/// Helper used by BeginDraw. This checks the graphics device status,
/// making sure it is big enough for drawing the current control, and
/// that the device is not lost. Returns an error string if the device
/// could not be reset.
/// </summary>
void HandleDeviceReset()
{
bool deviceNeedsReset = false;
switch (GraphicsDevice.GraphicsDeviceStatus)
{
case GraphicsDeviceStatus.Lost:
// If the graphics device is lost, we cannot use it at all.
throw new Exception("Graphics device lost");
case GraphicsDeviceStatus.NotReset:
// If device is in the not-reset state, we should try to reset it.
deviceNeedsReset = true;
break;
default:
// If the device state is ok, check whether it is big enough.
int w = Math.Max(ClientSize.Width, 1);
int h = Math.Max(ClientSize.Height, 1);
deviceNeedsReset = (w != _renderTarget.Width) ||
(h != _renderTarget.Height);
break;
}
// Do we need to reset the device?
if (deviceNeedsReset)
{
try
{
int w = Math.Max(ClientSize.Width, 1);
int h = Math.Max(ClientSize.Height, 1);
graphicsDeviceService.ResetDevice(w, h);
//recreate window swapchain
_renderTarget.Dispose();
_renderTarget = new SwapChainRenderTarget(GraphicsDevice, Handle, w, h);
}
catch (Exception e)
{
throw new Exception("Graphics device reset failed\n\n", e);
}
}
return;
}
protected void ResetSwapChainRenderTarget()
{
if (_renderTarget != null)
_renderTarget.Dispose();
_renderTarget = new SwapChainRenderTarget(GraphicsDevice, Handle, ClientSize.Width, ClientSize.Height);
}
/// <summary>
/// If we do not have a valid graphics device (for instance if the device
/// is lost, or if we are running inside the Form designer), we must use
/// regular System.Drawing method to display a status message.
/// </summary>
protected virtual void PaintUsingSystemDrawing(Graphics graphics, string text)
{
graphics.Clear(Color.Black);
using (Brush brush = new SolidBrush(Color.White))
{
using (StringFormat format = new StringFormat())
{
format.Alignment = StringAlignment.Near;
format.LineAlignment = StringAlignment.Near;
graphics.DrawString(text, Font, brush, ClientRectangle, format);
}
}
}
/// <summary>
/// Ignores WinForms paint-background messages. The default implementation
/// would clear the control to the current background color, causing
/// flickering when our OnPaint implementation then immediately draws some
/// other color over the top using the XNA Framework GraphicsDevice.
/// </summary>
protected override void OnPaintBackground(PaintEventArgs pevent)
{
}
/// <summary>
/// Derived classes override this to initialize their drawing code.
/// </summary>
abstract protected void Initialize();
public event EventHandler Draw;
/// <summary>
/// Derived classes override this to draw themselves using the GraphicsDevice.
/// </summary>
protected virtual void OnDraw(EventArgs e)
{
EventHandler handler = Draw;
if (handler != null)
handler(this, e);
}
}
}
#region File Description
//-----------------------------------------------------------------------------
// GraphicsDeviceService.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using System.Threading;
using Microsoft.Xna.Framework.Graphics;
#endregion
// The IGraphicsDeviceService interface requires a DeviceCreated event, but we
// always just create the device inside our constructor, so we have no place to
// raise that event. The C# compiler warns us that the event is never used, but
// we don't care so we just disable this warning.
#pragma warning disable 67
namespace XnaHosting
{
/// <summary>
/// Helper class responsible for creating and managing the GraphicsDevice.
/// All GraphicsDeviceControl instances share the same GraphicsDeviceService,
/// so even though there can be many controls, there will only ever be a single
/// underlying GraphicsDevice. This implements the standard IGraphicsDeviceService
/// interface, which provides notification events for when the device is reset
/// or disposed.
/// </summary>
class GraphicsDeviceService : IGraphicsDeviceService
{
#region Fields
// Singleton device service instance.
static GraphicsDeviceService singletonInstance;
// Keep track of how many controls are sharing the singletonInstance.
static int referenceCount;
#endregion
/// <summary>
/// Constructor is private, because this is a singleton class:
/// client controls should use the public AddRef method instead.
/// </summary>
GraphicsDeviceService(IntPtr windowHandle, int width, int height)
{
parameters = new PresentationParameters();
parameters.BackBufferWidth = Math.Max(width, 1);
parameters.BackBufferHeight = Math.Max(height, 1);
parameters.BackBufferFormat = SurfaceFormat.Color;
parameters.DepthStencilFormat = DepthFormat.Depth24Stencil8;
parameters.DeviceWindowHandle = windowHandle;
parameters.PresentationInterval = PresentInterval.Immediate;
parameters.IsFullScreen = false;
graphicsDevice = new GraphicsDevice(GraphicsAdapter.DefaultAdapter,
#if DEFERRED
GraphicsProfile.HiDef,
#else
GraphicsProfile.Reach,
#endif
parameters);
return;
}
/// <summary>
/// Gets a reference to the singleton instance.
/// </summary>
public static GraphicsDeviceService AddRef(IntPtr windowHandle,
int width, int height)
{
// Increment the "how many controls sharing the device" reference count.
if (Interlocked.Increment(ref referenceCount) == 1)
{
// If this is the first control to start using the
// device, we must create the singleton instance.
singletonInstance = new GraphicsDeviceService(windowHandle,
width, height);
}
return singletonInstance;
}
/// <summary>
/// Releases a reference to the singleton instance.
/// </summary>
public void Release(bool disposing)
{
// Decrement the "how many controls sharing the device" reference count.
if (Interlocked.Decrement(ref referenceCount) == 0)
{
// If this is the last control to finish using the
// device, we should dispose the singleton instance.
if (disposing)
{
if (DeviceDisposing != null)
DeviceDisposing(this, EventArgs.Empty);
graphicsDevice.Dispose();
}
graphicsDevice = null;
}
}
/// <summary>
/// Resets the graphics device to whichever is bigger out of the specified
/// resolution or its current size. This behavior means the device will
/// demand-grow to the largest of all its GraphicsDeviceControl clients.
/// </summary>
public void ResetDevice(int width, int height)
{
if (DeviceResetting != null)
DeviceResetting(this, EventArgs.Empty);
parameters.BackBufferWidth = Math.Max(parameters.BackBufferWidth, width);
parameters.BackBufferHeight = Math.Max(parameters.BackBufferHeight, height);
graphicsDevice.Reset(parameters);
if (DeviceReset != null)
DeviceReset(this, EventArgs.Empty);
}
/// <summary>
/// Gets the current graphics device.
/// </summary>
public GraphicsDevice GraphicsDevice
{
get { return graphicsDevice; }
}
GraphicsDevice graphicsDevice;
// Store the current device settings.
PresentationParameters parameters;
// IGraphicsDeviceService events.
public event EventHandler<EventArgs> DeviceCreated;
public event EventHandler<EventArgs> DeviceDisposing;
public event EventHandler<EventArgs> DeviceReset;
public event EventHandler<EventArgs> DeviceResetting;
}
}
#region File Description
//-----------------------------------------------------------------------------
// ServiceContainer.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using System.Collections.Generic;
#endregion
namespace XnaHosting
{
/// <summary>
/// Container class implements the IServiceProvider interface. This is used
/// to pass shared services between different components, for instance the
/// ContentManager uses it to locate the IGraphicsDeviceService implementation.
/// </summary>
public class ServiceContainer : IServiceProvider
{
Dictionary<Type, object> services = new Dictionary<Type, object>();
/// <summary>
/// Adds a new service to the collection.
/// </summary>
public void AddService<T>(T service)
{
services.Add(typeof(T), service);
}
/// <summary>
/// Looks up the specified service.
/// </summary>
public object GetService(Type serviceType)
{
object service;
services.TryGetValue(serviceType, out service);
return service;
}
}
}