Once we’ve retrieved a collection of trend lines from the chart, the most efficient way to work with them is with a C# loop. How do we do that in MultiCharts .NET?

Working with lines and their collection in MultiCharts .NET

Drawing MultiCharts .NET trend lines is done with DrwTrendLine.Create(), a method that returns a reference to the line made (MultiCharts, 2014; PowerLanguage .NET Help, n.d.). With that reference assigned to an ITrendLineObject interface variable, we can access the line’s properties and methods through that variable. And with those we can, for example remove, extend, relocate, or change a line’s appearance.

One way to work with a group of trend lines is by retrieving them from the chart. We do that with DrwTrendLine.GetTrendLineObjects(). That method returns a collection of lines and requires one argument: an EDrawingSource enumerated value that specifies the type of line to include (PowerLanguage .NET Help, n.d.). For more about this method, EDrawingSource, and the collection it returns, see retrieving a collection of MultiCharts .NET trend lines.

Once DrwTrendLine.GetTrendLineObjects() has returned the lines we’re interested in, we can work with them programmatically by using a C# loop. To see how, let’s consider a couple programming examples.

Looping over MultiCharts .NET trend lines with the C# foreach loop

An easy way to loop over the collection returned by DrwTrendLine.GetTrendLineObjects() is with a foreach loop. The syntax of the foreach statement declares a type and its iteration variable, together with the collection to loop over (Liberty & MacDonald, 2009; Sharp, 2013):

foreach (type iterationVariable in collectionExpression)
{
     // Statement(s) to execute on the
     // collection's elements
}

A foreach’s iteration variable automatically acquires the value of each element in the collection (Sharp, 2013), and is updated with the collection’s next element on each subsequent loop cycle (Sempf, Sphar, & Davis, 2010). This means we use this variable inside the loop to refer to the element that’s currently being looped over, and so its type needs to match the type of elements in the collection (Sharp, 2013).

Contrary to other C# loops, foreach doesn’t evaluate a condition. Instead, it goes through all elements in a collection, executing its code block for each element in turn (Dorman, 2010; Liberty & MacDonald, 2009). Foreach only stops when all elements have been looped over or until a jump statement terminates the loop (Dorman, 2010; Sharp, 2013).

Looping over trend lines with the foreach loop

Going through a collection of trend lines with foreach can be done as follows:

IEnumerable<ITrendLineObject> allTrendLines = 
    DrwTrendLine.GetTrendLineObjects(EDrawingSource.AnyTech);

foreach (ITrendLineObject line in allTrendLines)
{
    Output.WriteLine("Line begins at {0} and ends at {1}", 
        line.Begin.Price,
        line.End.Price);
}

//> Line begins at 1,12696 and ends at 1,1357
//> Line begins at 1,12662 and ends at 1,13564
//> Line begins at 1,1269 and ends at 1,13506
//> Line begins at 1,12694 and ends at 1,13568
//> Line begins at 1,12664 and ends at 1,13576
//> Line begins at 1,12707 and ends at 1,13614
//> Line begins at 1,12733 and ends at 1,1351
//> Line begins at 1,12711 and ends at 1,13546
//> Line begins at 1,12685 and ends at 1,135

We first create an IEnumerable<ITrendLineObject> collection object named allTrendLines that contains every trend line on the chart, which is returned by the DrwTrendLine.GetTrendLineObjects() method with the EDrawingSource.AnyTech enumerated value passed in. See retrieving a list of MultiCharts .NET trend lines for more on this method and the enumeration.

The foreach loop’s type is set to ITrendLineObject, its iteration variable named line, and the collection to loop over set to allTrendLines. In the loop we retrieve the price coordinates of the line currently being looped over with the line variable. The values of these Begin.Price and End.Price properties, which return data from the line’s ChartPoint structs (see PowerLanguage .NET Help, n.d.), are outputted to the PowerLanguage .NET Editor’s Output tab with the Output.WriteLine() method. We insert these values in the literal quoted string with placeholders (the numbers between { and }) that are replaced by the values following the string (Liberty & MacDonald, 2009; Sempf et al., 2010).

