انتقل إلى المحتوى

WinForms

نظرًا لأن مؤشرات cTrader وcBots تشكل تطبيقات .NET، يمكنك استخدام جميع تقنيات .NET عند إنشائها بما في ذلك الحلول المتعلقة بواجهة المستخدم مثل WinForms وWPF. تدعم cTrader جميع الميزات التي تقدمها WinForms وWPF بما في ذلك مربعات الحوار والنماذج ومصمم WinForms في Visual Studio وعناصر تحكم ومكونات واجهة المستخدم من جهات خارجية.

ملاحظة

يمكن تشغيل الخوارزميات التي تستخدم WinForms أو WPF فقط على أجهزة Windows.

لاستخدام WinForms، قم بتغيير مترجم cTrader الخاص بك إلى مترجم .NET SDK.

سنوضح كيفية استخدام WinForms من خلال إنشاء مربع حوار WinForms مخصص. إنها مفيدة لعرض التنبيهات أو طلب إدخال المستخدم.

تكوين مشروعك

قبل أن تتمكن من استخدام WinForms في cBots والمؤشرات الخاصة بك، سيتعين عليك إجراء العديد من التغييرات في ملف (ملفات) مشروع 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("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. سنتناول هذه المشكلة بمزيد من التفصيل في القسم التالي.

استخدام خيط مخصص لواجهة المستخدم

عند تشغيل الكود الذي يشير إلى WinForms، يجب عليك استخدام خيط مميز بـ STA، وإلا قد تحدث أخطاء أثناء التنفيذ. لمزيد من المعلومات، يرجى الرجوع إلى الوثائق الرسمية.

لا يمكنك تغيير خاصية ApartmentState لخيط قيد التشغيل بالفعل. نظرًا لأن الخيط الرئيسي لـ cBot أو المؤشر غير مميز بـ STA، يجب عليك استخدام خيط جديد مميز بـ STA لـ WinForms وWPF.

يستدعي الكود في القسم السابق أيضًا طريقة thread.Join(). وهي تمنع تنفيذ الخيط الرئيسي لـ cBot أو المؤشر حتى يتم تحرير الخيط المتعلق بواجهة المستخدم (على سبيل المثال، عندما ينقر المستخدم على موافق في مربع حوار التأكيد).

كلما أردت عرض نموذج Windows أو عنصر WPF، يجب عليك تشغيل الكود الخاص بك في خيط منفصل مميز بـ STA.

إنشاء نموذج مخصص جديد

حتى الآن، استخدمنا فئات WinForms المدمجة. ومع ذلك، من الممكن أيضًا إنشاء فئات مخصصة. في هذا القسم، سننشئ نموذجًا يعرض معلومات عن حساب التداول النشط حاليًا مثل رصيده والهامش.

لإنشاء نموذج جديد، انقر بزر الماوس الأيمن على cBot أو المؤشر الخاص بك أثناء وجودك في Visual Studio، وانقر على إضافة واختر عنصر تحكم المستخدم (Windows Forms) ...

Image title

سيفتح Visual Studio نافذة حوار يمكنك فيها اختيار نوع العنصر الذي ترغب في إضافته إلى مشروعك. حدد نموذج (Windows Forms) (يجب أن يحدده Visual Studio تلقائيًا).

Image title

بعد ذلك، قم بتغيير اسم النموذج من Form1.cs إلى AccountInfoForm.cs. انقر على زر إضافة.

سيتم إنشاء نموذج جديد داخل مشروعك؛ ستعرض بيئة التطوير المتكاملة مصمم 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. سنقوم الآن بكتابة كود الواجهة الخلفية للنموذج الجديد. للقيام بذلك، انقر بزر الماوس الأيمن على النموذج واختر عرض الكود.

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 هو تعيين مساحة الاسم.

بعد بناء المؤشر وإنشاء نسخة، يجب أن ترى النموذج التالي يظهر.

Image title

الوصول إلى أعضاء API من سلسلة مخصصة لواجهة المستخدم

كما شرحنا سابقًا، يجب عليك استخدام سلسلة منفصلة مخصصة لتشغيل WinForms الخاصة بك. فقط الكود الذي يتم تنفيذه على هذه السلسلة سيكون قادرًا على الوصول إلى عناصر التحكم وخصائص النموذج.

ينطبق الشيء نفسه على جميع أعضاء API للخوارزميات. نظرًا لأن API غير آمن للسلاسل، لا يمكنك الوصول إلى أعضاء API من سلسلة Form. بدلاً من ذلك، يجب عليك إرسال هذه المهمة باستخدام طريقة BeginInvokeOnMainThread() في كود المؤشر أو cBot الخاص بك.

قد يؤدي هذا إلى تعقيدات إذا كنت ترغب في تبادل البيانات بين المؤشر أو cBot الخاص بك و WinForm. لحل هذه المشكلة، يمكنك إنشاء فئة وسيطة لإدارة التفاعلات بين cBot أو المؤشر الخاص بك وسلاسل Form.