WinForms

イントロダクション

cTrader のインジケーター/cBots は .NET アプリケーションであるため、WinForms や WPF などの UI 関連のソリューションを含むすべての .NET テクノロジーを使用できます。cTrader は、ダイアログ、フォーム、Visual Studio WinForms デザイナー、サードパーティ製 UI コントロール、コンポーネントなど、WinForms および WPF が提供するすべての機能をサポートしています。

注記

WinForms や WPF を使用するアルゴリズムは、Windows マシンでのみ実行できます。

WinForms を使用するには、cTrader コンパイラ を .NET SDK コンパイラに変更する必要があります。

WinForms を使用する方法を紹介するために、カスタム WinForms ダイアログを作成します。これらは、アラートを表示したり、ユーザー入力を求めたりするのに便利です。

プロジェクトの設定方法

cBots/インジケーターで WinForms を使用するには、cBot/インジケーターのプロジェクトファイルにいくつかの変更を加える必要があります。WinForms は Windows でのみ機能するため、プロジェクトのインジケーター/cBot フレームワークのターゲットを Windows 用の .NET に変更する必要があります。

そのためには、Visual Studio でプロジェクトファイルを開き、内容を以下のように置き換えます。

 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>

ここでは、UseWindowsForms タグを追加し、TargetFramework の値を net6.0-windows に変更しました。

その後、AccessRights クラスのパラメーターの値を FullAccess に変更します。この変更を行わないと、WinForms ウィンドウを開くのに十分なアクセス権がありません。

上記の変更を行った後にプロジェクトを再構築してください。

ダイアログの使用方法

幸いなことに、WinForms にはエラーメッセージの表示、ユーザーからの確認取得、ファイル/フォルダーの選択など、さまざまなケースで便利な、いくつかの使いやすいダイアログが用意されています。

以下の例では、MessageBox クラスとファイルオープン/保存ダイアログを使用しています。

MessageBox

System.Windows.Forms.MessageBox クラスを、他の .NET アプリケーションで使用するのと同じ方法で使用できます。

以下のコードは、標準の確認ボックスをインジケーターに追加します。

 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)
        {
        }
    }
}

このインジケーターのインスタンスを実行すると、指定したテキストが表示された確認ボックスが即座に表示されるはずです。

ファイルの開く/保存ダイアログ

開く/保存ファイルダイアログを使用すると、cBots/インジケーターがローカルに保存されたファイルをブラウズして選択できます。

 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("ダイアログがキャンセルされました");
                }
            });

            thread.SetApartmentState(ApartmentState.STA);

            thread.Start();

            thread.Join();
        }

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

例では、ダイアログに関連するコードを実行するために別のスレッドを使用しています。これは、メインのcBot/インジケーターのスレッドがSTAスレッドでないためです。この問題については、次のセクションでさらに詳しく説明します。

UI用の専用スレッドの使用方法

WinFormsを参照するコードを実行する際には、STAマーキングされたスレッドを使用する必要があります。そうでないと、実行中にエラーが発生する可能性があります。詳細については、公式ドキュメントを参照してください。

既に実行中のスレッドのApartmentStateプロパティを変更することはできません。メインのcBot/インジケーターのスレッドはSTAマーキングされていないため、WinFormsおよびWPF用に新しいSTAマーキングされたスレッドを使用する必要があります。

前のセクションのコードでは、thread.Join()メソッドも呼び出しています。このメソッドは、UI関連のスレッドが解放されるまで(例えば、ユーザーが確認ダイアログで「OK」をクリックするまで)、メインのcBot/インジケーターのスレッドの実行をブロックします。

WindowsフォームやWPF要素を表示したい場合は、コードを別のSTAマーキングされたスレッドで実行する必要があります。

新しいカスタムフォームの作成方法

これまでのところ、組み込みのWinFormsクラスを使用してきました。しかし、カスタムクラスを作成することも可能です。

このセクションでは、現在アクティブな取引アカウントの情報(バランスやマージンなど)を表示するフォームを作成します。

新しいフォームを作成するには、Visual StudioでcBot/インジケーターを右クリックし、’Add‘をクリックして、’User Control (Windows Forms) …‘を選択します。

Image title

Visual Studioがダイアログウィンドウを開き、プロジェクトに追加するアイテムの種類を選択できます。’Form (Windows Forms)‘を選択します(VSは自動的に選択するはずです)。

Image title

その後、フォームの名前をForm1.csからAccountInfoForm.csに変更し、’Add‘ボタンをクリックします。

新しいフォームがプロジェクト内に作成され、IDEがVisual Studio WinFormsデザイナーを表示します。その結果、ツールボックスを使用してコントロールを追加するなど、すべての機能を使用できるようになります。

Image title

この例では、すでにフォームが作成されています。以下のコードを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;
    }
}

上記のコードは、WinFormsデザイナーによって生成されたものです。次に、新しいフォームのバックエンドコードを書きます。そのためには、フォームを右クリックし、’View Code‘を選択します。

Image title

以下の例をコピー&ペーストしてください。

 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();
    }
}

特に、私たちのコードは BeginInvokeOnMainThread() と BeginInvoke() メソッドを使用しています。この理由については、次のセクションで説明します。

とりあえず、カスタムフォームを ShowDialog() メソッドを使って表示します。以下のコードをインジケーターのソースコードにコピー&ペーストしてください。

 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)
        {
        }
    }
}

新しいフォームを追加すると、自動的にインジケーター/cBotの名前空間(cAlgo)が使用されます。エラーを避けるために、追加したフォームの名前空間を変更し、その後、インジケーター/cBotのソースコードファイルに追加してください。私たちの場合は、WinForms_Testが名前空間の指定です。

インジケーターをビルドしてインスタンスを作成すると、次のフォームが表示されるはずです。

画像タイトル

UI専用スレッドからAPIメンバーにアクセスする方法

これまで説明したように、WinFormsを実行するには、専用のスレッドを使用する必要があります。このスレッド上でのみ、フォームのコントロールやプロパティにアクセスできます。

すべてのアルゴ API メンバーにも同様のことが言えます。APIはスレッドセーフではありませんので、FormスレッドからAPIメンバーにアクセスすることはできません。代わりに、インジケーター/cBotコードでBeginInvokeOnMainThread() メソッドを使用して、このタスクをディスパッチする必要があります。

インジケーター/cBotとWinForm間でデータを交換する場合、複雑さが生じる可能性があります。この問題を解決するために、cBot/インジケーターとFormスレッド間の相互作用を管理する「プロキシ」クラスを作成することができます。

目次

このページについて