Tip: The IEnumerable<T> interface originates from the System.Collections.Generic namespace (Microsoft Developer Network [MSDN], n.d. a), which isn’t added by default to MultiCharts .NET scripts. Adding this namespace yourself with the using directive to the top of the script’s source code prevents having to precede IEnumerable<ITrendLineObject> with its namespace (Sharp, 2013). For more on IEnumerable<T>, see retrieving a list of MultiCharts .NET trend lines.

Using foreach to loop over trend lines efficiently

When the collection returned by DrwTrendLine.GetTrendLineObjects() only needs to be used in a foreach loop, we can skip creating an IEnumerable<ITrendLineObject> collection object and adding this interface’s namespace. This requires less code, like this:

foreach (ITrendLineObject line in 
    DrwTrendLine.GetTrendLineObjects(EDrawingSource.CurrentTech))
{
    Output.WriteLine("Line ID {0} extends to the left? {1} Or right? {2}",
        line.ID,
        line.ExtLeft,
        line.ExtRight);
}

//> Line ID 4 extends to the left? False Or right? False
//> Line ID 5 extends to the left? False Or right? False
//> Line ID 6 extends to the left? False Or right? False
//> Line ID 7 extends to the left? False Or right? False
//> Line ID 8 extends to the left? False Or right? False
//> Line ID 9 extends to the left? False Or right? False
//> Line ID 10 extends to the left? False Or right? False
//> Line ID 11 extends to the left? False Or right? False
//> Line ID 12 extends to the left? False Or right? False

This foreach loop has its type set to ITrendLineObject and its iteration variable is named line. Since this variable’s scope is limited to its foreach loop (Dorman, 2010), we can give it the same name as in the previous loop without running into naming conflicts.

The collection that this foreach loop goes through are all trend lines created by the current script, returned by DrwTrendLine.GetTrendLineObjects() with the EDrawingSource.CurrentTech value passed in (PowerLanguage .NET Help, n.d.). We use Output.WriteLine() inside the loop to output the trend line’s ID and its ExtLeft and ExtRight properties that indicate if the trend line is extended (PowerLanguage .NET Help, n.d.).

Looping with C#’s for loop through MultiCharts .NET trend lines

A limitation of the foreach loop is that its iteration variable is a read-only copy of the element in the collection, which makes removing elements from the collection impossible (Dorman, 2010; Sharp, 2013). So to delete all trend lines from the chart we need another type of loop, like C#’s for loop.

A for loop contains three parts: an iteration variable initialised before the loop begins, a Boolean expression that triggers a loop execution while true, and a part that updates the iteration variable (Albahari & Albahari, 2012; Liberty & MacDonald, 2009; Sharp, 2013):

for (iteration variable; Boolean expression; updating variable)
{
     // Statement(s) that go through a collection
}

After a for loop cycle, the iteration variable is updated and then the Boolean expression is evaluated again: when still true, the loop runs anew; otherwise, the loop stops (Sharp, 2013). So contrary to the foreach loop, a for loop runs until its Boolean expression is false (Dorman, 2010).

Looping over MultiCharts .NET trend lines with the for loop

Going through the collection returned by DrwTrendLine.GetTrendLineObjects() with a for loop can be done like this:

IEnumerable<ITrendLineObject> theTrendLines = 
    DrwTrendLine.GetTrendLineObjects(EDrawingSource.AnyTechOrManual);

for (int i = 0; i < theTrendLines.Count(); i++)
{
    Output.WriteLine("The line from {0} to {1} has a style of {2}",
        theTrendLines.ElementAt(i).Begin.Price,
        theTrendLines.ElementAt(i).End.Price,
        theTrendLines.ElementAt(i).Style);
}

//> The line from 1,12607 to 1,12642 has a style of ToolSolid
//> The line from 1,12632 to 1,12709 has a style of ToolSolid
//> The line from 1,12572 to 1,1267 has a style of ToolSolid
//> The line from 1,12621 to 1,12693 has a style of ToolSolid
//> The line from 1,12631 to 1,12654 has a style of ToolSolid
//> The line from 1,12634 to 1,12644 has a style of ToolSolid
//> The line from 1,12666 to 1,12654 has a style of ToolSolid
//> The line from 1,12693 to 1,12629 has a style of ToolSolid
//> The line from 1,12677 to 1,12702 has a style of ToolSolid

