Help needed with managing drawing objects

Questions about MultiCharts .NET and user contributed studies.
Talky_Zebra
Posts: 45
Joined: 07 Mar 2024
Has thanked: 13 times
Been thanked: 1 time

Help needed with managing drawing objects

Postby Talky_Zebra » 01 Apr 2024

Hi all, I have been working through JoshM's tut on managing drawing objects as collections
https://www.tradingcode.net/multicharts ... ollection/

Good stuff.

But, I am looking for examples for the following use case:
I want to keep a list of rectangles (for instance, to highlight consolidations). I expect that the most recent rectangle will need to be updated if it borders on bar 0. Such that its starting date time remains the same, but if bar 0's conditions are still valid, the rectangle will continue to extend its "datetime 2" to bar 0 until the conditions no longer apply. Or I might want the upper and lower bounds of the rectangle to match a range's highs / lows.

What I don't understand:
What is the best way to manage this? Delete the most recent rectangle, and then create a new one with the conditions needed?
  • If so, then how can I get the existing rectangle's datetimes and values? I can't find a way to extract those from the List object. Without those it's more complicated to create a new rectangle with the updated parameters.
Or is there a way to get the reference to the Rectangle Object and update it?
  • Just as I could not find a way to extract the parameters from the existing rectangle in the List, I don't know how to grab a reference to the drawn object and then apply an update to its parameters.
I am sure there is a way! Can someone point me in the direction of the right example / article / etc?

HellGhostEvocatorX
Posts: 81
Joined: 10 Feb 2022
Has thanked: 52 times
Been thanked: 11 times

Re: Help needed with managing drawing objects

Postby HellGhostEvocatorX » 02 Apr 2024

Code: Select all

