Langkau tajuk talian

WinForms

Oleh kerana indikator dan cBot cTrader merupakan aplikasi .NET, anda boleh menggunakan semua teknologi .NET semasa membuat mereka termasuk penyelesaian berkaitan UI seperti WinForms dan WPF. cTrader menyokong semua ciri yang ditawarkan oleh WinForms dan WPF termasuk dialog, borang, pereka WinForms Visual Studio, kawalan UI pihak ketiga dan komponen.

Nota

Algo yang menggunakan WinForms atau WPF hanya boleh dijalankan pada mesin Windows.

Untuk menggunakan WinForms, tukar penyusun cTrader anda kepada penyusun .NET SDK.

Kami akan menunjukkan bagaimana WinForms boleh digunakan dengan membuat dialog WinForms tersuai. Ia berguna untuk menunjukkan amaran atau meminta input pengguna.

Konfigurasi projek anda

Sebelum anda boleh menggunakan WinForms dalam cBot dan indikator anda, anda perlu membuat beberapa perubahan dalam fail projek cBot atau indikator anda. Oleh kerana WinForms hanya berfungsi pada Windows, anda perlu menukar sasaran rangka kerja indikator atau cBot projek anda kepada varian Windows .NET.

Untuk berbuat demikian, buka fail projek anda dalam Visual Studio dan gantikan kandungannya dengan yang berikut:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0-windows</TargetFramework>
    <UseWindowsForms>true</UseWindowsForms>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="cTrader.Automate" Version="1.*" />
  </ItemGroup>
</Project>

Kami telah menambah tag UseWindowsForms dan menukar nilai TargetFramework kepada net6.0-windows.

Selepas itu, tukar nilai parameter kelas AccessRights kepada FullAccess. Melainkan perubahan ini dibuat, sambungan anda tidak akan mempunyai hak akses yang mencukupi untuk membuka tetingkap WinForms.

Bina semula projek anda selepas membuat perubahan di atas.

Gunakan dialog

Nasib baik, WinForms mengandungi beberapa dialog sedia untuk digunakan yang berguna untuk pelbagai kes, terutamanya menunjukkan mesej ralat, mendapatkan pengesahan daripada pengguna atau memilih fail atau folder.

Contoh di bawah menggunakan kelas MessageBox dan dialog buka dan simpan fail.

MessageBox

Anda boleh menggunakan kelas System.Windows.Forms.MessageBox dengan cara yang sama seperti yang digunakan dalam mana-mana aplikasi .NET lain.

Kod di bawah melampirkan kotak pengesahan standard kepada indikator:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
using cAlgo.API;
using System.Windows.Forms;

namespace WinFormsTest
{
    [Indicator(IsOverlay = true, AccessRights = AccessRights.FullAccess)]
    public class WinFormsTest : Indicator
    {
        protected override void Initialize()
        {
            var result = System.Windows.Forms.MessageBox.Show("Are you sure you want confirm?", "Confirmation", MessageBoxButtons.OKCancel, MessageBoxIcon.Information);

            if (result == DialogResult.OK)
            {
                Print("OK");
            }
            else
            {
                Print("Cancel");
            }
        }

        public override void Calculate(int index)
        {
        }
    }
}

Selepas anda menjalankan contoh indikator ini, anda sepatutnya melihat kotak pengesahan dengan teks yang anda telah tentukan di atas dengan serta-merta.

Dialog buka atau simpan fail

Dialog buka atau simpan fail boleh digunakan untuk membolehkan cBot dan indikator melayari dan memilih fail yang disimpan secara tempatan.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
using cAlgo.API;
using System.Windows.Forms;
using System;
using System.Threading;

namespace WinFormsTest
{
    [Indicator(IsOverlay = true, AccessRights = AccessRights.FullAccess)]
    public class WinFormsTest : Indicator
    {
        protected override void Initialize()
        {
            var thread = new Thread(() =>
            {
                var dialog = new OpenFileDialog
                {
                    CheckFileExists = true,
                    CheckPathExists = true,
                    Filter = "Text Files (*.txt)|*.txt",
                    InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop),
                    Multiselect = true,
                    RestoreDirectory = true,
                };

                var result = dialog.ShowDialog();

                if (result == DialogResult.OK)
                {
                    foreach (var fileName in dialog.FileNames)
                    {
                        Print(fileName);
                    }
                }
                else
                {
                    Print("Dialog Canceled");
                }
            });

            thread.SetApartmentState(ApartmentState.STA);

            thread.Start();

            thread.Join();
        }