We first create a collection object named theTrendLines that’s assigned the value returned by DrwTrendLine.GetTrendLineObjects(). With the AnyTechOrManual argument (from the EDrawingSource enumeration) passed in, this method returns all trend lines made by any script or manually (PowerLanguage .NET Help, n.d.).

The for loop has its i loop variable start at 0 and continues as long as this variable’s value is less than (<) the number of elements in the collection, which is returned by the collection’s Count() method (MSDN, n.d. b). After each loop cycle, i is increased with 1 by the postfix increment operator (++). In this context, this operator is simply a shorthand way to write i = i + 1 (Dorman, 2010).

In the loop we use Output.WriteLine() to output the line’s begin price, end price, and its Style property that returns the line’s style (PowerLanguage .NET Help, n.d.). We access individual trend lines with the i loop variable and ElementAt(), a method that returns the collection’s element at the specified zero-based index (see MSDN, n.d. c).

Removing all trend lines with the C# for loop

Another use of C# loops is removing all trend lines from the chart:

foreach (ITrendLineObject line in
    DrwTrendLine.GetTrendLineObjects(EDrawingSource.AnyTech))
{
    Output.WriteLine("Line starting time: {0}, ending time {1}",
        line.Begin.Time,
        line.End.Time);
}

Output.WriteLine("Removing the trend lines with a for loop");

List<ITrendLineObject> allLines = 
    DrwTrendLine.GetTrendLineObjects(EDrawingSource.AnyTech).ToList();

for (int i = 0; i < allLines.Count; i++)
{
    allLines[i].Delete();
}

Output.WriteLine("Number of trend lines on the chart: {0}",
    DrwTrendLine.GetTrendLineObjects(EDrawingSource.AnyTech).Count());

//> Line starting time: 4-6-2015 6:46:00, ending time 4-6-2015 13:26:00
//> Line starting time: 4-6-2015 6:36:00, ending time 4-6-2015 13:16:00
//> Line starting time: 4-6-2015 6:26:00, ending time 4-6-2015 13:06:00
//> Line starting time: 4-6-2015 6:16:00, ending time 4-6-2015 12:56:00
//> Line starting time: 4-6-2015 6:06:00, ending time 4-6-2015 12:46:00
//> Line starting time: 4-6-2015 5:56:00, ending time 4-6-2015 12:36:00
//> Line starting time: 4-6-2015 5:46:00, ending time 4-6-2015 12:26:00
//> Line starting time: 4-6-2015 5:36:00, ending time 4-6-2015 12:16:00
//> Line starting time: 4-6-2015 5:26:00, ending time 4-6-2015 12:06:00
//> Removing the trend lines with a for loop
//> Number of trend lines on the chart: 0

In the first loop we output information about all trend lines that were created by any script, returned by DrwTrendLine.GetTrendLineObjects() with the EDrawingSource.AnyTech argument (PowerLanguage .NET Help, n.d.).

Inside that foreach loop we use Output.WriteLine() to print the line’s time coordinates to the PowerLanguage .NET Editor Output tab. We access those coordinates through the line’s Begin and End properties, which both return a ChartPoint chart coordinate that, in turn, has a Time property that returns the time value of line’s coordinate (see PowerLanguage .NET Help, n.d.).

The DrwTrendLine.GetTrendLineObjects() method returns a collection object that implements the IEnumerable<T> interface (PowerLanguage .NET Help, n.d.), which, however, only allows looping over the elements but not removing them (see Sharp, 2013). Furthermore, a foreach loop cannot remove elements from the collection it loops over (Dorman, 2010). As such, removing all trend lines from the chart first requires converting the collection returned by DrwTrendLine.GetTrendLineObjects() into another collection type, followed by looping over them with a loop type other than the foreach loop.