void ListeBefüllen(double preiswert, DateTime zeitwert, List<ChartPoint> chartPointListe) { // chartPointListe.Capacity++; chartPointListe.Insert(0, new ChartPoint(zeitwert, preiswert)); } void M_ZeichnungEinfügenZigZag(List<ChartPoint> chartPointListe, Color colorH, Color colorT) { // if (Bars.Status == EBarState.Close) { if (chartPointListe.Count > 1) { ChartPoint previousPoint = chartPointListe[1]; ChartPoint currentPoint = chartPointListe[0]; // Zeichnen Sie die Trendlinie zwischen previousPoint und currentPoint if (chartPointListe[0].Price > chartPointListe[1].Price) { HochTrendlinie.Value = DrwTrendLine.Create(previousPoint, currentPoint); //zeitlich 1. Punkt HochTrendlinie.Value.ExtLeft = false; HochTrendlinie.Value.ExtRight = false; HochTrendlinie.Value.Color = colorH; } else if (chartPointListe[0].Price < chartPointListe[1].Price) { TiefTrendlinie.Value = DrwTrendLine.Create(previousPoint, currentPoint); //zeitlich 1. Punkt TiefTrendlinie.Value.ExtLeft = false; TiefTrendlinie.Value.ExtRight = false; TiefTrendlinie.Value.Color = colorT; } } } }
Can you send me your previous code and 1-2 pictures of what exactly you want to achieve or what you are trying to achieve?

Otherwise she gave herself the answer. Simply update the "end points" of your rectangle chart points until it should no longer be updated according to your requirements. If you create your own (new) line for your lines, you don't actually need to delete it, but you could update it.
For example, you could not write the last points in the list, but in a variable object and only add them to the list when you no longer need the last point.

If you can't call up the points of the rectangle, simply create a list with the chart points, then you can access the individual points at any time... take a look at the code snippet above, for example I also wrote a method for this and below you can see how you could, for example, call up the price for a specific chart point :)

Otherwise, Josh shows how it works on his website. You could simply iterate through the list of the drawing with a for loop and then, for example, delete the last entry and then insert a new point.

Maybe this is also a problem for you: [RecoverDrawings(false)] --- just google it or try with true/false.

Talky_Zebra
Posts: 45
Joined: 07 Mar 2024
Has thanked: 13 times
Been thanked: 1 time

Re: Help needed with managing drawing objects

Postby Talky_Zebra » 05 Apr 2024

Hi HellGhost - not quite what I wanted to do, but it got me a little closer. Thank you for the inspiration. I still have an error that I cannot get past. :?:

Noting that you made a list of ChartPoints, I made my list of IRectangleObjects, and that allowed me to access the Properties of the rectangles (ie: Begin, End, etc)

Here's my toy code that shows what I want to do:

Code: Select all

using System; using System.Drawing; using System.Linq; using PowerLanguage.Function; using System.Collections.Generic; // Added for List namespace PowerLanguage.Indicator { [SameAsSymbol(true)] public class _TEST_ChartObjects_Stacks : IndicatorObject { public _TEST_ChartObjects_Stacks(object _ctx) : base(_ctx) { } private List<IRectangleObject> myDrawings; protected override void Create() { Output.Clear(); } protected override void StartCalc() { myDrawings = new List<IRectangleObject>(); } protected override void CalcBar() { if (Bars.FullSymbolData.Current == Bars.FullSymbolData.Count - 1) { myDrawings.Add(DrwRectangle.Create(new ChartPoint(Bars.Time[55], Bars.Low[55]), new ChartPoint(Bars.Time[45], Bars.High[45]))); if(myDrawings.Count > 0){ IRectangleObject robj = myDrawings.Last<IRectangleObject>(); // This writes to Output successfully Output.WriteLine("End point {0}", robj.End.ToString()); ChartPoint cp = new ChartPoint(Bars.Time[2], Bars.High[2]); robj.End = cp; // This fails with "System.ApplicationException: This was never displayed!" Output.WriteLine("End point {0}", robj.End.ToString()); } } } } }
The error that occurs when I try to assign a new chartpoint to an existing End property is this:
Rectangle error.JPG
(26.05 KiB) Not downloaded yet
"System.ApplicationException: This was never displayed!"

I also tried accessing the individual parameters of the chartpoint:

Code: Select all

robj.End.Time = Bars.Time[2]; robj.End.Price = Bars.High[2];
But that was no more successful. All of these properties should be both Get and Set according to the documentation:
chartpointtime.JPG
(7.32 KiB) Not downloaded yet
chartpointend.JPG
(8.62 KiB) Not downloaded yet
So, it seems that the methods and properties are available to use, but something about my syntax is preventing me from updating a pre-existing rectangle. Of course, I could:

Code: Select all

<pseudocode> Capture the Begin point of the last rectangle to a ChartPoint variable Delete the last rectangle Add a new Rectangle using the old Begin chartpoint and an updated End chartpoint </pseudocode>
But, if I can update the existing rectangle, that should be the easier, better option. Can it be done?

BD.
Posts: 19
Joined: 02 Feb 2024
Has thanked: 2 times
Been thanked: 2 times

Re: Help needed with managing drawing objects

Postby BD. » 06 Apr 2024

Hi HellGhost - not quite what I wanted to do, but it got me a little closer. Thank you for the inspiration. I still have an error that I cannot get past. :?:

Noting that you made a list of ChartPoints, I made my list of IRectangleObjects, and that allowed me to access the Properties of the rectangles (ie: Begin, End, etc)

Here's my toy code that shows what I want to do:

Code: Select all

using System; using System.Drawing; using System.Linq; using PowerLanguage.Function; using System.Collections.Generic; // Added for List namespace PowerLanguage.Indicator { [SameAsSymbol(true)] public class _TEST_ChartObjects_Stacks : IndicatorObject { public _TEST_ChartObjects_Stacks(object _ctx) : base(_ctx) { } private List<IRectangleObject> myDrawings; protected override void Create() { Output.Clear(); } protected override void StartCalc() { myDrawings = new List<IRectangleObject>(); } protected override void CalcBar() { if (Bars.FullSymbolData.Current == Bars.FullSymbolData.Count - 1) { myDrawings.Add(DrwRectangle.Create(new ChartPoint(Bars.Time[55], Bars.Low[55]), new ChartPoint(Bars.Time[45], Bars.High[45]))); if(myDrawings.Count > 0){ IRectangleObject robj = myDrawings.Last<IRectangleObject>(); // This writes to Output successfully Output.WriteLine("End point {0}", robj.End.ToString()); ChartPoint cp = new ChartPoint(Bars.Time[2], Bars.High[2]); robj.End = cp; // This fails with "System.ApplicationException: This was never displayed!" Output.WriteLine("End point {0}", robj.End.ToString()); } } } } }
The error that occurs when I try to assign a new chartpoint to an existing End property is this:Rectangle error.JPG
"System.ApplicationException: This was never displayed!"

I also tried accessing the individual parameters of the chartpoint:

Code: Select all

robj.End.Time = Bars.Time[2]; robj.End.Price = Bars.High[2];
But that was no more successful. All of these properties should be both Get and Set according to the documentation:chartpointtime.JPGchartpointend.JPG

So, it seems that the methods and properties are available to use, but something about my syntax is preventing me from updating a pre-existing rectangle. Of course, I could:

Code: Select all

<pseudocode> Capture the Begin point of the last rectangle to a ChartPoint variable Delete the last rectangle Add a new Rectangle using the old Begin chartpoint and an updated End chartpoint </pseudocode>
But, if I can update the existing rectangle, that should be the easier, better option. Can it be done?
So, I didn't solve the problem, but I think I found the issue.

Code: Select all

Time: 2/29/2024 1:00:00 AM, BarNumber: 2251099, Price: 1.0828 //.End from DrwRectangle Time: 1/1/0001 12:00:00 AM, BarNumber: 2251099, Price: 1.0828 //cp
I'm making uneducated guess that lack of Date value in cp is causing the problem.

Try to either draw rectangle using time, if it won't help then delete the rectangle and create a new one. Please report back what you found out.

Talky_Zebra
Posts: 45
Joined: 07 Mar 2024
Has thanked: 13 times
Been thanked: 1 time

Re: Help needed with managing drawing objects

Postby Talky_Zebra » 06 Apr 2024

BD, thank you for joining the conversation.

Upon seeing your post, I did a similar test to print out the chartpoints. I am not getting the erroneous dates, I see correct datetimes. I changed the bar numbers a few times to be sure. I don't think that is the issue.

Although, while knocking my way through the code a bit more, I found something out. DrwRectangles need a NEW ChartPoint declared when they are created. So, even if I had just declared a CP to a new variable and tried to use that, it would not work. But, even with this, I tried updating the end coordinates of a rectangle with a new chartpoint and that would not do it.

So, I can get it to work with the code below. I gave up and resorted to deleting the last rectangle and replacing, and re-introduced the mouseclick controls to demonstrate the updating. While the code is a bit over-cooked, I can clean it up and use it. I learned a bit more about C# along the way. I hope it helps someone else.

MultiCharts team - both the IRectangleObject and ChartPoint show that their Public Instance properties are both Get/Set or Read/Write. They are not shown as static. What is the proper syntax for updating them once they are created?

Code: Select all

using System; using System.Drawing; using System.Linq; using PowerLanguage.Function; using System.Collections.Generic; // Added for List using System.Windows.Forms; // Added for Keys enumeration namespace PowerLanguage.Indicator { [SameAsSymbol(true), MouseEvents(true)] public class _TEST_ChartObjects_Stacks : IndicatorObject { public _TEST_ChartObjects_Stacks(object _ctx) : base(_ctx) { } private List<IRectangleObject> myDrawings; protected override void Create() { Output.Clear(); } protected override void StartCalc() { myDrawings = new List<IRectangleObject>(); } protected override void CalcBar() { if (Bars.FullSymbolData.Current == Bars.FullSymbolData.Count - 1) { // Add some original rectangles myDrawings.Add(DrwRectangle.Create(new ChartPoint(Bars.Time[90], Bars.Low[90]), new ChartPoint(Bars.Time[80], Bars.High[80]))); myDrawings.Add(DrwRectangle.Create(new ChartPoint(Bars.Time[75], Bars.Low[75]), new ChartPoint(Bars.Time[65], Bars.High[65]))); myDrawings.Add(DrwRectangle.Create(new ChartPoint(Bars.Time[55], Bars.Low[55]), new ChartPoint(Bars.Time[45], Bars.High[45]))); } } private void UpdateLastRect() { if (myDrawings.Count > 0) { Output.WriteLine("myDrawings.Count {0}", myDrawings.Count); IRectangleObject robj = myDrawings.Last<IRectangleObject>(); ChartPoint before_update_begin = robj.Begin; ChartPoint before_update_end = robj.End; Output.WriteLine("Before update: before_update_begin {0}, before_update_end {1}", before_update_begin, before_update_end); DateTime before_update_begin_time = before_update_begin.Time; double before_update_begin_price = before_update_begin.Price; ChartPoint after_update_end = new ChartPoint(Bars.Time[15], Bars.High[15]); DateTime after_update_end_time = after_update_end.Time; double after_update_end_price = after_update_end.Price; /* try to update just the end chartpoints of a rectangle, does not work at all * //robj.End = after_update_end; // This fails with "System.ApplicationException: This was never displayed!" //robj.End = new ChartPoint(after_update_end_time, after_update_end_price); // This fails with "System.ApplicationException: This was never displayed!" * */ // delete and replace myDrawings[myDrawings.Count - 1].Delete(); Output.WriteLine("myDrawings.Count {0}", myDrawings.Count); myDrawings.Add(DrwRectangle.Create(new ChartPoint(before_update_begin_time, before_update_begin_price), new ChartPoint(after_update_end_time, after_update_end_price))); //myDrawings.Add(DrwRectangle.Create(before_update_begin, after_update_end)); // Does not work, syntax error robj = myDrawings.Last<IRectangleObject>(); // print out the last rect update Output.WriteLine("After update: robj.Begin {0}, robj.End {1}", robj.Begin, robj.End); } } protected override void OnMouseEvent(MouseClickArgs arg) { // Change the formatting of the text boxes with a mouse click + Shift if (arg.keys == Keys.Shift) { Output.WriteLine("Updating last Rectangle..."); UpdateLastRect(); } // Remove all drawings when a mouse click with Control and Shift happens else if (arg.keys == (Keys.Control | Keys.Shift)) { Output.Clear(); for (int i = 0; i < myDrawings.Count; i++) { myDrawings[i].Delete(); } } } } }


Return to “MultiCharts .NET”