• If this is your first visit, you will have to register before you can post. To view messages, please scroll below and select the forum that you would like to visits. Questions? Be sure to check out the Forum FAQ.

Announcement

Collapse
No announcement yet.

Partner 728x90

Collapse

OnRender() Issue

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    OnRender() Issue

    Hi,

    I have run into a strange situation and clearly there's something I am missing here.

    I am using the following code construct inside OnRender():
    Code:
    OnRender()
    {
         // some code
    
         Action<int> paintString = (barIndex) =>
         {
              TriggerCustomEvent(o =>
              {
                   // some code
                   RenderTarget.DrawTextLayout(...);
              }, null);
         };
    
         // some code
         paintString(0);
    }
    Everything is well defined (I have checked) and there are no errors. I have placed Print statements throughout this segment and the values printed are all correct. In fact, the Render statement is executed as a Print statement before and after the Render statement provides output. The problem? Nothing is painted on the chart. For comparison, if I don't use the Action<T> delegate, the code (as is) works, and the string is painted on the chart. It therefore appears that there might be an issue with calling RenderTarget.DrawTextLayout inside a delegate. This shouldn't really be an issue so I am baffled. Any insight would be greatly appreciated. Thank you.

    #2
    Hello Zeos6,

    Thanks for your post.

    Without the full context, I could not say why the code works outside of the action delegate but not within. I would suggest checking your implementation for DrawTextLayout since you are able to see prints within the action delegate but not the DrawTextLayout.

    When I test on my end with the code below, the DrawTextLayout works as expected.
    Code:
    		protected override void OnRender(ChartControl chartControl, ChartScale chartScale)
    		{
    			Action<int> paintString = (barIndex) =>
    			{
    				TriggerCustomEvent(o =>
    				{
    				   // some code
    				   DrawString("text", new SimpleFont("Arial", 20), Brushes.Red, ChartPanel.W/2, ChartPanel.H/2, Brushes.Transparent);
    				}, null);
    			};
    			
    			 // some code
    			 paintString(0);
    		}
    		
    		private void DrawString(string text, SimpleFont font, Brush fontBrush, double pointX, double pointY, Brush areaBrush)
    		{
    			SharpDX.Direct2D1.Brush dxBrush = fontBrush.ToDxBrush(RenderTarget);
    			SharpDX.Direct2D1.Brush dxAreaBrush = areaBrush.ToDxBrush(RenderTarget);
    			
    			SharpDX.DirectWrite.TextFormat textFormat = font.ToDirectWriteTextFormat();
    			SharpDX.DirectWrite.TextLayout textLayout =
    			new SharpDX.DirectWrite.TextLayout(NinjaTrader.Core.Globals.DirectWriteFactory,
    				text, textFormat, ChartPanel.X + ChartPanel.W,
    				textFormat.FontSize);
    			SharpDX.Vector2 TextPlotPoint = new System.Windows.Point(pointX, pointY-textLayout.Metrics.Height/2).ToVector2();
    			
    			float newW = textLayout.Metrics.Width; 
    			float newH = textLayout.Metrics.Height;
    			SharpDX.RectangleF PLBoundRect = new SharpDX.RectangleF((float)pointX+2, (float)pointY-textLayout.Metrics.Height/2, newW+5, newH+2);
    			RenderTarget.FillRectangle(PLBoundRect, dxAreaBrush);
    			
    			RenderTarget.DrawTextLayout(TextPlotPoint, textLayout, dxBrush, SharpDX.Direct2D1.DrawTextOptions.NoSnap);
    			
    			textLayout.Dispose();
    			textFormat.Dispose();
    			dxBrush.Dispose();
    			dxAreaBrush.Dispose();
    		}
    Please let me know if I can be of further help.
    Last edited by NinjaTrader_Jim; 06-20-2018, 10:06 AM.
    JimNinjaTrader Customer Service

    Comment


      #3
      Hi Jim,

      Thank you for your reply. The sample you provide is fine but that isn't quite the way I'd like to use the delegate.Please allow me to clarify. What I am currently attempting to do is the following:

      Code:
      protected override void OnRender(ChartControl chartControl, ChartScale chartScale)
      {
           // some code
      
           PaintMyString(chartControl, chartScale);
      }
      
      private void PaintMyString(ChartControl chartControl, ChartScale chartScale)
      {
           // some code
           // set up TextFormat, set up TextLayout, set up SharpDX.Vector2
      
           Action<int> paintString = (barIndex) =>
           {
                TriggerCustomEvent(o =>
                {
                     // some code
      
                     RenderTarget.DrawTextLayout(...);
                }, null);
           };
      
           // some code
      
           for (int i = ChartBars.FromIndex; i <= ChartBars.ToIndex; i++)
           {
                // some code
                  
                paintString(CurrentBar-i);
           }
      }
      Clearly I am missing something here. Thank you for your insight and help.

      Comment


        #4
        Hello Zeos6,

        There are several nuances with SharpDX that involve resource management. Without complete context, I am only left to speculate what may the cause of your issue.

        Could you modify my sample code to model what you are trying to do so I can give further input?
        JimNinjaTrader Customer Service

        Comment


          #5
          Hi Jim,
          I probably could but that kid of misses the point of the delegate. I am trying to use the delegate as a local function (C#7 construct) to resuse code inside my method.Your approach doesn't really require the delegate and might work, but does involve code duplication. I am puzzled because SharpDX is finicky as all heck and for me not to get any error messages from it is simply strange.So this may not be a resource issue, or a SharpDX issue at all. What resources are you speaking of? RenderTarget? TextLayout, TextFormat?

          EDIT: I also debugged this with VS. All the arguments going into RenderTarget.DrawTextLayout() were well defined. No error messages.
          Last edited by Zeos6; 06-20-2018, 01:14 PM.

          Comment


            #6
            Hello Zeos6,

            SharpDX resources that inherit the SharpDX.DisposeBase class will need to be disposed. Device dependent resources like brushes should be disposed of and recreated on RenderTarget changes if they are to be reused. Device independent resources like TextLayout and TextFormat can be disposed of in State.Terminated, but if you are making new resources you should be sure to dispose of them after using to avoid additional resources being created that cannot be disposed of in State.Terminated.

            Not properly managing resources can lead to memory leaks, issues with rendering, and OnRender() errors.

            More info on best practices for SharpDX can be found here: https://ninjatrader.com/support/help...arpDXResources

            If I change my code around, I still get the text drawn without issue.

            Code:
            		private SharpDX.Direct2D1.Brush dxBrush;
            		private	SharpDX.Direct2D1.Brush dxAreaBrush;
            			
            		private SharpDX.DirectWrite.TextFormat textFormat;
            		private SharpDX.DirectWrite.TextLayout textLayout;
            		private	SharpDX.Vector2 TextPlotPoint;
            		
            		protected override void OnRender(ChartControl chartControl, ChartScale chartScale)
            		{
            			dxBrush = Brushes.Red.ToDxBrush(RenderTarget);
            			dxAreaBrush = Brushes.Transparent.ToDxBrush(RenderTarget);
            			textFormat = new SimpleFont("Arial", 20).ToDirectWriteTextFormat();
            			textLayout =
            			new SharpDX.DirectWrite.TextLayout(NinjaTrader.Core.Globals.DirectWriteFactory,
            				"text", textFormat, ChartPanel.X + ChartPanel.W,
            				textFormat.FontSize);
            			
            			TextPlotPoint = new System.Windows.Point(ChartPanel.W/2, ChartPanel.H/2-textLayout.Metrics.Height/2).ToVector2();
            			
            			float newW = textLayout.Metrics.Width; 
            			float newH = textLayout.Metrics.Height;
            			
            			SharpDX.RectangleF PLBoundRect = new SharpDX.RectangleF((float)ChartPanel.W/2+2, (float)ChartPanel.W/2-textLayout.Metrics.Height/2, newW+5, newH+2);
            			RenderTarget.FillRectangle(PLBoundRect, dxAreaBrush);
            			
            			Action<int> paintString = (barIndex) =>
            			{
            				TriggerCustomEvent(o =>
            				{
            					RenderTarget.DrawTextLayout(TextPlotPoint, textLayout, dxBrush, SharpDX.Direct2D1.DrawTextOptions.NoSnap);	
            				}, null);
            			};
            			
            			 // some code
            			 paintString(0);
            			
            			textLayout.Dispose();
            			textFormat.Dispose();
            			dxBrush.Dispose();
            			dxAreaBrush.Dispose();
            		}
            You can make additional modifications as I have to make my code closer to yours. Once you have a case that can reproduce the issue, you should be able to identify the cause by noting the differences in implementation.

            If something is still standing out, I could give further input with a reduced case that hits the issue. Please base this example off of my code so we can rule out other extraneous possibilities.
            Last edited by NinjaTrader_Jim; 06-20-2018, 02:16 PM.
            JimNinjaTrader Customer Service

            Comment


              #7
              Thanks for your reply Jim. I appreciate it. Thanks for the clarification on the resources.

              The immediate issue I see with your code sample is that you are not using the delegate as a local function within a method (for example, within the DrawString method you had earlier), and are not invoking it multiple times within the same OnRender call to paint a different string. For example, try painting the bar number - for each bar - for the current chart window on the same OnRender call, and put your delegate into a DrawString method. This way you'll have to create a TextFormat and TextLayout for each bar number string. All the samples you provided so far don't really require a delegate.They also don't show recreating TextFormat and TextLayout multiple times.

              EDIT. I already handle the resources as you suggested but I will check once again to be sure. Thank you.
              Last edited by Zeos6; 06-20-2018, 02:31 PM.

              Comment


                #8
                Hello Zeos6,

                There are still likely going to be differences in what I write in comparison to what you are doing exactly. I don't have enough information regarding the full context of what you are doing and am merely guessing.

                If you need further assistance, please create an example based on my code that demonstrates what you are doing and the issue you are hitting that I could also test as-is on my end. If the issue is not apparent at this stage, I could give feedback based off this example.

                I'll be happy to work with you further on this with the described sample.
                JimNinjaTrader Customer Service

                Comment


                  #9
                  Hi Jim,

                  Here you go; an example based on your code that you can run to see the issue...
                  Code:
                  protected override void OnRender(ChartControl chartControl, ChartScale chartScale)
                  {
                       base.OnRender(chartControl, chartScale);
                  
                       TriggerCustomEvent(o =>
                       {
                            DrawMyString(chartControl, chartScale);
                       }, null);
                  }
                  
                  private void DrawMyString(ChartControl chartControl, ChartScale chartScale)
                  {
                          Brush _fontBrush = Brushes.Red;
                          Brush _areaBrush = Brushes.Transparent;
                          double _pointX =  ChartPanel.W/5;
                          double _pointY = ChartPanel.H/2;
                      
                          Action<string, SimpleFont, Brush, double, double, Brush> paintString = (text, font, fontBrush, pointX, pointY, areaBrush) =>
                          {        
                              SharpDX.Direct2D1.Brush dxBrush = fontBrush.ToDxBrush(RenderTarget);
                              SharpDX.Direct2D1.Brush dxAreaBrush = areaBrush.ToDxBrush(RenderTarget);
                              
                              SharpDX.DirectWrite.TextFormat textFormat = font.ToDirectWriteTextFormat();
                              SharpDX.DirectWrite.TextLayout textLayout =
                              new SharpDX.DirectWrite.TextLayout(NinjaTrader.Core.Globals.DirectWriteFactory,
                                  text, textFormat, ChartPanel.X + ChartPanel.W,
                                  textFormat.FontSize);
                              SharpDX.Vector2 TextPlotPoint = new System.Windows.Point(pointX, pointY-textLayout.Metrics.Height/2).ToVector2();
                              
                              float newW = textLayout.Metrics.Width; 
                              float newH = textLayout.Metrics.Height;
                              SharpDX.RectangleF PLBoundRect = new SharpDX.RectangleF((float)pointX+2, (float)pointY-textLayout.Metrics.Height/2, newW+5, newH+2);
                              RenderTarget.FillRectangle(PLBoundRect, dxAreaBrush);
                              
                              RenderTarget.DrawTextLayout(TextPlotPoint, textLayout, dxBrush, SharpDX.Direct2D1.DrawTextOptions.NoSnap);
                              
                              textLayout.Dispose();
                              textFormat.Dispose();
                              dxBrush.Dispose();
                              dxAreaBrush.Dispose();
                          };
                      
                          for(int i = ChartBars.FromIndex; i <= ChartBars.ToIndex; i++)
                          {        
                              _pointX += i*ChartPanel.W/100;
                              string _text = i.ToString();
                              paintString(_text, new SimpleFont("Arial", 20), _fontBrush, _pointX, _pointY, _areaBrush);
                          }
                  }
                  As an aside, I have also tried using
                  Code:
                  Action<string, SimpleFont, Brush, double, double, Brush> paintString = new Action<string, SimpleFont, Brush, double, double, Brush> ( (text, font, fontBrush, pointX, pointY, areaBrush) =>
                  {});
                  and that too does not work. No error messages and no output.

                  Comment


                    #10
                    Thanks Zeos6,

                    It looks like the issue was where the code was drawing to.

                    Please see my changes here:

                    Code:
                    protected override void OnRender(ChartControl chartControl, ChartScale chartScale)
                    {
                    	base.OnRender(chartControl, chartScale);
                    
                    	TriggerCustomEvent(o =>
                    	{
                    		DrawMyString(chartControl, chartScale);
                    	}, null);
                    }
                    
                    private void DrawMyString(ChartControl chartControl, ChartScale chartScale)
                    {
                    	Brush _fontBrush = Brushes.Red;
                    	Brush _areaBrush = Brushes.Transparent;
                    	double _pointX =  ChartPanel.W/5;
                    	double _pointY = ChartPanel.H/2;
                    
                    	Action<string, SimpleFont, Brush, double, double, Brush> paintString = (text, font, fontBrush, pointX, pointY, areaBrush) =>
                    	{        
                    		SharpDX.Direct2D1.Brush dxBrush = fontBrush.ToDxBrush(RenderTarget);
                    		SharpDX.Direct2D1.Brush dxAreaBrush = areaBrush.ToDxBrush(RenderTarget);
                    
                    		SharpDX.DirectWrite.TextFormat textFormat = font.ToDirectWriteTextFormat();
                    		SharpDX.DirectWrite.TextLayout textLayout =
                    			new SharpDX.DirectWrite.TextLayout(NinjaTrader.Core.Globals.DirectWriteFactory,
                    			text, textFormat, ChartPanel.X + ChartPanel.W,
                    			textFormat.FontSize);
                    		SharpDX.Vector2 TextPlotPoint = new System.Windows.Point(pointX, pointY-textLayout.Metrics.Height/2).ToVector2();
                    
                    		float newW = textLayout.Metrics.Width; 
                    		float newH = textLayout.Metrics.Height;
                    		SharpDX.RectangleF PLBoundRect = new SharpDX.RectangleF((float)pointX+2, (float)pointY-textLayout.Metrics.Height/2, newW+5, newH+2);
                    		RenderTarget.FillRectangle(PLBoundRect, dxAreaBrush);
                    
                    		RenderTarget.DrawTextLayout(TextPlotPoint, textLayout, dxBrush, SharpDX.Direct2D1.DrawTextOptions.NoSnap);
                    
                    		textLayout.Dispose();
                    		textFormat.Dispose();
                    		dxBrush.Dispose();
                    		dxAreaBrush.Dispose();
                    	};
                    
                    	for(int i = ChartBars.FromIndex; i <= ChartBars.ToIndex; i++)
                    	{        
                    		_pointX = ChartControl.GetXByBarIndex(ChartBars, i);
                    		_pointY = chartScale.GetYByValue(High.GetValueAt(i)+3*TickSize);
                    		string _text = i.ToString();
                    		
                    		paintString(_text, new SimpleFont("Arial", 10), _fontBrush, _pointX, _pointY, _areaBrush);
                    	}
                    }
                    If there is anything else I may do to help, please let me know.
                    Last edited by NinjaTrader_Jim; 06-21-2018, 12:41 PM.
                    JimNinjaTrader Customer Service

                    Comment


                      #11
                      Thanks Jim,

                      Although the sample I provided did in fact have the error you indicated, this was not an issue in my actual code. My actual code was doing this calculation correctly. However, you did give me the clue I needed when you said
                      ... the issue was where the code was drawing to...
                      This made me check the painting x and y coordinates and that's when I realized that I was painting way off the screen.This led me to find the error. As I am using a Series object, the series needs a bars back reference whereas the coordinate calculation needs the bars index. This mismatch was responsible for the error. I can't believe I missed that. Thank you very much again Jim. I appreciate your help.

                      Comment


                        #12
                        Hello Zeos6,

                        I'm glad you got it resolved.

                        I had figured you had some other code using bars ago references since you were using TriggerCustomEvent() from OnRender(). You are free to do as you want with your code but as a tip, you could avoid using TriggerCustomEvent() if your price series references use the bar index instead of a barsAgo index. I.E. using Close.GetValueAt(barIndex).

                        Further notes and advise can be referenced in the OnRender() documentation page - https://ninjatrader.com/support/help...s/onrender.htm
                        JimNinjaTrader Customer Service

                        Comment


                          #13
                          Thanks Jim!!!! That is excellent advice. Didn't think of it but makes sense. I am all in favour of reducing code complexity. Thank you. Will do as you suggested.

                          Comment

                          Latest Posts

                          Collapse

                          Topics Statistics Last Post
                          Started by Gerik, Yesterday, 11:27 PM
                          0 responses
                          2 views
                          0 likes
                          Last Post Gerik
                          by Gerik
                           
                          Started by Bionian, Yesterday, 11:14 PM
                          0 responses
                          2 views
                          0 likes
                          Last Post Bionian
                          by Bionian
                           
                          Started by Gerik, Yesterday, 10:58 PM
                          0 responses
                          1 view
                          0 likes
                          Last Post Gerik
                          by Gerik
                           
                          Vps by stooby
                          Started by stooby, Yesterday, 08:32 PM
                          0 responses
                          11 views
                          0 likes
                          Last Post stooby
                          by stooby
                           
                          Started by FaaastEddy, Yesterday, 08:25 PM
                          0 responses
                          3 views
                          0 likes
                          Last Post FaaastEddy  
                          Working...
                          X