using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX;
using System.IO;
using System.Threading;
namespace MdxWpfInteroperability
{
[TemplatePart(Name = "PART_D3dRegion", Type = typeof(Rectangle))]
public class D3dHost : Control, IDisposable
{
private Rectangle _d3dRegion;
public D3dHost()
{
TryUseD3DImageBeforeUsingMemory = true;
TryUseMemoryBeforeUsingFilesSystem = true;
FreeMemoryBeforeUpdateD3dRegion = true;
MillisecondsForDispatcherInvokeTimeout = 200;
Loaded += OnLoaded;
Unloaded += OnUnloaded;
}
protected virtual void OnLoaded(object sender, RoutedEventArgs e)
{
try
{
if (_d3dDevice == null)
{
InitDevice();
}
}
catch
{
}
}
protected virtual void OnUnloaded(object sender, RoutedEventArgs e)
{
Dispose();
}
#region properties
#region D3dHostingPanel
private System.Windows.Forms.Panel _d3dHostingPanel;
protected System.Windows.Forms.Panel D3dHostingPanel
{
get
{
if (_d3dHostingPanel == null)
{
_d3dHostingPanel = new System.Windows.Forms.Panel();
int surfaceWidth = (int)D3dSurfaceWidth;
int surfaceHeight = (int)D3dSurfaceHeight;
_d3dHostingPanel.Width = (surfaceWidth > 0) ? surfaceWidth : 1;
_d3dHostingPanel.Height = (surfaceHeight > 0) ? surfaceHeight : 1;
}
return _d3dHostingPanel;
}
}
#endregion
#region D3dDevice
private Device _d3dDevice;
public Device D3dDevice
{
get
{
if (_d3dDevice == null)
{
InitDevice();
}
return _d3dDevice;
}
}
protected virtual void InitDevice()
{
try
{
ReleaseDevice();
PresentParameters presentParams = new PresentParameters();
presentParams.Windowed = true;
presentParams.SwapEffect = D3dSwapEffect;
presentParams.EnableAutoDepthStencil = D3dEnableAutoDepthStencil;
presentParams.AutoDepthStencilFormat = D3dAutoDepthStencilFormat;
_d3dDevice = new Device(0, D3dDeviceType, D3dHostingPanel, D3dCreateFlags, presentParams);
}
catch (Exception ex)
{
// Raise a failure event.
D3dDeviceFailureEventArgs initFailureArgs =
new D3dDeviceFailureEventArgs(D3dDeviceInitFailureEvent)
{
FailureException = ex
};
RaiseEvent(initFailureArgs);
}
}
protected virtual void ReleaseDevice()
{
if (_d3dDevice != null)
{
try
{
_d3dDevice.Dispose();
}
catch
{
}
finally
{
_d3dDevice = null;
}
}
}
protected void HandleD3dDeviceFailure(Exception ex)
{
D3dDeviceFailureEventArgs beforeFailureArgs =
new D3dDeviceFailureEventArgs(BeforeHandlingD3dDeviceFailureEvent)
{
FailureException = ex
};
RaiseEvent(beforeFailureArgs);
OnD3dDeviceFailure(ex);
D3dDeviceFailureEventArgs afterFailureArgs =
new D3dDeviceFailureEventArgs(AfterHandlingD3dDeviceFailureEvent)
{
FailureException = ex
};
RaiseEvent(afterFailureArgs);
}
protected virtual void OnD3dDeviceFailure(Exception ex)
{
// Restart the device.
InitDevice();
}
#endregion
#region D3dDeviceType
public DeviceType D3dDeviceType
{
get { return (DeviceType)GetValue(D3dDeviceTypeProperty); }
set { SetValue(D3dDeviceTypeProperty, value); }
}
public static readonly DependencyProperty D3dDeviceTypeProperty =
DependencyProperty.Register("D3dDeviceType", typeof(DeviceType), typeof(D3dHost),
new UIPropertyMetadata(DeviceType.Hardware, OnD3dDeviceTypeChanged));
private static void OnD3dDeviceTypeChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
D3dHost dh = sender as D3dHost;
if (dh == null)
{
return;
}
if (dh._d3dDevice != null)
{
// The device has been created with a different value. So, recreate it.
dh.InitDevice();
}
}
#endregion
#region D3dCreateFlags
public CreateFlags D3dCreateFlags
{
get { return (CreateFlags)GetValue(D3dCreateFlagsProperty); }
set { SetValue(D3dCreateFlagsProperty, value); }
}
public static readonly DependencyProperty D3dCreateFlagsProperty =
DependencyProperty.Register("D3dCreateFlags", typeof(CreateFlags), typeof(D3dHost),
new UIPropertyMetadata(CreateFlags.SoftwareVertexProcessing, OnD3dCreateFlagsChanged));
private static void OnD3dCreateFlagsChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
D3dHost dh = sender as D3dHost;
if (dh == null)
{
return;
}
if (dh._d3dDevice != null)
{
// The device has been created with a different value. So, recreate it.
dh.InitDevice();
}
}
#endregion
#region D3dSwapEffect
public SwapEffect D3dSwapEffect
{
get { return (SwapEffect)GetValue(D3dSwapEffectProperty); }
set { SetValue(D3dSwapEffectProperty, value); }
}
public static readonly DependencyProperty D3dSwapEffectProperty =
DependencyProperty.Register("D3dSwapEffect", typeof(SwapEffect), typeof(D3dHost),
new UIPropertyMetadata(SwapEffect.Discard, OnD3dSwapEffectChanged));
private static void OnD3dSwapEffectChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
D3dHost dh = sender as D3dHost;
if (dh == null)
{
return;
}
if (dh._d3dDevice != null)
{
// The device has been created with a different value. So, recreate it.
dh.InitDevice();
}
}
#endregion
#region D3dEnableAutoDepthStencil
public bool D3dEnableAutoDepthStencil
{
get { return (bool)GetValue(D3dEnableAutoDepthStencilProperty); }
set { SetValue(D3dEnableAutoDepthStencilProperty, value); }
}
public static readonly DependencyProperty D3dEnableAutoDepthStencilProperty =
DependencyProperty.Register("D3dEnableAutoDepthStencil", typeof(bool), typeof(D3dHost),
new UIPropertyMetadata(true, OnD3dEnableAutoDepthStencilChanged));
private static void OnD3dEnableAutoDepthStencilChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
D3dHost dh = sender as D3dHost;
if (dh == null)
{
return;
}
if (dh._d3dDevice != null)
{
// The device has been created with a different value. So, recreate it.
dh.InitDevice();
}
}
#endregion
#region D3dAutoDepthStencilFormat
public DepthFormat D3dAutoDepthStencilFormat
{
get { return (DepthFormat)GetValue(D3dAutoDepthStencilFormatProperty); }
set { SetValue(D3dAutoDepthStencilFormatProperty, value); }
}
public static readonly DependencyProperty D3dAutoDepthStencilFormatProperty =
DependencyProperty.Register("D3dAutoDepthStencilFormat", typeof(DepthFormat), typeof(D3dHost),
new UIPropertyMetadata(DepthFormat.D16, OnD3dAutoDepthStencilFormatChanged));
private static void OnD3dAutoDepthStencilFormatChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
D3dHost dh = sender as D3dHost;
if (dh == null)
{
return;
}
if (dh._d3dDevice != null)
{
// The device has been created with a different value. So, recreate it.
dh.InitDevice();
}
}
#endregion
#region D3dSurfaceWidth
public double D3dSurfaceWidth
{
get { return (double)GetValue(D3dSurfaceWidthProperty); }
set { SetValue(D3dSurfaceWidthProperty, value); }
}
public static readonly DependencyProperty D3dSurfaceWidthProperty =
DependencyProperty.Register("D3dSurfaceWidth", typeof(double), typeof(D3dHost),
new UIPropertyMetadata(1000.0, OnD3dSurfaceWidthChanged));
private static void OnD3dSurfaceWidthChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
D3dHost dh = sender as D3dHost;
if (dh == null)
{
return;
}
dh.UpdateDeviceWidth();
}
private void UpdateDeviceWidth()
{
int surfaceWidth = (int)D3dSurfaceWidth;
D3dHostingPanel.Width = (surfaceWidth > 0) ? surfaceWidth : 1;
}
#endregion
#region D3dSurfaceHeight
public double D3dSurfaceHeight
{
get { return (double)GetValue(D3dSurfaceHeightProperty); }
set { SetValue(D3dSurfaceHeightProperty, value); }
}
public static readonly DependencyProperty D3dSurfaceHeightProperty =
DependencyProperty.Register("D3dSurfaceHeight", typeof(double), typeof(D3dHost),
new UIPropertyMetadata(1000.0, OnD3dSurfaceHeightChanged));
private static void OnD3dSurfaceHeightChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
D3dHost dh = sender as D3dHost;
if (dh == null)
{
return;
}
dh.UpdateDeviceHeight();
}
private void UpdateDeviceHeight()
{
int surfaceHeight = (int)D3dSurfaceHeight;
D3dHostingPanel.Height = (surfaceHeight > 0) ? surfaceHeight : 1;
}
#endregion
#region D3dRegionActualWidth
public double D3dRegionActualWidth
{
get { return (double)GetValue(D3dRegionActualWidthProperty); }
private set { SetValue(D3dRegionActualWidthProperty, value); }
}
public static readonly DependencyProperty D3dRegionActualWidthProperty =
DependencyProperty.Register("D3dRegionActualWidth", typeof(double), typeof(D3dHost),
new UIPropertyMetadata(0.0));
#endregion
#region D3dRegionActualHeight
public double D3dRegionActualHeight
{
get { return (double)GetValue(D3dRegionActualHeightProperty); }
private set { SetValue(D3dRegionActualHeightProperty, value); }
}
public static readonly DependencyProperty D3dRegionActualHeightProperty =
DependencyProperty.Register("D3dRegionActualHeight", typeof(double), typeof(D3dHost),
new UIPropertyMetadata(0.0));
#endregion
#region D3dRegion
protected Rectangle D3dRegion { get { return _d3dRegion; } }
#endregion
#region TryUseD3DImageBeforeUsingMemory
public bool TryUseD3DImageBeforeUsingMemory { get; set; }
#endregion
#region TryUseMemoryBeforeUsingFilesSystem
public bool TryUseMemoryBeforeUsingFilesSystem { get; set; }
#endregion
#region FreeMemoryBeforeUpdateD3dRegion
public bool FreeMemoryBeforeUpdateD3dRegion { get; set; }
#endregion
#region MillisecondsForDispatcherInvokeTimeout
public double MillisecondsForDispatcherInvokeTimeout { get; set; }
#endregion
#endregion
#region Events
#region BeforeHandlingD3dDeviceFailure
public static readonly RoutedEvent BeforeHandlingD3dDeviceFailureEvent = EventManager.RegisterRoutedEvent(
"BeforeHandlingD3dDeviceFailure", RoutingStrategy.Bubble, typeof(D3dDeviceFailureEventHandler), typeof(D3dHost));
public event D3dDeviceFailureEventHandler BeforeHandlingD3dDeviceFailure
{
add { AddHandler(BeforeHandlingD3dDeviceFailureEvent, value); }
remove { RemoveHandler(BeforeHandlingD3dDeviceFailureEvent, value); }
}
#endregion
#region AfterHandlingD3dDeviceFailure
public static readonly RoutedEvent AfterHandlingD3dDeviceFailureEvent = EventManager.RegisterRoutedEvent(
"AfterHandlingD3dDeviceFailure", RoutingStrategy.Bubble, typeof(D3dDeviceFailureEventHandler), typeof(D3dHost));
public event D3dDeviceFailureEventHandler AfterHandlingD3dDeviceFailure
{
add { AddHandler(AfterHandlingD3dDeviceFailureEvent, value); }
remove { RemoveHandler(AfterHandlingD3dDeviceFailureEvent, value); }
}
#endregion
#region D3dDeviceInitFailure
public static readonly RoutedEvent D3dDeviceInitFailureEvent = EventManager.RegisterRoutedEvent(
"D3dDeviceInitFailure", RoutingStrategy.Bubble, typeof(D3dDeviceFailureEventHandler), typeof(D3dHost));
public event D3dDeviceFailureEventHandler D3dDeviceInitFailure
{
add { AddHandler(D3dDeviceInitFailureEvent, value); }
remove { RemoveHandler(D3dDeviceInitFailureEvent, value); }
}
#endregion
#region D3dRegionSizeChanged
public static readonly RoutedEvent D3dRegionSizeChangedEvent = EventManager.RegisterRoutedEvent(
"D3dRegionSizeChanged", RoutingStrategy.Bubble, typeof(SizeChangedEventHandler), typeof(D3dHost));
public event SizeChangedEventHandler D3dRegionSizeChanged
{
add { AddHandler(D3dRegionSizeChangedEvent, value); }
remove { RemoveHandler(D3dRegionSizeChangedEvent, value); }
}
#endregion
#region D3dSurfaceGotMouseCapture
public static readonly RoutedEvent D3dSurfaceGotMouseCaptureEvent = EventManager.RegisterRoutedEvent(
"D3dSurfaceGotMouseCapture", RoutingStrategy.Bubble, typeof(D3dSurfaceMouseEventHandler), typeof(D3dHost));
public event D3dSurfaceMouseEventHandler D3dSurfaceGotMouseCapture
{
add { AddHandler(D3dSurfaceGotMouseCaptureEvent, value); }
remove { RemoveHandler(D3dSurfaceGotMouseCaptureEvent, value); }
}
#endregion
#region D3dSurfaceLostMouseCapture
public static readonly RoutedEvent D3dSurfaceLostMouseCaptureEvent = EventManager.RegisterRoutedEvent(
"D3dSurfaceLostMouseCapture", RoutingStrategy.Bubble, typeof(D3dSurfaceMouseEventHandler), typeof(D3dHost));
public event D3dSurfaceMouseEventHandler D3dSurfaceLostMouseCapture
{
add { AddHandler(D3dSurfaceLostMouseCaptureEvent, value); }
remove { RemoveHandler(D3dSurfaceLostMouseCaptureEvent, value); }
}
#endregion
#region D3dSurfaceMouseEnter
public static readonly RoutedEvent D3dSurfaceMouseEnterEvent = EventManager.RegisterRoutedEvent(
"D3dSurfaceMouseEnter", RoutingStrategy.Bubble, typeof(D3dSurfaceMouseEventHandler), typeof(D3dHost));
public event D3dSurfaceMouseEventHandler D3dSurfaceMouseEnter
{
add { AddHandler(D3dSurfaceMouseEnterEvent, value); }
remove { RemoveHandler(D3dSurfaceMouseEnterEvent, value); }
}
#endregion
#region D3dSurfaceMouseLeave
public static readonly RoutedEvent D3dSurfaceMouseLeaveEvent = EventManager.RegisterRoutedEvent(
"D3dSurfaceMouseLeave", RoutingStrategy.Bubble, typeof(D3dSurfaceMouseEventHandler), typeof(D3dHost));
public event D3dSurfaceMouseEventHandler D3dSurfaceMouseLeave
{
add { AddHandler(D3dSurfaceMouseLeaveEvent, value); }
remove { RemoveHandler(D3dSurfaceMouseLeaveEvent, value); }
}
#endregion
#region D3dSurfaceMouseMove
public static readonly RoutedEvent D3dSurfaceMouseMoveEvent = EventManager.RegisterRoutedEvent(
"D3dSurfaceMouseMove", RoutingStrategy.Bubble, typeof(D3dSurfaceMouseEventHandler), typeof(D3dHost));
public event D3dSurfaceMouseEventHandler D3dSurfaceMouseMove
{
add { AddHandler(D3dSurfaceMouseMoveEvent, value); }
remove { RemoveHandler(D3dSurfaceMouseMoveEvent, value); }
}
#endregion
#region D3dSurfaceMouseDown
public static readonly RoutedEvent D3dSurfaceMouseDownEvent = EventManager.RegisterRoutedEvent(
"D3dSurfaceMouseDown", RoutingStrategy.Bubble, typeof(D3dSurfaceMouseButtonEventHandler), typeof(D3dHost));
public event D3dSurfaceMouseButtonEventHandler D3dSurfaceMouseDown
{
add { AddHandler(D3dSurfaceMouseDownEvent, value); }
remove { RemoveHandler(D3dSurfaceMouseDownEvent, value); }
}
#endregion
#region D3dSurfaceMouseLeftButtonDown
public static readonly RoutedEvent D3dSurfaceMouseLeftButtonDownEvent = EventManager.RegisterRoutedEvent(
"D3dSurfaceMouseLeftButtonDown", RoutingStrategy.Bubble, typeof(D3dSurfaceMouseButtonEventHandler), typeof(D3dHost));
public event D3dSurfaceMouseButtonEventHandler D3dSurfaceMouseLeftButtonDown
{
add { AddHandler(D3dSurfaceMouseLeftButtonDownEvent, value); }
remove { RemoveHandler(D3dSurfaceMouseLeftButtonDownEvent, value); }
}
#endregion
#region D3dSurfaceMouseLeftButtonUp
public static readonly RoutedEvent D3dSurfaceMouseLeftButtonUpEvent = EventManager.RegisterRoutedEvent(
"D3dSurfaceMouseLeftButtonUp", RoutingStrategy.Bubble, typeof(D3dSurfaceMouseButtonEventHandler), typeof(D3dHost));
public event D3dSurfaceMouseButtonEventHandler D3dSurfaceMouseLeftButtonUp
{
add { AddHandler(D3dSurfaceMouseLeftButtonUpEvent, value); }
remove { RemoveHandler(D3dSurfaceMouseLeftButtonUpEvent, value); }
}
#endregion
#region D3dSurfaceMouseRightButtonDown
public static readonly RoutedEvent D3dSurfaceMouseRightButtonDownEvent = EventManager.RegisterRoutedEvent(
"D3dSurfaceMouseRightButtonDown", RoutingStrategy.Bubble, typeof(D3dSurfaceMouseButtonEventHandler), typeof(D3dHost));
public event D3dSurfaceMouseButtonEventHandler D3dSurfaceMouseRightButtonDown
{
add { AddHandler(D3dSurfaceMouseRightButtonDownEvent, value); }
remove { RemoveHandler(D3dSurfaceMouseRightButtonDownEvent, value); }
}
#endregion
#region D3dSurfaceMouseRightButtonUp
public static readonly RoutedEvent D3dSurfaceMouseRightButtonUpEvent = EventManager.RegisterRoutedEvent(
"D3dSurfaceMouseRightButtonUp", RoutingStrategy.Bubble, typeof(D3dSurfaceMouseButtonEventHandler), typeof(D3dHost));
public event D3dSurfaceMouseButtonEventHandler D3dSurfaceMouseRightButtonUp
{
add { AddHandler(D3dSurfaceMouseRightButtonUpEvent, value); }
remove { RemoveHandler(D3dSurfaceMouseRightButtonUpEvent, value); }
}
#endregion
#region D3dSurfaceMouseUp
public static readonly RoutedEvent D3dSurfaceMouseUpEvent = EventManager.RegisterRoutedEvent(
"D3dSurfaceMouseUp", RoutingStrategy.Bubble, typeof(D3dSurfaceMouseButtonEventHandler), typeof(D3dHost));
public event D3dSurfaceMouseButtonEventHandler D3dSurfaceMouseUp
{
add { AddHandler(D3dSurfaceMouseUpEvent, value); }
remove { RemoveHandler(D3dSurfaceMouseUpEvent, value); }
}
#endregion
#region D3dSurfaceMouseWheel
public static readonly RoutedEvent D3dSurfaceMouseWheelEvent = EventManager.RegisterRoutedEvent(
"D3dSurfaceMouseWheel", RoutingStrategy.Bubble, typeof(D3dSurfaceMouseWheelEventHandler), typeof(D3dHost));
public event D3dSurfaceMouseWheelEventHandler D3dSurfaceMouseWheel
{
add { AddHandler(D3dSurfaceMouseWheelEvent, value); }
remove { RemoveHandler(D3dSurfaceMouseWheelEvent, value); }
}
#endregion
#region PreviewD3dSurfaceMouseDown
public static readonly RoutedEvent PreviewD3dSurfaceMouseDownEvent = EventManager.RegisterRoutedEvent(
"PreviewD3dSurfaceMouseDown", RoutingStrategy.Tunnel, typeof(D3dSurfaceMouseButtonEventHandler), typeof(D3dHost));
public event D3dSurfaceMouseButtonEventHandler PreviewD3dSurfaceMouseDown
{
add { AddHandler(PreviewD3dSurfaceMouseDownEvent, value); }
remove { RemoveHandler(PreviewD3dSurfaceMouseDownEvent, value); }
}
#endregion
#region PreviewD3dSurfaceMouseLeftButtonDown
public static readonly RoutedEvent PreviewD3dSurfaceMouseLeftButtonDownEvent = EventManager.RegisterRoutedEvent(
"PreviewD3dSurfaceMouseLeftButtonDown", RoutingStrategy.Tunnel, typeof(D3dSurfaceMouseButtonEventHandler), typeof(D3dHost));
public event D3dSurfaceMouseButtonEventHandler PreviewD3dSurfaceMouseLeftButtonDown
{
add { AddHandler(PreviewD3dSurfaceMouseLeftButtonDownEvent, value); }
remove { RemoveHandler(PreviewD3dSurfaceMouseLeftButtonDownEvent, value); }
}
#endregion
#region PreviewD3dSurfaceMouseLeftButtonUp
public static readonly RoutedEvent PreviewD3dSurfaceMouseLeftButtonUpEvent = EventManager.RegisterRoutedEvent(
"PreviewD3dSurfaceMouseLeftButtonUp", RoutingStrategy.Tunnel, typeof(D3dSurfaceMouseButtonEventHandler), typeof(D3dHost));
public event D3dSurfaceMouseButtonEventHandler PreviewD3dSurfaceMouseLeftButtonUp
{
add { AddHandler(PreviewD3dSurfaceMouseLeftButtonUpEvent, value); }
remove { RemoveHandler(PreviewD3dSurfaceMouseLeftButtonUpEvent, value); }
}
#endregion
#region PreviewD3dSurfaceMouseMove
public static readonly RoutedEvent PreviewD3dSurfaceMouseMoveEvent = EventManager.RegisterRoutedEvent(
"PreviewD3dSurfaceMouseMove", RoutingStrategy.Tunnel, typeof(D3dSurfaceMouseEventHandler), typeof(D3dHost));
public event D3dSurfaceMouseEventHandler PreviewD3dSurfaceMouseMove
{
add { AddHandler(PreviewD3dSurfaceMouseMoveEvent, value); }
remove { RemoveHandler(PreviewD3dSurfaceMouseMoveEvent, value); }
}
#endregion
#region PreviewD3dSurfaceMouseRightButtonDown
public static readonly RoutedEvent PreviewD3dSurfaceMouseRightButtonDownEvent = EventManager.RegisterRoutedEvent(
"PreviewD3dSurfaceMouseRightButtonDown", RoutingStrategy.Tunnel, typeof(D3dSurfaceMouseButtonEventHandler), typeof(D3dHost));
public event D3dSurfaceMouseButtonEventHandler PreviewD3dSurfaceMouseRightButtonDown
{
add { AddHandler(PreviewD3dSurfaceMouseRightButtonDownEvent, value); }
remove { RemoveHandler(PreviewD3dSurfaceMouseRightButtonDownEvent, value); }
}
#endregion
#region PreviewD3dSurfaceMouseRightButtonUp
public static readonly RoutedEvent PreviewD3dSurfaceMouseRightButtonUpEvent = EventManager.RegisterRoutedEvent(
"PreviewD3dSurfaceMouseRightButtonUp", RoutingStrategy.Tunnel, typeof(D3dSurfaceMouseButtonEventHandler), typeof(D3dHost));
public event D3dSurfaceMouseButtonEventHandler PreviewD3dSurfaceMouseRightButtonUp
{
add { AddHandler(PreviewD3dSurfaceMouseRightButtonUpEvent, value); }
remove { RemoveHandler(PreviewD3dSurfaceMouseRightButtonUpEvent, value); }
}
#endregion
#region PreviewD3dSurfaceMouseUp
public static readonly RoutedEvent PreviewD3dSurfaceMouseUpEvent = EventManager.RegisterRoutedEvent(
"PreviewD3dSurfaceMouseUp", RoutingStrategy.Tunnel, typeof(D3dSurfaceMouseButtonEventHandler), typeof(D3dHost));
public event D3dSurfaceMouseButtonEventHandler PreviewD3dSurfaceMouseUp
{
add { AddHandler(PreviewD3dSurfaceMouseUpEvent, value); }
remove { RemoveHandler(PreviewD3dSurfaceMouseUpEvent, value); }
}
#endregion
#region PreviewD3dSurfaceMouseWheel
public static readonly RoutedEvent PreviewD3dSurfaceMouseWheelEvent = EventManager.RegisterRoutedEvent(
"PreviewD3dSurfaceMouseWheel", RoutingStrategy.Tunnel, typeof(D3dSurfaceMouseWheelEventHandler), typeof(D3dHost));
public event D3dSurfaceMouseWheelEventHandler PreviewD3dSurfaceMouseWheel
{
add { AddHandler(PreviewD3dSurfaceMouseWheelEvent, value); }
remove { RemoveHandler(PreviewD3dSurfaceMouseWheelEvent, value); }
}
#endregion
#endregion
public void BeginDrawing()
{
// Lock the D3dHostingPanel, for ensuring that the scene is fully rendered.
Monitor.Enter(D3dHostingPanel);
}
public void EndDrawing()
{
// Unlock the D3dHostingPanel, for letting the scene to be presented.
Monitor.Exit(D3dHostingPanel);
// Present the scene.
InvalidateD3dRegion();
}
#region InvalidateD3dRegion
public void InvalidateD3dRegion()
{
if (_d3dRegion == null)
{
return;
}
// Start the thread that updates the Fill of the 3D region, if it is needed.
if (_updateD3dRegionThread == null)
{
StartUpdateD3dRegionThread();
}
// Indicate that the Fill of the 3D region is invalid.
_updateD3dRegionEvent.Set();
}
#region UpdateD3dRegion
private Thread _updateD3dRegionThread = null;
private bool _continueUpdateD3dRegionThread;
private AutoResetEvent _updateD3dRegionEvent = new AutoResetEvent(false);
private bool _isMemoryFreeNeeded = false;
private bool? _isSetD3dRegionFillUsingD3DImageSupported = null;
private bool? _isSetD3dRegionFillUsingMemorySupported = null;
private void UpdateD3dRegion()
{
if (_d3dRegion == null)
{
return;
}
//Lock the D3dHostingPanel, for waiting to the scene to be fully rendered.
Monitor.Enter(D3dHostingPanel);
if (FreeMemoryBeforeUpdateD3dRegion || _isMemoryFreeNeeded)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
try
{
// Get the device's back-buffer.
Surface s = D3dDevice.GetBackBuffer(0, 0, BackBufferType.Mono);
if (TryUseD3DImageBeforeUsingMemory && _isSetD3dRegionFillUsingD3DImageSupported != false)
{
SetD3dRegionFillUsingD3DImage(s);
}
else if (TryUseMemoryBeforeUsingFilesSystem && _isSetD3dRegionFillUsingMemorySupported != false)
{
SetD3dRegionFillUsingMemory(s);
}
else
{
SetD3dRegionFillUsingFile(s);
}
}
catch (Exception ex)
{
Dispatcher.Invoke(new ThreadStart(() => HandleD3dDeviceFailure(ex)),
TimeSpan.FromMilliseconds(MillisecondsForDispatcherInvokeTimeout));
}
finally
{
// Unlock the D3dHostingPanel, for letting the scene to be rendered.
Monitor.Exit(D3dHostingPanel);
}
}
private void StartUpdateD3dRegionThread()
{
if (_updateD3dRegionThread == null)
{
_continueUpdateD3dRegionThread = true;
_updateD3dRegionThread = new Thread(new ThreadStart(() =>
{
while (_continueUpdateD3dRegionThread)
{
_updateD3dRegionEvent.WaitOne();
if (_continueUpdateD3dRegionThread)
{
UpdateD3dRegion();
}
}
}));
_updateD3dRegionThread.Start();
}
}
private void StopUpdateD3dRegionThread()
{
if (_updateD3dRegionThread != null)
{
_continueUpdateD3dRegionThread = false;
_updateD3dRegionEvent.Set();
_updateD3dRegionThread.Join();
_updateD3dRegionThread = null;
}
}
#endregion
#region SetD3dRegionFillUsingD3DImage
// First approach for setting the Fill of the 3D region, can be using a D3DImage.
// For presenting a DirectX scene using a D3DImage,
// we can set the D3DImage's back-buffer with the surface's pointer.
// But, If we set the D3DImage (that holds the surface's pointer) as the Fill of the 3D region,
// every change to the surface, can be reflected directly to the Fill of the 3D region.
// Therefore, in order to update the Fill of the 3D region,
// only when the scene has been completely rendered,
// we set the Fill of the 3D region with a copy of the D3Dimage's back-buffer.
// Since the D3DImage.CopyBackBuffer method is protected,
// we create another class for getting the copy of the back-buffer (D3DImageEx).
protected class D3DImageEx : D3DImage
{
public D3DImageEx()
{
}
public D3DImageEx(double width, double height)
: base(width, height)
{
}
public BitmapSource GetBackBufferCopy()
{
return CopyBackBuffer();
}
}
private void SetD3dRegionFillUsingD3DImage(Surface s)
{
bool isD3dRegionUpdateNeeded = true;
// Update the Fill of the 3D region, in the UI thread.
Dispatcher.Invoke(
new ThreadStart(() =>
{
try
{
// Create a D3DImage that holds the surface's pointer.
D3DImageEx di = new D3DImageEx(D3dSurfaceWidth, D3dSurfaceHeight);
SetD3dImageBackBuffer(di, s);
// Set the Fill of the 3D region, to the D3DImage's back-buffer's copy.
BitmapSource bs = di.GetBackBufferCopy();
_d3dRegion.Fill = new ImageBrush(bs);
isD3dRegionUpdateNeeded = false;
_isSetD3dRegionFillUsingD3DImageSupported = true;
}
catch (Exception ex)
{
if (_isSetD3dRegionFillUsingD3DImageSupported != true)
{
// The update using D3DImage isn't supported...
_isSetD3dRegionFillUsingD3DImageSupported = false;
}
else
{
HandleD3dDeviceFailure(ex);
isD3dRegionUpdateNeeded = false;
}
}
}), TimeSpan.FromMilliseconds(MillisecondsForDispatcherInvokeTimeout));
if (isD3dRegionUpdateNeeded && _continueUpdateD3dRegionThread)
{
InvalidateD3dRegion();
}
}
private void SetD3dImageBackBuffer(D3DImage di, Surface s)
{
if (di == null || s == null)
{
return;
}
IntPtr backBuffer;
unsafe
{
backBuffer = new IntPtr(s.UnmanagedComPointer);
}
di.Lock();
di.SetBackBuffer(D3DResourceType.IDirect3DSurface9, backBuffer);
di.AddDirtyRect(new Int32Rect(0, 0, di.PixelWidth,
di.PixelHeight));
di.Unlock();
}
#endregion
#region SetD3dRegionFillUsingMemory
// A second approach for setting the Fill of the 3D region, can be using a buffer in the memory.
private GraphicsStream _d3dGraphicsStream;
private void SetD3dRegionFillUsingMemory(Surface s)
{
GraphicsStream oldGraphicsStream = _d3dGraphicsStream;
// Store the back-buffer as an image in the memory.
GraphicsStream newGraphicsStream = SurfaceLoader.SaveToStream(ImageFileFormat.Bmp, s);
newGraphicsStream.Seek(0, System.IO.SeekOrigin.Begin);
lock (_d3dRegion)
{
_d3dGraphicsStream = newGraphicsStream;
}
// Update the Fill of the 3D region, in the UI thread.
Dispatcher.BeginInvoke(new ThreadStart(() =>
{
lock (_d3dRegion)
{
if (_continueUpdateD3dRegionThread)
{
try
{
// Create an ImageSource that contains the image of the back-buffer.
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.StreamSource = _d3dGraphicsStream;
bi.EndInit();
// Set the Fill of the 3D region to the image of the back-buffer.
_d3dRegion.Fill = new ImageBrush(bi);
// The operation succeeded. So, it is supported.
_isSetD3dRegionFillUsingMemorySupported = true;
}
catch
{
if (_isSetD3dRegionFillUsingMemorySupported == true)
{
// There is a failure in the operation. But, it's supported.
// Maybe we have to free the memory of the unneeded GraphicsStream objects.
_isMemoryFreeNeeded = true;
}
else
{
// The update using the memory isn't supported...
_isSetD3dRegionFillUsingMemorySupported = false;
}
// This operation has failed. Give it another chance.
_updateD3dRegionEvent.Set();
}
}
}
}));
ReleaseD3dGraphicsStream(oldGraphicsStream);
}
private void ReleaseD3dGraphicsStream(GraphicsStream d3dGraphicsStream)
{
if (d3dGraphicsStream != null)
{
d3dGraphicsStream.Close();
d3dGraphicsStream.Close(); // Extra Close ( http://www.eggheadcafe.com/microsoft/Win32-DirectX-Managed/31961917/surfaceloadersavetostream-major-memory-leak.aspx )
}
}
protected void ReleaseD3dRegionMemory()
{
if (_d3dRegion == null)
{
return;
}
lock (_d3dRegion)
{
if (_d3dGraphicsStream != null)
{
ReleaseD3dGraphicsStream(_d3dGraphicsStream);
_d3dGraphicsStream = null;
}
}
}
#endregion
#region SetD3dRegionFillUsingFile
// A third approach for setting the Fill of the 3D region, can be using an image file.
private string _currentD3dRegionFillFileName;
#region UsedFileNames
private List<string> _usedFileNames;
public List<string> UsedFileNames
{
get { return _usedFileNames ?? (_usedFileNames = new List<string>()); }
}
#endregion
private void SetD3dRegionFillUsingFile(Surface s)
{
// Get available file name, for the storing the back-buffer.
string currD3dRegionFillFileName = GetAvailableFileName();
// Save the back-buffer as a file.
SurfaceLoader.Save(currD3dRegionFillFileName, ImageFileFormat.Jpg, s);
lock (_d3dRegion)
{
_currentD3dRegionFillFileName = currD3dRegionFillFileName;
// Store the back-buffer file's name, in order to delete it later.
UsedFileNames.Add(currD3dRegionFillFileName);
}
// Update the Fill of the 3D region, in the UI thread.
Dispatcher.BeginInvoke(new ThreadStart(() =>
{
lock (_d3dRegion)
{
if (_continueUpdateD3dRegionThread)
{
try
{
// Set the Fill of the 3D region to the saved back-buffer's file.
_d3dRegion.Fill =
new ImageBrush(new BitmapImage(new Uri(_currentD3dRegionFillFileName, UriKind.Relative)));
}
catch
{
}
}
}
}));
// Delete the used files except the last one (we use it as the current Fill...).
DeleteD3dRegionFiles(false);
}
private string GetAvailableFileName()
{
string fileNameBegin = "MdxWpf";
string fileNameEnd = ".jpg";
int fileNameCounter = 1;
string currFileName = string.Format("{0}{1}{2}",
fileNameBegin, fileNameCounter.ToString(), fileNameEnd);
while (File.Exists(currFileName) && fileNameCounter > 0)
{
fileNameCounter++;
currFileName = string.Format("{0}{1}{2}",
fileNameBegin, fileNameCounter.ToString(), fileNameEnd);
}
return currFileName;
}
protected void DeleteD3dRegionFiles(bool deleteLastFile)
{
if (_d3dRegion == null)
{
return;
}
string[] usedFileNamesCopy = null;
lock (_d3dRegion)
{
usedFileNamesCopy = UsedFileNames.ToArray();
}
int filesCount = usedFileNamesCopy.Length;
if (filesCount < 1)
{
return;
}
if (!deleteLastFile)
{
filesCount--;
}
for (int fileInx = 0; fileInx < filesCount; fileInx++)
{
string currFileName = usedFileNamesCopy[fileInx];
try
{
if (File.Exists(currFileName))
{
File.Delete(currFileName);
}
lock (_d3dRegion)
{
UsedFileNames.Remove(currFileName);
}
}
catch
{
}
}
}
#endregion
protected void ReleaseD3dRegion()
{
StopUpdateD3dRegionThread();
ReleaseD3dRegionMemory();
DeleteD3dRegionFiles(true);
}
#endregion
public override void OnApplyTemplate()
{
_d3dRegion = GetTemplateChild("PART_D3dRegion") as Rectangle;
if (_d3dRegion != null)
{
D3dRegionActualWidth = _d3dRegion.ActualWidth;
D3dRegionActualHeight = _d3dRegion.ActualHeight;
_d3dRegion.SizeChanged += (s, e) =>
{
D3dRegionActualWidth = _d3dRegion.ActualWidth;
D3dRegionActualHeight = _d3dRegion.ActualHeight;
// Raise the D3dRegionSizeChanged on the D3D region's size is changed.
e.RoutedEvent = D3dHost.D3dRegionSizeChangedEvent;
RaiseEvent(e);
};
RegisterD3dRegionMouseEvents();
}
base.OnApplyTemplate();
}
#region Mouse event-handlers
private void RegisterD3dRegionMouseEvents()
{
if (_d3dRegion == null)
{
return;
}
_d3dRegion.GotMouseCapture += OnD3dRegionMouseEvent;
_d3dRegion.LostMouseCapture += OnD3dRegionMouseEvent;
_d3dRegion.MouseEnter += OnD3dRegionMouseEvent;
_d3dRegion.MouseLeave += OnD3dRegionMouseEvent;
_d3dRegion.MouseMove += OnD3dRegionMouseEvent;
_d3dRegion.MouseDown += OnD3dRegionMouseButtonEvent;
_d3dRegion.MouseLeftButtonDown += OnD3dRegionMouseButtonEvent;
_d3dRegion.MouseLeftButtonUp += OnD3dRegionMouseButtonEvent;
_d3dRegion.MouseRightButtonDown += OnD3dRegionMouseButtonEvent;
_d3dRegion.MouseRightButtonUp += OnD3dRegionMouseButtonEvent;
_d3dRegion.MouseUp += OnD3dRegionMouseButtonEvent;
_d3dRegion.MouseWheel += OnD3dRegionMouseWheelEvent;
_d3dRegion.PreviewMouseDown += OnD3dRegionMouseButtonEvent;
_d3dRegion.PreviewMouseLeftButtonDown += OnD3dRegionMouseButtonEvent;
_d3dRegion.PreviewMouseLeftButtonUp += OnD3dRegionMouseButtonEvent;
_d3dRegion.PreviewMouseMove += OnD3dRegionMouseEvent;
_d3dRegion.PreviewMouseRightButtonDown += OnD3dRegionMouseButtonEvent;
_d3dRegion.PreviewMouseRightButtonUp += OnD3dRegionMouseButtonEvent;
_d3dRegion.PreviewMouseUp += OnD3dRegionMouseButtonEvent;
_d3dRegion.PreviewMouseWheel += OnD3dRegionMouseWheelEvent;
}
private void OnD3dRegionMouseWheelEvent(object sender, MouseWheelEventArgs e)
{
RoutedEvent d3dSurfaceMouseEvent = GetD3dSurfaceMouseEvent(e);
if (d3dSurfaceMouseEvent != null)
{
D3dSurfaceMouseWheelEventArgs d3dSurfaceEventArgs =
new D3dSurfaceMouseWheelEventArgs(d3dSurfaceMouseEvent)
{
MouseWheelEventArgs = e,
D3dSurfaceMousePosition = GetD3dSurfaceMousePosition(e)
};
RaiseEvent(d3dSurfaceEventArgs);
}
}
private void OnD3dRegionMouseButtonEvent(object sender, MouseButtonEventArgs e)
{
RoutedEvent d3dSurfaceMouseEvent = GetD3dSurfaceMouseEvent(e);
if (d3dSurfaceMouseEvent != null)
{
D3dSurfaceMouseButtonEventArgs d3dSurfaceEventArgs =
new D3dSurfaceMouseButtonEventArgs(d3dSurfaceMouseEvent)
{
MouseButtonEventArgs = e,
D3dSurfaceMousePosition = GetD3dSurfaceMousePosition(e)
};
RaiseEvent(d3dSurfaceEventArgs);
}
}
private void OnD3dRegionMouseEvent(object sender, MouseEventArgs e)
{
RoutedEvent d3dSurfaceMouseEvent = GetD3dSurfaceMouseEvent(e);
if (d3dSurfaceMouseEvent != null)
{
D3dSurfaceMouseEventArgs d3dSurfaceEventArgs =
new D3dSurfaceMouseEventArgs(d3dSurfaceMouseEvent)
{
MouseEventArgs = e,
D3dSurfaceMousePosition = GetD3dSurfaceMousePosition(e)
};
RaiseEvent(d3dSurfaceEventArgs);
}
}
private Point GetD3dSurfaceMousePosition(MouseEventArgs mouseArgs)
{
// Get the mouse position on the 3D region.
Point d3dRegionMousePosition = mouseArgs.GetPosition(_d3dRegion);
// Calculate the mouse position on the MDX surface.
Point d3dSurfaceMousePosition =
new Point(d3dRegionMousePosition.X * D3dSurfaceWidth / D3dRegionActualWidth,
d3dRegionMousePosition.Y * D3dSurfaceHeight / D3dRegionActualHeight);
return d3dSurfaceMousePosition;
}
private RoutedEvent GetD3dSurfaceMouseEvent(MouseEventArgs mouseArgs)
{
string d3dRegionEventName = mouseArgs.RoutedEvent.Name;
string d3dSurfaceEventName;
if (d3dRegionEventName.StartsWith("Preview"))
{
d3dSurfaceEventName = "PreviewD3dSurface" + d3dRegionEventName.Substring(7);
}
else
{
d3dSurfaceEventName = "D3dSurface" + d3dRegionEventName;
}
RoutedEvent d3dSurfaceMouseEvent =
EventManager.GetRoutedEvents().FirstOrDefault(re => re.OwnerType == typeof(D3dHost) && re.Name == d3dSurfaceEventName);
return d3dSurfaceMouseEvent;
}
#endregion
static D3dHost()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(D3dHost), new FrameworkPropertyMetadata(typeof(D3dHost)));
}
~D3dHost()
{
Dispose();
}
#region IDisposable implementation
public virtual void Dispose()
{
ReleaseD3dRegion();
ReleaseDevice();
}
#endregion
}
}