A collection that does allow removing elements is List<T> (see Sharp, 2013; Stellman & Greene, 2010). To transform a trend line collection into a generic list, we call the ToList() method on the DrwTrendLine.GetTrendLineObjects() method to convert this IEnumerable<ITrendLineObject> collection to a List<ITrendLineObject> list (see MSDN, n.d. d). We assign the result of this operation to the allLines variable.

Then a for loop begins at 0 and continues as long as its i loop variable is less than the number of lines in the collection (which is returned by the list’s Count property; Liberty & MacDonald, 2009). After each loop cycle, we add one to the value of the i variable with the postfix increment operator (++). In the loop we place i between square brackets ([ and ]) following the list’s variable (allLines) to access individual trend lines from the collection (see Liberty & MacDonald, 2009). We then remove an individual trend line by calling its Delete() method (PowerLanguage .NET Help, n.d.).

We output the number of lines on the chart when the loop ends. To do so, we again call DrwTrendLine.GetTrendLineObjects() with the EDrawingSource.AnyTech value as an argument to re-fetch the trend lines from the chart. On the collection returned by this method we call Count(), a method that returns the number of items in an IEnumerable<T> collection (MSDN, n.d. b). As the output shows, no programmatically drawn trend lines are left on the chart.

Working with a list of trend lines in MultiCharts .NET

Other examples of DrwTrendLine.GetTrendLineObjects() and C# loops are:

Loops are also used with creating custom trend line collections, like the List collection to hold trend lines and creating a Dictionary to map data series with trend lines.

Summary

The DrwTrendLine.Create() method draws a trend line and returns an ITrendLineObject reference, while DrwTrendLine.GetTrendLineObjects() returns a collection object that implements IEnumerable<T> and holds references to several trend lines. This collection can be looped over with several loops, like the foreach and for loops. Removing trend lines, however, cannot be done with foreach and also requires transforming the collection returned by DrwTrendLine.GetTrendLineObjects() into another collection type, like List<T>.


References

Albahari, J. & Albahari, B. (2012). C# 5.0 in a Nutshell: The Definitive Reference (5th edition). Sebastopol, CA: O’Reilly Media.

Dorman, S. (2010). Sams Teach Yourself Visual C# 2010 in 24 Hours. Indianapolis, IN: Sams/Pearson Education.

Liberty, J. & MacDonald, B. (2009). Learning C# 3.0: Master the Fundamentals of C# 3.0. Sebastopol, CA: O’Reilly Media.

Microsoft Developer Network (MSDN) (n.d. a). IEnumerable<T> Interface. Retrieved on June 3, 2015, from https://msdn.microsoft.com/en-us/library/9eekhta0%28v=vs.110%29.aspx

MSDN (n.d. b). Enumerable.Count<TSource> Method (IEnumerable<TSource>). Retrieved on June 4, 2015, from https://msdn.microsoft.com/en-us/library/bb338038%28v=vs.110%29.aspx

MSDN (n.d. c). Enumerable.ElementAt<TSource> Method. Retrieved on June 4, 2015, from https://msdn.microsoft.com/en-us/library/bb299233%28v=vs.110%29.aspx

MSDN (n.d. d). Enumerable.ToList<TSource> Method. Retrieved on June 4, 2015, from https://msdn.microsoft.com/en-us/library/bb342261%28v=vs.110%29.aspx

MultiCharts (2014). MultiCharts .NET Programming Guide (version 1.1). Retrieved from http://www.multicharts.com/downloads/MultiCharts.NET-ProgrammingGuide-v1.1.pdf

PowerLanguage .NET Help (n.d.). Retrieved on November 18, 2014, from http://www.multicharts.com/downloads/PowerLanguage.NET.chm

Sempf, B., Sphar, C., & Davis, S.R. (2010). C# 2010 All-In-One for Dummies. Hoboken, NJ: John Wiley & Sons.

Sharp, J. (2013). Microsoft Visual C# 2013 Step by Step. Microsoft Press.

Stellman, A. & Greene, J. (2010). Head First C#: A Brain-Friendly Guide (2nd edition). Sebastopol, CA: O’Reilly Media.