        public override void Calculate(int index)
        {
        }
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
using cAlgo.API;
using System.Windows.Forms;
using System;
using System.Threading;

namespace WinFormsTest
{
    [Indicator(IsOverlay = true, AccessRights = AccessRights.FullAccess)]
    public class WinFormsTest : Indicator
    {
        protected override void Initialize()
        {
            var thread = new Thread(() =>
            {
                var dialog = new SaveFileDialog
                {
                    CheckFileExists = true,
                    CheckPathExists = true,
                    Filter = "Text Files (*.txt)|*.txt",
                    InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop),
                    RestoreDirectory = true,
                };

                var result = dialog.ShowDialog();

                if (result == DialogResult.OK)
                {
                    Print(dialog.FileName);
                }
                else
                {
                    Print("Dialog Canceled");
                }
            });

            thread.SetApartmentState(ApartmentState.STA);

            thread.Start();

            thread.Join();
        }

        public override void Calculate(int index)
        {
        }
    }
}

Dalam contoh, kami telah menggunakan benang berasingan untuk menjalankan kod yang berkaitan dengan dialog kami. Ini dilakukan untuk mengambil kira fakta bahawa benang utama cBot atau indikator bukan benang STA. Kami membincangkan isu ini dengan lebih terperinci dalam bahagian berikutnya.

Gunakan benang khusus untuk UI

Apabila menjalankan kod yang merujuk kepada WinForms, anda perlu menggunakan benang yang ditandakan STA, jika tidak, mungkin terdapat ralat semasa pelaksanaan. Untuk maklumat lanjut, sila rujuk dokumentasi rasmi.

Anda tidak boleh menukar sifat ApartmentState benang yang sudah berjalan. Oleh kerana benang utama cBot atau indikator tidak ditandakan STA, anda perlu menggunakan benang baharu yang ditandakan STA untuk WinForms dan WPF.

Kod dalam bahagian sebelumnya juga memanggil kaedah thread.Join(). Ia menghalang pelaksanaan benang utama cBot atau indikator sehingga benang berkaitan UI dilepaskan (contohnya, apabila pengguna mengklik OK dalam dialog pengesahan).

Setiap kali anda ingin memaparkan Borang Windows atau elemen WPF, anda perlu menjalankan kod anda dalam benang berasingan yang ditandakan STA.

Buat borang tersuai baharu

Setakat ini, kami telah menggunakan kelas WinForms terbina dalam. Walau bagaimanapun, ia juga boleh membuat kelas tersuai. Dalam bahagian ini, kami akan membuat borang yang menunjukkan maklumat tentang akaun dagangan yang aktif pada masa ini seperti baki dan margin.

Untuk membuat borang baharu, klik kanan pada cBot atau indikator anda semasa dalam Visual Studio, klik Add dan pilih User Control (Windows Forms) ...'

Image title

Visual Studio akan membuka tetingkap dialog di mana anda boleh memilih jenis item yang anda ingin tambah kepada projek anda. Pilih Form (Windows Forms) (Visual Studio sepatutnya memilihnya secara automatik).

Image title

Selepas itu, tukar nama borang dari Form1.cs kepada AccountInfoForm.cs. Klik butang Add.

Borang baharu akan dibuat di dalam projek anda; IDE akan memaparkan pereka WinForms Visual Studio. Hasilnya, anda akan dapat menggunakan semua cirinya termasuk menambah kawalan melalui kotak alat.

Image title

