I’m encountering a persistent DirectX error when switching between workspaces, even when following documented best practices for SharpDX resource management in NinjaScript. This happens even in a minimal indicator that draws a single line using a correctly managed SharpDX brush and implements OnRenderTargetChanged() properly.
Steps To Reproduce
Add the attached SimpleDxLineTest indicator to any chart.
Switch to a different workspace (leaving the current chart open but no longer visible).
Observe the NinjaScript Output Log or Control Center > Log tab.
You’ll see the following DirectX error immediately upon switching away:
A direct X error has occurred while rendering the chart: HRESULT: [0x88990015]
Module: [SharpDX.Direct2D1]
ApiCode: [D2DERR_WRONG_RESOURCE_DOMAIN/WrongResourceDomain]
Message: The resource was realized on the wrong render target.
Observations
No calls to OnRender() or OnRenderTargetChanged()** are made at the moment of workspace switch (verified via print logs).
Error occurs only when switching away from the workspace.
When switching back, OnRender() and OnRenderTargetChanged() are called, and rendering resumes without any error.
RenderTarget.IsDisposed remains false throughout.
Implementation Notes
The indicator:
Uses SharpDX properly: brushes are disposed and recreated in OnRenderTargetChanged().
Never accesses RenderTarget outside OnRender() or OnRenderTargetChanged().
Avoids calling ToDxBrush() at runtime.
All rendering is guarded by null checks and IsDisposed checks.
Expected Behavior
If the chart is no longer visible (due to workspace switch), OnRender() should not be invoked — and if it is, SharpDX resources tied to a different render target shouldn’t cause a crash if we’re not actively drawing. Or OnRenderTargetChanged() should be invoked when switching away and back to a workspace so that it can handle the dispose and create of SharpDx resources to prevent the run time error
I’ve attached the SimpleDxLineTest.cs file that demonstrates the issue in isolation.
#region Using declarations
using System;
using System.Windows.Media;
using SharpDX;
using SharpDX.Direct2D1;
using NinjaTrader.Cbi;
using NinjaTrader.Gui.Tools;
using NinjaTrader.NinjaScript;
using NinjaTrader.Data;
using NinjaTrader.Gui.Chart;
using NinjaTrader.NinjaScript.Strategies;
using NinjaTrader.NinjaScript.Indicators;
using Media = System.Windows.Media;
#endregion
namespace NinjaTrader.NinjaScript.Indicators
{
public class SimpleDxLineTest : Indicator
{
private SharpDX.Direct2D1.Brush dxBrush;
private StrokeStyle strokeStyle;
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Description = "Draws a DX line at Close[0]";
Name = "SimpleDxLineTest";
IsOverlay = true;
Calculate = Calculate.OnEachTick;
IsSuspendedWhileInactive = true;
}
else if (State == State.Terminated)
{
strokeStyle?.Dispose();
dxBrush?.Dispose();
}
}
public override void OnRenderTargetChanged()
{
Print($">>> [OnRenderTargetChanged] called at {DateTime.Now:HH:mm:ss.fff}");
// Only create strokeStyle once
if (strokeStyle == null)
{
strokeStyle = new StrokeStyle(Core.Globals.D2DFactory,
new StrokeStyleProperties { DashStyle = SharpDX.Direct2D1.DashStyle.Solid });
}
dxBrush?.Dispose();
dxBrush = new SharpDX.Direct2D1.SolidColorBrush(RenderTarget, SharpDX.Color.DodgerBlue);
}
protected override void OnRender(ChartControl chartControl, ChartScale chartScale)
{
Print($">>> [OnRender] called at {DateTime.Now:HH:mm:ss.fff}");
if (ChartBars == null || chartScale == null || RenderTarget == null)
return;
// Draw horizontal line in the middle of chart panel
float y = ChartPanel.H / 2f;
RenderTarget.DrawLine(
new Vector2(0, y),
new Vector2(ChartPanel.W, y),
dxBrush,
2f,
strokeStyle
);
}
}
}
Please let me know if there is a lifecycle-safe workaround or recommended approach to handle workspace switches cleanly without triggering this error. I’m happy to assist with any additional diagnostics.
Thank you for reviewing this and writing this coding suggestion. It’s very useful to know this and I will use this for indicators that require simpler SharpDX rendering. My actual indicator is more complicated with the use of multiple strokes, gradient fills, different brushes for up/down candles, filled candles. So for that indicator I may need to manage the SharpDX resources.
However I did compile this code and found that this never caused the run-time error in my System Log when switching workspaces. Puzzled by this, I then found the root cause of my problem. I had another indicator in the workspace which had not implemented OnRenderTargetChanged() and was not disposing and recreating SharpDX resources and it was this indicator giving the run-time errors.
This thread can be closed as issue identified due to unrelated indicator using SharpDX but not implementing OnRenderTargetChanged() to dispose and recreate SharpDX resources as per guidelines.
Create SimpleFont public property to be used for TextFormat.
TextFormat can be declared and assigned once, then re-used.
TextLayout not so much. I would recommend using() statement.
While you do not need a TextLayout to just render a DrawText.
Easier to grab the height and length of string from Metrics.