เนื่องจากอินดิเคเตอร์และ cBots ของ cTrader เป็นแอปพลิเคชัน .NET คุณสามารถใช้เทคโนโลยี .NET ทั้งหมดเมื่อสร้างรวมถึงโซลูชัน UI ที่เกี่ยวข้องเช่น WinForms และ WPF cTrader รองรับคุณสมบัติทั้งหมดที่นำเสนอโดย WinForms และ WPF รวมถึงกล่องโต้ตอบ, ฟอร์ม, ตัวออกแบบ WinForms ของ Visual Studio, การควบคุม UI ของบุคคลที่สามและส่วนประกอบ
หมายเหตุ
อัลกอริทึมที่ใช้ WinForms หรือ WPF สามารถทำงานได้เฉพาะบนเครื่อง Windows เท่านั้น
หากต้องการใช้ WinForms ให้เปลี่ยนคอมไพเลอร์ cTrader ของคุณเป็นคอมไพเลอร์ .NET SDK
เราจะแสดงวิธีการใช้ WinForms โดยการสร้างกล่องโต้ตอบ WinForms ที่กำหนดเอง พวกมันมีประโยชน์สำหรับการแสดงการแจ้งเตือนหรือขอข้อมูลจากผู้ใช้
กำหนดค่าโปรเจกต์ของคุณ ก่อนที่คุณจะสามารถใช้ WinForms ใน cBots และอินดิเคเตอร์ของคุณ คุณจะต้องทำการเปลี่ยนแปลงหลายอย่างในไฟล์โปรเจกต์ cBot หรืออินดิเคเตอร์ของคุณ เนื่องจาก WinForms ทำงานได้เฉพาะบน Windows คุณจะต้องเปลี่ยนเป้าหมายของเฟรมเวิร์กอินดิเคเตอร์หรือ cBot ของโปรเจกต์ของคุณเป็นรุ่น Windows ของ .NET
หากต้องการทำเช่นนั้น ให้เปิดไฟล์โปรเจกต์ของคุณใน Visual Studio และแทนที่เนื้อหาด้วยสิ่งต่อไปนี้:
<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 )
{
}
}
}
หลังจากที่คุณเรียกใช้อินสแตนซ์ของอินดิเคเตอร์นี้ คุณควรเห็นกล่องยืนยันทันทีพร้อมข้อความที่คุณได้ระบุไว้ข้างต้น
กล่องโต้ตอบเปิดหรือบันทึกไฟล์ กล่องโต้ตอบเปิดหรือบันทึกไฟล์สามารถใช้เพื่อให้ cBot และอินดิเคเตอร์เรียกดูและเลือกไฟล์ที่จัดเก็บในเครื่อง
เปิดไฟล์ บันทึกไฟล์
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 )
{
}
}
}
ในตัวอย่าง เราได้ใช้เธรดแยกต่างหากเพื่อรันโค้ดที่เกี่ยวข้องกับกล่องโต้ตอบของเรา สิ่งนี้ทำเพื่อรองรับข้อเท็จจริงที่ว่าเธรดหลักของ cBot หรืออินดิเคเตอร์ไม่ใช่เธรด STA เราจะอธิบายรายละเอียดเพิ่มเติมเกี่ยวกับปัญหานี้ในส่วนถัดไป
ใช้เธรดเฉพาะสำหรับ UI เมื่อรันโค้ดที่อ้างอิงถึง WinForms คุณต้องใช้เธรดที่ทำเครื่องหมาย STA มิฉะนั้นอาจเกิดข้อผิดพลาดระหว่างการทำงาน สำหรับข้อมูลเพิ่มเติม โปรดดูที่ เอกสารอย่างเป็นทางการ
คุณไม่สามารถเปลี่ยนคุณสมบัติ ApartmentState ของเธรดที่กำลังทำงานอยู่ได้ เนื่องจากเธรดหลักของ cBot หรืออินดิเคเตอร์ไม่ได้ทำเครื่องหมาย STA คุณต้องใช้เธรดใหม่ที่ทำเครื่องหมาย STA สำหรับ WinForms และ WPF
โค้ดในส่วนก่อนหน้านี้ยังเรียกใช้เมธอด thread . Join () มันบล็อกการทำงานของเธรดหลักของ cBot หรืออินดิเคเตอร์จนกว่าเธรดที่เกี่ยวข้องกับ UI จะถูกปล่อย (เช่น โดยผู้ใช้คลิก OK ในกล่องโต้ตอบยืนยัน)
เมื่อใดก็ตามที่คุณต้องการแสดง Windows Form หรือองค์ประกอบ WPF คุณต้องรันโค้ดของคุณในเธรดแยกที่ทำเครื่องหมาย STA
สร้างฟอร์มกำหนดเองใหม่ จนถึงตอนนี้ เราได้ใช้คลาส WinForms ที่มีอยู่แล้ว อย่างไรก็ตาม ยังสามารถสร้างคลาสกำหนดเองได้ ในส่วนนี้ เราจะสร้างฟอร์มที่แสดงข้อมูลเกี่ยวกับบัญชีเทรดที่ใช้งานอยู่ในปัจจุบัน เช่น ยอดคงเหลือและมาร์จิ้น
ในการสร้างฟอร์มใหม่ ให้คลิกขวาที่ cBot หรืออินดิเคเตอร์ของคุณขณะอยู่ใน Visual Studio คลิก Add และเลือก User Control (Windows Forms) ...
Visual Studio จะเปิดหน้าต่างกล่องโต้ตอบซึ่งคุณสามารถเลือกประเภทของรายการที่คุณต้องการเพิ่มลงในโปรเจ็กต์ของคุณ เลือก Form (Windows Forms) (Visual Studio ควรเลือกให้โดยอัตโนมัติ)
หลังจากนั้น เปลี่ยนชื่อฟอร์มจาก Form1.cs เป็น AccountInfoForm.cs คลิกปุ่ม Add
ฟอร์มใหม่จะถูกสร้างขึ้นภายในโปรเจ็กต์ของคุณ IDE จะแสดงตัวออกแบบ WinForms ของ Visual Studio ผลลัพธ์คือคุณจะสามารถใช้คุณสมบัติทั้งหมดของมันรวมถึงการเพิ่มตัวควบคุมผ่านกล่องเครื่องมือ
ในตัวอย่างนี้ เราได้สร้างฟอร์มไว้แล้ว คัดลอกและวางโค้ดด้านล่างลงในไฟล์ 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
คัดลอกและวางตัวอย่างต่อไปนี้:
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 คือการกำหนดเนมสเปซ
หลังจากสร้างอินดิเคเตอร์และสร้างอินสแตนซ์ คุณควรเห็นฟอร์มต่อไปนี้ปรากฏขึ้น
เข้าถึงสมาชิก API จากเธรดเฉพาะสำหรับ UI ตามที่เราได้อธิบายไปก่อนหน้านี้ คุณต้องใช้เธรดแยกเฉพาะเพื่อรัน WinForms ของคุณ เฉพาะโค้ดที่ทำงานบนเธรดนี้เท่านั้นที่จะสามารถเข้าถึงตัวควบคุมและคุณสมบัติของฟอร์มได้
สิ่งเดียวกันนี้เป็นจริงสำหรับสมาชิก API ทั้งหมด เนื่องจาก API ไม่ปลอดภัยต่อเธรด คุณไม่สามารถเข้าถึงสมาชิก API จากเธรด Form ได้ แทนที่จะทำเช่นนั้น คุณต้องส่งงานนี้โดยใช้เมธอด BeginInvokeOnMainThread () ในโค้ดอินดิเคเตอร์หรือ cBot ของคุณ
สิ่งนี้อาจสร้างความซับซ้อนหากคุณต้องการแลกเปลี่ยนข้อมูลระหว่างอินดิเคเตอร์หรือ cBot และ WinForm ของคุณ เพื่อแก้ไขปัญหานี้ คุณอาจสร้างคลาสพร็อกซีเพื่อจัดการการโต้ตอบระหว่างเธรด cBot หรืออินดิเคเตอร์และ Form ของคุณ