Controls allow for interacting with a cBot/indicator. Using the below guide, you can easily create basic and advanced UI controls directly on a chart.
There are several built-in classes that represent popular controls such as buttons, text blocks, text boxes, and shapes. However, you can also create custom controls.
If you build the above indicator and create an instance, you should see a grey 'Click Me' button right at the centre of the chart.
The Difference Between Controls and Chart Objects¶
In an earlier section, we have already covered chart objects. Controls enable users to interact with cBots/indicators. Conversely, chart objects give the opportunity to draw something on the trade chart or in a detached indicator output window.
Chart controls derive from the ControlBase class while chart objects are derived from the ChartObject class.
Chart controls are positioned statically by using different alignment options. While chart objects can be positioned in exactly the same way, their position can also change dynamically depending on certain X and Y coordinates.
Similarly to chart objects, chart controls can be added both to the 'main' chart and/or any indicator output windows (if they exist). An example of such positioning is given below.
To make working with controls more convenient, you may want to place several controls into a distinct 'group' with its own position in the UI. To do this, you can use Panels class and its derivatives.
Think of a panel as a control with other controls as its contents. cTrader supports five different classes inheriting from the base Panels class (which itself inherits from the Control class).
Canvas
DockPanel
Grid
StackPanel
WrapPanel
Each of the above classes uses different panel layouts and positioning strategies as discussed below.
The canvas is a panel that allows for positioning controls based on certain X and Y coordinates.
Notably, the X and Y axes are different from the ones used by chart objects/drawings. The X and Y coordinates used by the Canvas class represent numeric values starting from (0, 0) from the top left corner of the chart.
usingcAlgo.API;namespaceChartControlsTest{[Indicator(IsOverlay = true, AccessRights = AccessRights.None)]publicclassChartControls:Indicator{[Parameter(DefaultValue = "Click Me")]publicstringText{get;set;}[Parameter(DefaultValue = 0)]publicdoubleLeft{get;set;}[Parameter(DefaultValue = 0)]publicdoubleTop{get;set;}[Parameter(DefaultValue = 0)]publicdoubleMargin{get;set;}[Parameter(DefaultValue = 10)]publicdoublePadding{get;set;}protectedoverridevoidInitialize(){varbutton=newButton{Text=Text,Left=Left,Top=Top,Margin=Margin,Padding=Padding};button.Click+=Button_Click;varcanvas=newCanvas();/* We add our button control to the canvas panel. */canvas.AddChild(button);// We add our canvas panel to the chart.Chart.AddControl(canvas);}privatevoidButton_Click(ButtonClickEventArgsobj){obj.Button.Text="You clicked me, thanks";}publicoverridevoidCalculate(intindex){}}}
Upon creating an instance of the above indicator, you should see the 'Click Me' button in the top left corner of the chart.
The Top property of a control determines its positioning on the Y-axis. In turn, the Left property defines its positioning on the X-axis.
The above code also uses the Padding and Margin properties. Padding refers to the distance between the control contents and its outer borders. Margin is the distance between the control and the borders of its 'parent'. The Padding and Margin properties are applicable to all panels, not just the canvas class. They are useful for managing spacing between your controls.
In the majority of cases, the Canvas class is used to group only a small number of controls.
The DockPanel class is used to 'dock' a control in a static location on the chart. There are four possible dock positions.
Top
Bottom
Left
Right
Each control has a Dock property. When using the DockPanel class, you can use this property to position a control inside a DockPanel. This is illustrated in the following example.
usingcAlgo.API;namespaceChartControlsTest{[Indicator(IsOverlay = true, AccessRights = AccessRights.None)]publicclassChartControls:Indicator{[Parameter(DefaultValue = Dock.Top)]publicDockTextBoxDock{get;set;}[Parameter(DefaultValue = Dock.Bottom)]publicDockButtonDock{get;set;}privateTextBox_textBox;protectedoverridevoidInitialize(){_textBox=newTextBox{Margin=5,Text="Write here...",ForegroundColor=Color.Yellow,Dock=TextBoxDock,Width=200};varbutton=newButton{Text="Tell what I wrote?",Dock=ButtonDock,Width=200};button.Click+=Button_Click;vardockPanel=newDockPanel{HorizontalAlignment=HorizontalAlignment.Center,VerticalAlignment=VerticalAlignment.Center,};dockPanel.AddChild(_textBox);dockPanel.AddChild(button);Chart.AddControl(dockPanel);}privatevoidButton_Click(ButtonClickEventArgsobj){obj.Button.Text=$"You wrote: {_textBox.Text}";}publicoverridevoidCalculate(intindex){}}}
When creating an instance of this indicator, you should see a dock panel in the centre of the chart containing a text field and a clickable button.
Note that dock panels can be populated either horizontally or vertically. You cannot use both of these alignments at the same time. The dock panel orientation is set automatically when setting up the first control in a DockPanel. If the Dock property of the first control is set to either Top or Bottom, the entire DockPanel will be oriented vertically, and vice versa.
Stack panels are one of the most frequently used controls due to their simplicity and usability. Stack panels align 'child' controls either horizontally or vertically one by one. As shown below, you only have to set up its orientation and the class will manage the remainder.
After creating an indicator instance, you should see a horizontal stack panel with two text fields and a 'Submit' button in the bottom-right corner of the main chart.
Wrap panels are mostly identical to stack panels. However, when there is not enough space to fit all elements of a wrap panel, it will automatically 'wrap' the remaining controls to the next line on the Y axis.
Think of grids as spreadsheets with a set number of columns and rows. When using grids, you can add controls to each separate cell.
As demonstrated below, when you create an instance of the Grid class, you pass the number of its rows and columns as integer arguments. When adding new 'children' or controls, you similarly have to specify the number of 'child' rows and columns.
When creating an instance of the above indicator, you should see a 10x2 grid right in the centre of the chart.
Positioning Controls Within Price and Time Coordinates¶
In addition to panel controls, cTrader allows for specifying price and time coordinates for controls directly in the chart area. The AddControl() and MoveControl() methods provide algo developers with this functionality.
Use the following method overloads to manage the coordinates of your chart controls depending on your preferences.
To add a chart control to the chart/indicator area on absolute bar index and price (x, y) coordinates.
To add a chart control to the chart/indicator area on absolute time (x) coordinate.
1
voidAddControl(ControlBasecontrol,DateTimetime)
To move a chart control to the chart/indicator area on absolute time (x) coordinate.
1
voidMoveControl(ControlBasecontrol,DateTimetime)
To add a chart control to the chart/indicator area on absolute bar index (x) coordinate.
1
voidAddControl(ControlBasecontrol,intbarIndex)
To move a chart control to the chart/indicator area on absolute bar index (x) coordinate.
1
voidMoveControl(ControlBasecontrol,intbarIndex)
To add a chart control to the chart/indicator area on absolute price (y) coordinate.
1
voidAddControl(ControlBasecontrol,doubley)
To move a chart control to the chart/indicator area on absolute price (y) coordinate.
1
voidMoveControl(ControlBasecontrol,doubley)
The ControlBase parameter class can include any other subordinate class (e.g., Control, Button, etc.) for the method to be called in accordance with the above signatures.
The following cBot example adds the 'Click me!' button above the last bar on the chart. The button is moved ahead together with every new bar added. After clicking on the button, a message box appears on the screen.
The number of root controls to be added on a chart is limited to 100 because of possible performance issues. This limitation applies only to one algorithm instance and controls attached to price and/or bars.
The Margin property defines the space between the borders of a Control object and its parent (e.g., a chart, a panel, a border, etc.).
In turn, the Padding property determines the space between the control contents and its borders. You can vary the property values so that they are all different for different sides.
usingcAlgo.API;namespaceChartControlsTest{[Indicator(IsOverlay = true, AccessRights = AccessRights.None)]publicclassChartControls:Indicator{[Parameter(DefaultValue = "Click Me")]publicstringText{get;set;}[Parameter(DefaultValue = 0)]publicdoubleLeft{get;set;}[Parameter(DefaultValue = 0)]publicdoubleTop{get;set;}[Parameter(DefaultValue = 0, Group = "Margin")]publicdoubleLeftMargin{get;set;}[Parameter(DefaultValue = 0, Group = "Margin")]publicdoubleTopMargin{get;set;}[Parameter(DefaultValue = 0, Group = "Margin")]publicdoubleRightMargin{get;set;}[Parameter(DefaultValue = 0, Group = "Margin")]publicdoubleBottomMargin{get;set;}[Parameter(DefaultValue = 5, Group = "Padding")]publicdoubleLeftPadding{get;set;}[Parameter(DefaultValue = 5, Group = "Padding")]publicdoubleTopPadding{get;set;}[Parameter(DefaultValue = 5, Group = "Padding")]publicdoubleRightPadding{get;set;}[Parameter(DefaultValue = 5, Group = "Padding")]publicdoubleBottomPadding{get;set;}protectedoverridevoidInitialize(){varbutton=newButton{Text=Text,Left=Left,Top=Top,Margin=newThickness(LeftMargin,TopMargin,RightMargin,BottomMargin),Padding=newThickness(LeftPadding,TopPadding,RightPadding,BottomPadding)};button.Click+=Button_Click;varcanvas=newCanvas();canvas.AddChild(button);Chart.AddControl(canvas);}privatevoidButton_Click(ButtonClickEventArgsobj){obj.Button.Text="You clicked me, thanks";}publicoverridevoidCalculate(intindex){}}}
An instance of this indicator will create a grey 'Click Me' button in the top-left corner of the chart. Modify the parameters in the 'Add instance'/'Modify parameters' windows to see how exactly different margin and padding values change how the control is displayed.
A custom control is a control that is, in essence, 'made up' of several pre-defined controls. In other words, it is a control for which other controls constitute its content.
Custom controls act as reusable classes similarly to built-in controls.
When using styles you can give a similar look to several different types of controls. This is particularly useful when dealing with a large (five or more) number of controls.
The Style class allows for setting values for different properties of controls such as Margin or BackgroundColor once. Afterward, you can reuse these values as a template for multiple other controls.
We can also create a consistent look for multiple controls without using the Style class at all.
usingcAlgo.API;namespaceChartControlsTest{[Indicator(IsOverlay = true, AccessRights = AccessRights.None)]publicclassChartControls:Indicator{protectedoverridevoidInitialize(){vartextBoxStyle=newStyle();textBoxStyle.Set(ControlProperty.ForegroundColor,Color.Red);textBoxStyle.Set(ControlProperty.Margin,5);textBoxStyle.Set(ControlProperty.FontFamily,"Cambria");textBoxStyle.Set(ControlProperty.FontSize,12);textBoxStyle.Set(ControlProperty.Width,150);// Here we change the foreground color to Yellow if mouse hover over the textboxtextBoxStyle.Set(ControlProperty.ForegroundColor,Color.Yellow,ControlState.Hover);varfirstTextBox=newTextBox{Text="Type...",Style=textBoxStyle};varsecondTextBox=newTextBox{Text="Type...",Style=textBoxStyle};varthirdTextBox=newTextBox{Text="Type...",Style=textBoxStyle};varpanel=newStackPanel{Orientation=Orientation.Vertical,HorizontalAlignment=HorizontalAlignment.Center,VerticalAlignment=VerticalAlignment.Center};panel.AddChild(firstTextBox);panel.AddChild(secondTextBox);panel.AddChild(thirdTextBox);Chart.AddControl(panel);}publicoverridevoidCalculate(intindex){}}}
The Image control can be used to display a locally stored image. The Image control uses the .NET Bitmap class, which means that it supports the majority of popular image formats such as the following.
.PNG
.JPG
.BMP
.GIF
.TIFF
Consult the .NET Bitmap class documentation to learn more about it.
To use an Image control, set its Source property to an image file data in a byte array (byte[]).
After launching an instance of the above indicator, you should see nothing new on the main chart. However, input a valid file path as a value of the 'Image File Path' parameter, and you should see the chosen image displayed in the centre of the screen.
You can also use your project resources for storing images and displaying them via the Image control.
To do so, open the project resources in Visual Studio and switch to the 'Resources' tab. In it, create a new resource and add an existing image.
Afterward, you will be able to this image as a source in any Image control via the Project_Name_Space.Properties.Resources._Image_Name property.
For an illustration, save the below image as image.png on your system.
As an example copy the below image and save it as "image.png" on your system:
Create a new indicator in cTrader, set its name to 'Image Sample', and open it via Visual Studio. Afterward, add the logo indicator project as a resource. To do so, right-click on your project, select 'Properties' and click on 'Resources', and, subsequently, 'Create and manage assembly resources'. Copy the logo.png file into the newly opened tab.
Copy the below code to your indicator main source code file.