#region File Description
//-----------------------------------------------------------------------------
// GraphicsDeviceControl.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.Xna.Framework.Graphics;
#endregion
namespace Darwinbots3.DrawingSurface
{
// 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;
///
/// 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.
///
abstract public class GraphicsDeviceControl : Control
{
#region Fields
// However many GraphicsDeviceControl instances you have, they all share
// the same underlying GraphicsDevice, managed by this helper service.
GraphicsDeviceService graphicsDeviceService;
#endregion
#region Properties
///
/// Gets a GraphicsDevice that can be used to draw onto this control.
///
public GraphicsDevice GraphicsDevice
{
get { return graphicsDeviceService.GraphicsDevice; }
}
///
/// 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.
///
public ServiceContainer Services
{
get { return services; }
}
ServiceContainer services = new ServiceContainer();
#endregion
#region Initialization
///
/// Initializes the control.
///
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);
// Register the service, so components like ContentManager can find it.
services.AddService(graphicsDeviceService);
// Give derived classes a chance to initialize themselves.
Initialize();
}
base.OnCreateControl();
}
///
/// Disposes the control.
///
protected override void Dispose(bool disposing)
{
if (graphicsDeviceService != null)
{
graphicsDeviceService.Release(disposing);
graphicsDeviceService = null;
}
base.Dispose(disposing);
}
#endregion
#region Paint
///
/// Redraws the control in response to a WinForms paint message.
///
protected override void OnPaint(PaintEventArgs e)
{
string beginDrawError = BeginDraw();
if (string.IsNullOrEmpty(beginDrawError))
{
// Draw the control using the GraphicsDevice.
Draw();
EndDraw();
}
else
{
// If BeginDraw failed, show an error message using System.Drawing.
PaintUsingSystemDrawing(e.Graphics, beginDrawError);
}
}
///
/// 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.
///
string BeginDraw()
{
// If we have no graphics device, we must be running in the designer.
if (graphicsDeviceService == null)
{
return Text + "\n\n" + GetType();
}
// Make sure the graphics device is big enough, and is not lost.
string deviceResetError = HandleDeviceReset();
if (!string.IsNullOrEmpty(deviceResetError))
{
return deviceResetError;
}
// 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.
Viewport viewport = new Viewport();
viewport.X = 0;
viewport.Y = 0;
viewport.Width = ClientSize.Width;
viewport.Height = ClientSize.Height;
viewport.MinDepth = 0;
viewport.MaxDepth = 1;
GraphicsDevice.Viewport = viewport;
return null;
}
///
/// 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.
///
void EndDraw()
{
try
{
Rectangle sourceRectangle = new Rectangle(0, 0, ClientSize.Width,
ClientSize.Height);
GraphicsDevice.Present(sourceRectangle, null, this.Handle);
}
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.
}
}
///
/// 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.
///
string HandleDeviceReset()
{
bool deviceNeedsReset = false;
switch (GraphicsDevice.GraphicsDeviceStatus)
{
case GraphicsDeviceStatus.Lost:
// If the graphics device is lost, we cannot use it at all.
return "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.
PresentationParameters pp = GraphicsDevice.PresentationParameters;
deviceNeedsReset = (ClientSize.Width > pp.BackBufferWidth) ||
(ClientSize.Height > pp.BackBufferHeight);
break;
}
// Do we need to reset the device?
if (deviceNeedsReset)
{
try
{
graphicsDeviceService.ResetDevice(ClientSize.Width,
ClientSize.Height);
}
catch (Exception e)
{
return "Graphics device reset failed\n\n" + e;
}
}
return null;
}
///
/// 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.
///
protected virtual void PaintUsingSystemDrawing(Graphics graphics, string text)
{
graphics.Clear(Color.CornflowerBlue);
using (Brush brush = new SolidBrush(Color.Black))
{
using (StringFormat format = new StringFormat())
{
format.Alignment = StringAlignment.Center;
format.LineAlignment = StringAlignment.Center;
graphics.DrawString(text, Font, brush, ClientRectangle, format);
}
}
}
///
/// 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.
///
protected override void OnPaintBackground(PaintEventArgs pevent)
{
}
#endregion
#region Abstract Methods
///
/// Derived classes override this to initialize their drawing code.
///
protected abstract void Initialize();
///
/// Derived classes override this to draw themselves using the GraphicsDevice.
///
protected abstract void Draw();
#endregion
}
}