Announcement
Collapse
No announcement yet.
Partner 728x90
Collapse
NinjaTrader
Custom Data Box example?
Collapse
X
-
It seems that drawing in on-render is cpu intensive. Here's what I came up with, is this the right approach? Perhaps change the cursor itself?
PHP Code:#region Using declarations using System; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Windows; using System.Windows.Input; using System.Windows.Threading; using NinjaTrader.Gui; using NinjaTrader.Gui.Chart; using NinjaTrader.NinjaScript; // Be explicit about which namespaces to use for resolving ambiguous references using SharpDX; using SharpDX.Direct2D1; using SharpDX.DirectWrite; // Use alias for System.Windows.Media to prevent ambiguous references using WPFBrush = System.Windows.Media.Brush; using WPFColor = System.Windows.Media.Color; using WPFSolidColorBrush = System.Windows.Media.SolidColorBrush; #endregion namespace NinjaTrader.NinjaScript.Indicators { public class MouseFollowingTextIndicator : Indicator { private System.Windows.Point lastPosition = new System.Windows.Point(0, 0); private System.Windows.Point lastValidPosition = new System.Windows.Point(0, 0); private DateTime lastCursorMoveTime = DateTime.MinValue; private DispatcherTimer updateTimer; private bool ctrlPressed = false; private bool isTextVisible = false; private ChartControl chartControl; private System.Windows.Point lastCursorPosition; private TextFormat textFormat; private byte currentAlpha = 255; // Full opacity by default private int currentAlphaLevel = 3; // Starting at max level private const int ALPHA_LEVELS = 4; // Number of alpha levels (0, 1, 2, 3) protected override void OnStateChange() { if (State == State.SetDefaults) { Description = "Displays text that follows the mouse cursor on the chart when pressing CTRL"; Name = "Mouse Following Text"; Calculate = Calculate.OnPriceChange; IsOverlay = true; DisplayInDataBox = false; PaintPriceMarkers = false; IsSuspendedWhileInactive = true; // Default parameters UpdateFrequencyMs = 150; DisplayText = "BUY MSFT 100"; MaxCursorSpeedForDisplay = 500; // pixels per second MaxFadeDistance = 300; // pixels // Set drawing style TextColor = System.Windows.Media.Brushes.Blue; TextSize = 14; TextOffsetX = 15; TextOffsetY = 15; } else if (State == State.Historical) { // We don't need to do anything during a historical calculation } else if (State == State.Realtime) { if (ChartControl != null) { // Store reference to chart control chartControl = ChartControl; // Create TextFormat once for reuse textFormat = new TextFormat(Core.Globals.DirectWriteFactory, "Arial", SharpDX.DirectWrite.FontWeight.Normal, SharpDX.DirectWrite.FontStyle.Normal, (float)TextSize); // Set up the update timer on the UI thread Dispatcher.InvokeAsync(() => { // Set up the update timer updateTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(UpdateFrequencyMs) }; updateTimer.Tick += UpdateTimerTick; // Set up event handlers chartControl.PreviewKeyDown += ChartControl_PreviewKeyDown; chartControl.PreviewKeyUp += ChartControl_PreviewKeyUp; chartControl.MouseMove += ChartControl_MouseMove; // Initialize time tracking lastCursorMoveTime = DateTime.Now; lastCursorPosition = new System.Windows.Point(0, 0); // Start the timer updateTimer.Start(); }); } } else if (State == State.Terminated) { // Clean up resources and event handlers if (ChartControl != null) { Dispatcher.InvokeAsync(() => { if (updateTimer != null) { updateTimer.Stop(); updateTimer.Tick -= UpdateTimerTick; } if (chartControl != null) { chartControl.PreviewKeyDown -= ChartControl_PreviewKeyDown; chartControl.PreviewKeyUp -= ChartControl_PreviewKeyUp; chartControl.MouseMove -= ChartControl_MouseMove; } }); } // Dispose of DirectWrite resources if (textFormat != null) { textFormat.Dispose(); textFormat = null; } } } protected override void OnRender(ChartControl chartControl, ChartScale chartScale) { // Only draw the text if it's supposed to be visible if (isTextVisible && ctrlPressed) { // Convert brush color to SharpDX color with current alpha SharpDX.Color textColor = ToSharpDXColor(TextColor, currentAlpha); // Create brush using (var brush = new SolidColorBrush(RenderTarget, textColor)) { // Define the layout rectangle - make it large enough for the text var layoutRect = new SharpDX.RectangleF( (float)(lastValidPosition.X + TextOffsetX), (float)(lastValidPosition.Y + TextOffsetY), 1000, // Large enough to accommodate the text 100); // Draw the text RenderTarget.DrawText( DisplayText, textFormat, layoutRect, brush, DrawTextOptions.None, MeasuringMode.Natural); } } } private SharpDX.Color ToSharpDXColor(WPFBrush brush, byte alpha = 255) { if (brush is WPFSolidColorBrush solidBrush) { WPFColor color = solidBrush.Color; // Explicitly cast to byte to avoid ambiguity between constructor overloads return new SharpDX.Color((byte)color.R, (byte)color.G, (byte)color.B, alpha); } // Default to black if brush is not a solid color return new SharpDX.Color((byte)0, (byte)0, (byte)0, alpha); } private void ChartControl_PreviewKeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.LeftCtrl || e.Key == Key.RightCtrl) { ctrlPressed = true; // The text will become visible on the next timer tick if cursor speed is below threshold ChartControl.InvalidateVisual(); } } private void ChartControl_PreviewKeyUp(object sender, KeyEventArgs e) { if (e.Key == Key.LeftCtrl || e.Key == Key.RightCtrl) { ctrlPressed = false; isTextVisible = false; ChartControl.InvalidateVisual(); } } private void ChartControl_MouseMove(object sender, MouseEventArgs e) { if (chartControl == null) return; // Get current cursor position lastCursorPosition = e.GetPosition(chartControl); // Track cursor movement for speed calculation lastCursorMoveTime = DateTime.Now; } private void UpdateTimerTick(object sender, EventArgs e) { if (!ctrlPressed || chartControl == null) { if (isTextVisible) { isTextVisible = false; ChartControl.InvalidateVisual(); } return; } DateTime now = DateTime.Now; // Calculate cursor speed (pixels per second) double timeDeltaSeconds = (now - lastCursorMoveTime).TotalSeconds; if (timeDeltaSeconds <= 0) timeDeltaSeconds = 0.001; // Prevent division by zero double cursorSpeed = 0; if (lastPosition.X != 0 || lastPosition.Y != 0) // Only if we have a previous position { double distance = Math.Sqrt( Math.Pow(lastCursorPosition.X - lastPosition.X, 2) + Math.Pow(lastCursorPosition.Y - lastPosition.Y, 2)); cursorSpeed = distance / timeDeltaSeconds; } // Update position lastPosition = lastCursorPosition; // Only update the text position if cursor speed is below threshold bool shouldUpdateTextPosition = ctrlPressed && cursorSpeed < MaxCursorSpeedForDisplay; if (shouldUpdateTextPosition) { // Update the valid position where text should be shown lastValidPosition = lastCursorPosition; } // Calculate distance from cursor to text for alpha double dx = Math.Abs(lastCursorPosition.X - lastValidPosition.X); double dy = Math.Abs(lastCursorPosition.Y - lastValidPosition.Y); double maxDist = Math.Max(dx, dy); // Calculate alpha level based on distance (simplified approach) int newAlphaLevel = ALPHA_LEVELS - 1; // Start with max level if (maxDist > 0) { // Convert to alpha level (0-3) double distanceFactor = Math.Min(1.0, maxDist / MaxFadeDistance); newAlphaLevel = ALPHA_LEVELS - 1 - (int)(distanceFactor * ALPHA_LEVELS); // Clamp to valid range if (newAlphaLevel < 0) newAlphaLevel = 0; if (newAlphaLevel >= ALPHA_LEVELS) newAlphaLevel = ALPHA_LEVELS - 1; } // Only update alpha and invalidate if level changed if (newAlphaLevel != currentAlphaLevel) { currentAlphaLevel = newAlphaLevel; // Convert level to actual alpha value (0-255) currentAlpha = (byte)(currentAlphaLevel * (255 / (ALPHA_LEVELS - 1))); // Ensure text is visible isTextVisible = true; // Invalidate chart to update display ChartControl.InvalidateVisual(); } else if (!isTextVisible) { // Make sure text is visible if it should be isTextVisible = true; ChartControl.InvalidateVisual(); } else if (shouldUpdateTextPosition) { // Position changed, need to redraw ChartControl.InvalidateVisual(); } } protected override void OnBarUpdate() { } #region Properties [NinjaScriptProperty] [Range(50, 1000)] [Display(Name="Update Frequency (ms)", Description="Frequency at which text position updates (milliseconds)", Order=1, GroupName="Parameters")] public int UpdateFrequencyMs { get; set; } [NinjaScriptProperty] [Display(Name="Display Text", Description="Text to display next to cursor", Order=2, GroupName="Parameters")] public string DisplayText { get; set; } [NinjaScriptProperty] [Range(50, 2000)] [Display(Name="Max Cursor Speed", Description="Maximum cursor speed for text display (pixels/sec)", Order=3, GroupName="Parameters")] public double MaxCursorSpeedForDisplay { get; set; } [NinjaScriptProperty] [Range(50, 1000)] [Display(Name="Max Fade Distance", Description="Distance at which text becomes fully transparent (pixels)", Order=4, GroupName="Parameters")] public double MaxFadeDistance { get; set; } [NinjaScriptProperty] [Display(Name="Text Color", Description="Color of the displayed text", Order=5, GroupName="Appearance")] public WPFBrush TextColor { get; set; } [NinjaScriptProperty] [Range(8, 72)] [Display(Name="Text Size", Description="Size of the displayed text", Order=6, GroupName="Appearance")] public double TextSize { get; set; } [NinjaScriptProperty] [Range(-100, 100)] [Display(Name="X Offset", Description="Horizontal offset from cursor", Order=7, GroupName="Appearance")] public double TextOffsetX { get; set; } [NinjaScriptProperty] [Range(-100, 100)] [Display(Name="Y Offset", Description="Vertical offset from cursor", Order=8, GroupName="Appearance")] public double TextOffsetY { get; set; } #endregion } }​
Last edited by MiCe1999; 03-15-2025, 09:11 AM.
-
Hello MiCe1999,
Below is a link to an example on the forum of capturing the mouse points and converting these to x and y rendering coordinates.
I can also provide an example that sends information from an addon window to an indicator, which you could reverse to send information from the indicator to the addon window and run a method that updates text in the window.
Chelsea B.NinjaTrader Customer Service
Comment
Latest Posts
Collapse
Topics | Statistics | Last Post | ||
---|---|---|---|---|
Started by SiebertCowen, Yesterday, 11:58 PM
|
0 responses
3 views
0 likes
|
Last Post
![]()
by SiebertCowen
Yesterday, 11:58 PM
|
||
Started by hunghnguyen2016, Yesterday, 08:00 PM
|
0 responses
8 views
0 likes
|
Last Post
![]() |
||
Started by cbentrikin, Yesterday, 03:49 PM
|
0 responses
11 views
0 likes
|
Last Post
![]()
by cbentrikin
Yesterday, 03:49 PM
|
||
Started by MiCe1999, 04-14-2025, 06:54 PM
|
7 responses
77 views
0 likes
|
Last Post
![]()
by b.j.d
Yesterday, 03:45 PM
|
||
Started by NISNOS69, Yesterday, 02:20 PM
|
0 responses
18 views
0 likes
|
Last Post
![]()
by NISNOS69
Yesterday, 02:20 PM
|
Comment