Dalam contoh ini, kami telah membuat borang. Salin dan tampal kod di bawah ke dalam fail AccountIntoForm.Designer.cs:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
namespace WinForms_Test
{
    partial class AccountInfoForm
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.label1 = new System.Windows.Forms.Label();
            this.NumberLabel = new System.Windows.Forms.Label();
            this.BrokerLabel = new System.Windows.Forms.Label();
            this.label3 = new System.Windows.Forms.Label();
            this.CurrencyLabel = new System.Windows.Forms.Label();
            this.label5 = new System.Windows.Forms.Label();
            this.BalanceLabel = new System.Windows.Forms.Label();
            this.label7 = new System.Windows.Forms.Label();
            this.EquityLabel = new System.Windows.Forms.Label();
            this.label9 = new System.Windows.Forms.Label();
            this.PositionsNumberLabel = new System.Windows.Forms.Label();
            this.label11 = new System.Windows.Forms.Label();
            this.OrdersNumberLabel = new System.Windows.Forms.Label();
            this.label13 = new System.Windows.Forms.Label();
            this.NetProfitLabel = new System.Windows.Forms.Label();
            this.label15 = new System.Windows.Forms.Label();
            this.UpdateButton = new System.Windows.Forms.Button();
            this.SuspendLayout();
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Font = new System.Drawing.Font("Segoe UI Variable Display", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
            this.label1.Location = new System.Drawing.Point(12, 33);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(72, 20);
            this.label1.TabIndex = 0;
            this.label1.Text = "Number:";
            // 
            // NumberLabel
            // 
            this.NumberLabel.AutoSize = true;
            this.NumberLabel.Font = new System.Drawing.Font("Segoe UI Variable Display", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
            this.NumberLabel.Location = new System.Drawing.Point(188, 33);
            this.NumberLabel.Name = "NumberLabel";
            this.NumberLabel.Size = new System.Drawing.Size(0, 20);
            this.NumberLabel.TabIndex = 1;
            // 
            // BrokerLabel
            // 
            this.BrokerLabel.AutoSize = true;
            this.BrokerLabel.Font = new System.Drawing.Font("Segoe UI Variable Display", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
            this.BrokerLabel.Location = new System.Drawing.Point(188, 67);
            this.BrokerLabel.Name = "BrokerLabel";
            this.BrokerLabel.Size = new System.Drawing.Size(0, 20);
            this.BrokerLabel.TabIndex = 3;
            // 
            // label3
            // 
            this.label3.AutoSize = true;
            this.label3.Font = new System.Drawing.Font("Segoe UI Variable Display", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
            this.label3.Location = new System.Drawing.Point(12, 67);
            this.label3.Name = "label3";
            this.label3.Size = new System.Drawing.Size(56, 20);
            this.label3.TabIndex = 2;
            this.label3.Text = "Broker";
            // 
            // CurrencyLabel
            // 
            this.CurrencyLabel.AutoSize = true;
            this.CurrencyLabel.Font = new System.Drawing.Font("Segoe UI Variable Display", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
            this.CurrencyLabel.Location = new System.Drawing.Point(188, 101);
            this.CurrencyLabel.Name = "CurrencyLabel";
            this.CurrencyLabel.Size = new System.Drawing.Size(0, 20);
            this.CurrencyLabel.TabIndex = 5;
            // 
            // label5
            // 
            this.label5.AutoSize = true;
            this.label5.Font = new System.Drawing.Font("Segoe UI Variable Display", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
            this.label5.Location = new System.Drawing.Point(12, 101);
            this.label5.Name = "label5";
            this.label5.Size = new System.Drawing.Size(75, 20);
            this.label5.TabIndex = 4;
            this.label5.Text = "Currency";
            // 
            // BalanceLabel
            // 
            this.BalanceLabel.AutoSize = true;
            this.BalanceLabel.Font = new System.Drawing.Font("Segoe UI Variable Display", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
            this.BalanceLabel.Location = new System.Drawing.Point(188, 135);
            this.BalanceLabel.Name = "BalanceLabel";
            this.BalanceLabel.Size = new System.Drawing.Size(0, 20);
            this.BalanceLabel.TabIndex = 7;
            // 
            // label7
            // 
            this.label7.AutoSize = true;
            this.label7.Font = new System.Drawing.Font("Segoe UI Variable Display", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
            this.label7.Location = new System.Drawing.Point(12, 135);
            this.label7.Name = "label7";
            this.label7.Size = new System.Drawing.Size(63, 20);
            this.label7.TabIndex = 6;
            this.label7.Text = "Balance";
            // 
            // EquityLabel
            // 
            this.EquityLabel.AutoSize = true;
            this.EquityLabel.Font = new System.Drawing.Font("Segoe UI Variable Display", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
            this.EquityLabel.Location = new System.Drawing.Point(188, 173);
            this.EquityLabel.Name = "EquityLabel";
            this.EquityLabel.Size = new System.Drawing.Size(0, 20);
            this.EquityLabel.TabIndex = 9;
            // 
            // label9
            // 
            this.label9.AutoSize = true;
            this.label9.Font = new System.Drawing.Font("Segoe UI Variable Display", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
            this.label9.Location = new System.Drawing.Point(12, 173);
            this.label9.Name = "label9";
            this.label9.Size = new System.Drawing.Size(54, 20);
            this.label9.TabIndex = 8;
            this.label9.Text = "Equity";
            // 
            // PositionsNumberLabel
            // 
            this.PositionsNumberLabel.AutoSize = true;
            this.PositionsNumberLabel.Font = new System.Drawing.Font("Segoe UI Variable Display", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
            this.PositionsNumberLabel.Location = new System.Drawing.Point(188, 210);
            this.PositionsNumberLabel.Name = "PositionsNumberLabel";
            this.PositionsNumberLabel.Size = new System.Drawing.Size(0, 20);
            this.PositionsNumberLabel.TabIndex = 11;
            // 
            // label11
            // 
            this.label11.AutoSize = true;
            this.label11.Font = new System.Drawing.Font("Segoe UI Variable Display", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
            this.label11.Location = new System.Drawing.Point(12, 210);
            this.label11.Name = "label11";
            this.label11.Size = new System.Drawing.Size(85, 20);
            this.label11.TabIndex = 10;
            this.label11.Text = "Positions #";
            // 
            // OrdersNumberLabel
            // 
            this.OrdersNumberLabel.AutoSize = true;
            this.OrdersNumberLabel.Font = new System.Drawing.Font("Segoe UI Variable Display", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
            this.OrdersNumberLabel.Location = new System.Drawing.Point(188, 250);
            this.OrdersNumberLabel.Name = "OrdersNumberLabel";
            this.OrdersNumberLabel.Size = new System.Drawing.Size(0, 20);
            this.OrdersNumberLabel.TabIndex = 13;
            // 
            // label13
            // 
            this.label13.AutoSize = true;
            this.label13.Font = new System.Drawing.Font("Segoe UI Variable Display", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
            this.label13.Location = new System.Drawing.Point(12, 250);
            this.label13.Name = "label13";
            this.label13.Size = new System.Drawing.Size(69, 20);
            this.label13.TabIndex = 12;
            this.label13.Text = "Orders #";
            // 
            // NetProfitLabel
            // 
            this.NetProfitLabel.AutoSize = true;
            this.NetProfitLabel.Font = new System.Drawing.Font("Segoe UI Variable Display", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
            this.NetProfitLabel.Location = new System.Drawing.Point(188, 285);
            this.NetProfitLabel.Name = "NetProfitLabel";
            this.NetProfitLabel.Size = new System.Drawing.Size(0, 20);
            this.NetProfitLabel.TabIndex = 15;
            // 
            // label15
            // 
            this.label15.AutoSize = true;
            this.label15.Font = new System.Drawing.Font("Segoe UI Variable Display", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
            this.label15.Location = new System.Drawing.Point(12, 285);
            this.label15.Name = "label15";
            this.label15.Size = new System.Drawing.Size(79, 20);
            this.label15.TabIndex = 14;
            this.label15.Text = "Net Profit";
            // 
            // UpdateButton
            // 
            this.UpdateButton.Location = new System.Drawing.Point(273, 340);
            this.UpdateButton.Name = "UpdateButton";
            this.UpdateButton.Size = new System.Drawing.Size(75, 23);
            this.UpdateButton.TabIndex = 16;
            this.UpdateButton.Text = "Update";
            this.UpdateButton.UseVisualStyleBackColor = true;
            this.UpdateButton.Click += new System.EventHandler(this.UpdateButton_Click);
            // 
            // AccountInfoForm
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(360, 375);
            this.Controls.Add(this.UpdateButton);
            this.Controls.Add(this.NetProfitLabel);
            this.Controls.Add(this.label15);
            this.Controls.Add(this.OrdersNumberLabel);
            this.Controls.Add(this.label13);
            this.Controls.Add(this.PositionsNumberLabel);
            this.Controls.Add(this.label11);
            this.Controls.Add(this.EquityLabel);
            this.Controls.Add(this.label9);
            this.Controls.Add(this.BalanceLabel);
            this.Controls.Add(this.label7);
            this.Controls.Add(this.CurrencyLabel);
            this.Controls.Add(this.label5);
            this.Controls.Add(this.BrokerLabel);
            this.Controls.Add(this.label3);
            this.Controls.Add(this.NumberLabel);
            this.Controls.Add(this.label1);
            this.Name = "AccountInfoForm";
            this.Text = "Account Info";
            this.Load += new System.EventHandler(this.AccountInfoForm_Load);
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.Label NumberLabel;
        private System.Windows.Forms.Label BrokerLabel;
        private System.Windows.Forms.Label label3;
        private System.Windows.Forms.Label CurrencyLabel;
        private System.Windows.Forms.Label label5;
        private System.Windows.Forms.Label BalanceLabel;
        private System.Windows.Forms.Label label7;
        private System.Windows.Forms.Label EquityLabel;
        private System.Windows.Forms.Label label9;
        private System.Windows.Forms.Label PositionsNumberLabel;
        private System.Windows.Forms.Label label11;
        private System.Windows.Forms.Label OrdersNumberLabel;
        private System.Windows.Forms.Label label13;
        private System.Windows.Forms.Label NetProfitLabel;
        private System.Windows.Forms.Label label15;
        private System.Windows.Forms.Button UpdateButton;
    }
}

Kod di atas dijana oleh pereka WinForms. Kami akan menulis kod backend untuk borang baharu. Untuk berbuat demikian, klik kanan pada borang dan pilih View Code.

Image title

Salin dan tampal contoh berikut:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
using System;
using System.Linq;
using System.Windows.Forms;
using cAlgo.API;

namespace WinForms_Test
{
    public partial class AccountInfoForm : Form
    {
        private int _accountNumber;
        private string _brokerName;
        private string _currency;
        private double _balance;
        private double _equity;
        private int _positionsCount;
        private int _ordersCount;
        private double _netProfit;
        private readonly Indicator _indicator;

        public AccountInfoForm(Indicator indicator)
        {
            _indicator = indicator;

            InitializeComponent();
        }

        private void GetDataFromIndicator()
        {
            _indicator.BeginInvokeOnMainThread(() =>
            {
                _accountNumber = _indicator.Account.Number;
                _brokerName = _indicator.Account.BrokerName;
                _currency = _indicator.Account.Asset.Name;
                _balance = _indicator.Account.Balance;
                _equity = _indicator.Account.Equity;
                _positionsCount = _indicator.Positions.Count;
                _ordersCount = _indicator.PendingOrders.Count;
                _netProfit = _indicator.History.Sum(trade => trade.NetProfit);

                UpdateData();
            });
        }

        private void UpdateData()
        {
            _ = BeginInvoke(() =>
            {
                NumberLabel.Text = _accountNumber.ToString();
                BrokerLabel.Text = _brokerName;
                CurrencyLabel.Text = _currency;
                BalanceLabel.Text = Math.Round(_balance, 2).ToString();
                EquityLabel.Text = Math.Round(_equity, 2).ToString();
                PositionsNumberLabel.Text = _positionsCount.ToString();
                OrdersNumberLabel.Text = _ordersCount.ToString();
                NetProfitLabel.Text = Math.Round(_netProfit, 2).ToString();
            });
        }

        private void UpdateButton_Click(object sender, EventArgs e) => GetDataFromIndicator();

        private void AccountInfoForm_Load(object sender, EventArgs e) => GetDataFromIndicator();
    }
}

Terutamanya, kod kami menggunakan kaedah BeginInvokeOnMainThread() dan BeginInvoke(). Kami menjelaskan rasional kami untuk ini dalam bahagian berikutnya.

Buat masa ini, kami akan menggunakan borang tersuai kami dengan memanggil kaedah ShowDialog() dari indikator kami. Salin dan tampal kod di bawah ke dalam kod sumber indikator anda:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using cAlgo.API;
using System.Threading;
using WinForms_Test;

namespace WinFormsTest
{
    [Indicator(IsOverlay = true, AccessRights = AccessRights.FullAccess)]
    public class WinFormsTest : Indicator
    {
        protected override void Initialize()
        {
            var thread = new Thread(() =>
            {
                var form = new AccountInfoForm(this);

                _ = form.ShowDialog();
            });

            thread.SetApartmentState(ApartmentState.STA);

            thread.Start();
        }

        public override void Calculate(int index)
        {
        }
    }
}

Apabila anda menambah borang baharu, ia akan secara automatik menggunakan ruang nama indikator atau cBot anda (cAlgo). Untuk mengelakkan ralat, tukar ruang nama borang yang ditambah dan kemudian tambahkannya ke fail kod sumber indikator atau cBot anda. Dalam kes kami, WinForms_Test adalah penetapan ruang nama.

Selepas membina indikator dan membuat contoh, anda sepatutnya melihat borang berikut muncul.

Image title

Akses ahli API daripada bebenang khusus UI

Seperti yang telah kami jelaskan sebelum ini, anda perlu menggunakan bebenang khusus yang berasingan untuk menjalankan WinForms anda. Hanya kod yang dilaksanakan pada bebenang ini akan dapat mengakses kawalan dan sifat borang.

Perkara yang sama juga berlaku untuk semua ahli API algo. Oleh kerana API tidak selamat bebenang, anda tidak boleh mengakses ahli API daripada bebenang Form. Sebaliknya, anda perlu menghantar tugas ini dengan menggunakan kaedah BeginInvokeOnMainThread() dalam kod indikator atau cBot anda.

Ini mungkin menimbulkan komplikasi jika anda ingin bertukar data antara indikator atau cBot anda dan WinForm. Untuk menyelesaikan isu ini, anda boleh mencipta kelas proksi untuk menguruskan interaksi antara cBot atau indikator anda dan bebenang Form.