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:
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:
usingcAlgo.API;usingSystem.Windows.Forms;namespaceWinFormsTest{[Indicator(IsOverlay = true, AccessRights = AccessRights.FullAccess)]publicclassWinFormsTest:Indicator{protectedoverridevoidInitialize(){varresult=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");}}publicoverridevoidCalculate(intindex){}}}
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.
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) ...'
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).
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.
Dalam contoh ini, kami telah membuat borang. Salin dan tampal kod di bawah ke dalam fail AccountIntoForm.Designer.cs:
namespaceWinForms_Test{partialclassAccountInfoForm{/// <summary>/// Required designer variable./// </summary>privateSystem.ComponentModel.IContainercomponents=null;/// <summary>/// Clean up any resources being used./// </summary>/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>protectedoverridevoidDispose(booldisposing){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>privatevoidInitializeComponent(){this.label1=newSystem.Windows.Forms.Label();this.NumberLabel=newSystem.Windows.Forms.Label();this.BrokerLabel=newSystem.Windows.Forms.Label();this.label3=newSystem.Windows.Forms.Label();this.CurrencyLabel=newSystem.Windows.Forms.Label();this.label5=newSystem.Windows.Forms.Label();this.BalanceLabel=newSystem.Windows.Forms.Label();this.label7=newSystem.Windows.Forms.Label();this.EquityLabel=newSystem.Windows.Forms.Label();this.label9=newSystem.Windows.Forms.Label();this.PositionsNumberLabel=newSystem.Windows.Forms.Label();this.label11=newSystem.Windows.Forms.Label();this.OrdersNumberLabel=newSystem.Windows.Forms.Label();this.label13=newSystem.Windows.Forms.Label();this.NetProfitLabel=newSystem.Windows.Forms.Label();this.label15=newSystem.Windows.Forms.Label();this.UpdateButton=newSystem.Windows.Forms.Button();this.SuspendLayout();// // label1// this.label1.AutoSize=true;this.label1.Font=newSystem.Drawing.Font("Segoe UI Variable Display",11.25F,System.Drawing.FontStyle.Bold,System.Drawing.GraphicsUnit.Point);this.label1.Location=newSystem.Drawing.Point(12,33);this.label1.Name="label1";this.label1.Size=newSystem.Drawing.Size(72,20);this.label1.TabIndex=0;this.label1.Text="Number:";// // NumberLabel// this.NumberLabel.AutoSize=true;this.NumberLabel.Font=newSystem.Drawing.Font("Segoe UI Variable Display",11.25F,System.Drawing.FontStyle.Bold,System.Drawing.GraphicsUnit.Point);this.NumberLabel.Location=newSystem.Drawing.Point(188,33);this.NumberLabel.Name="NumberLabel";this.NumberLabel.Size=newSystem.Drawing.Size(0,20);this.NumberLabel.TabIndex=1;// // BrokerLabel// this.BrokerLabel.AutoSize=true;this.BrokerLabel.Font=newSystem.Drawing.Font("Segoe UI Variable Display",11.25F,System.Drawing.FontStyle.Bold,System.Drawing.GraphicsUnit.Point);this.BrokerLabel.Location=newSystem.Drawing.Point(188,67);this.BrokerLabel.Name="BrokerLabel";this.BrokerLabel.Size=newSystem.Drawing.Size(0,20);this.BrokerLabel.TabIndex=3;// // label3// this.label3.AutoSize=true;this.label3.Font=newSystem.Drawing.Font("Segoe UI Variable Display",11.25F,System.Drawing.FontStyle.Bold,System.Drawing.GraphicsUnit.Point);this.label3.Location=newSystem.Drawing.Point(12,67);this.label3.Name="label3";this.label3.Size=newSystem.Drawing.Size(56,20);this.label3.TabIndex=2;this.label3.Text="Broker";// // CurrencyLabel// this.CurrencyLabel.AutoSize=true;this.CurrencyLabel.Font=newSystem.Drawing.Font("Segoe UI Variable Display",11.25F,System.Drawing.FontStyle.Bold,System.Drawing.GraphicsUnit.Point);this.CurrencyLabel.Location=newSystem.Drawing.Point(188,101);this.CurrencyLabel.Name="CurrencyLabel";this.CurrencyLabel.Size=newSystem.Drawing.Size(0,20);this.CurrencyLabel.TabIndex=5;// // label5// this.label5.AutoSize=true;this.label5.Font=newSystem.Drawing.Font("Segoe UI Variable Display",11.25F,System.Drawing.FontStyle.Bold,System.Drawing.GraphicsUnit.Point);this.label5.Location=newSystem.Drawing.Point(12,101);this.label5.Name="label5";this.label5.Size=newSystem.Drawing.Size(75,20);this.label5.TabIndex=4;this.label5.Text="Currency";// // BalanceLabel// this.BalanceLabel.AutoSize=true;this.BalanceLabel.Font=newSystem.Drawing.Font("Segoe UI Variable Display",11.25F,System.Drawing.FontStyle.Bold,System.Drawing.GraphicsUnit.Point);this.BalanceLabel.Location=newSystem.Drawing.Point(188,135);this.BalanceLabel.Name="BalanceLabel";this.BalanceLabel.Size=newSystem.Drawing.Size(0,20);this.BalanceLabel.TabIndex=7;// // label7// this.label7.AutoSize=true;this.label7.Font=newSystem.Drawing.Font("Segoe UI Variable Display",11.25F,System.Drawing.FontStyle.Bold,System.Drawing.GraphicsUnit.Point);this.label7.Location=newSystem.Drawing.Point(12,135);this.label7.Name="label7";this.label7.Size=newSystem.Drawing.Size(63,20);this.label7.TabIndex=6;this.label7.Text="Balance";// // EquityLabel// this.EquityLabel.AutoSize=true;this.EquityLabel.Font=newSystem.Drawing.Font("Segoe UI Variable Display",11.25F,System.Drawing.FontStyle.Bold,System.Drawing.GraphicsUnit.Point);this.EquityLabel.Location=newSystem.Drawing.Point(188,173);this.EquityLabel.Name="EquityLabel";this.EquityLabel.Size=newSystem.Drawing.Size(0,20);this.EquityLabel.TabIndex=9;// // label9// this.label9.AutoSize=true;this.label9.Font=newSystem.Drawing.Font("Segoe UI Variable Display",11.25F,System.Drawing.FontStyle.Bold,System.Drawing.GraphicsUnit.Point);this.label9.Location=newSystem.Drawing.Point(12,173);this.label9.Name="label9";this.label9.Size=newSystem.Drawing.Size(54,20);this.label9.TabIndex=8;this.label9.Text="Equity";// // PositionsNumberLabel// this.PositionsNumberLabel.AutoSize=true;this.PositionsNumberLabel.Font=newSystem.Drawing.Font("Segoe UI Variable Display",11.25F,System.Drawing.FontStyle.Bold,System.Drawing.GraphicsUnit.Point);this.PositionsNumberLabel.Location=newSystem.Drawing.Point(188,210);this.PositionsNumberLabel.Name="PositionsNumberLabel";this.PositionsNumberLabel.Size=newSystem.Drawing.Size(0,20);this.PositionsNumberLabel.TabIndex=11;// // label11// this.label11.AutoSize=true;this.label11.Font=newSystem.Drawing.Font("Segoe UI Variable Display",11.25F,System.Drawing.FontStyle.Bold,System.Drawing.GraphicsUnit.Point);this.label11.Location=newSystem.Drawing.Point(12,210);this.label11.Name="label11";this.label11.Size=newSystem.Drawing.Size(85,20);this.label11.TabIndex=10;this.label11.Text="Positions #";// // OrdersNumberLabel// this.OrdersNumberLabel.AutoSize=true;this.OrdersNumberLabel.Font=newSystem.Drawing.Font("Segoe UI Variable Display",11.25F,System.Drawing.FontStyle.Bold,System.Drawing.GraphicsUnit.Point);this.OrdersNumberLabel.Location=newSystem.Drawing.Point(188,250);this.OrdersNumberLabel.Name="OrdersNumberLabel";this.OrdersNumberLabel.Size=newSystem.Drawing.Size(0,20);this.OrdersNumberLabel.TabIndex=13;// // label13// this.label13.AutoSize=true;this.label13.Font=newSystem.Drawing.Font("Segoe UI Variable Display",11.25F,System.Drawing.FontStyle.Bold,System.Drawing.GraphicsUnit.Point);this.label13.Location=newSystem.Drawing.Point(12,250);this.label13.Name="label13";this.label13.Size=newSystem.Drawing.Size(69,20);this.label13.TabIndex=12;this.label13.Text="Orders #";// // NetProfitLabel// this.NetProfitLabel.AutoSize=true;this.NetProfitLabel.Font=newSystem.Drawing.Font("Segoe UI Variable Display",11.25F,System.Drawing.FontStyle.Bold,System.Drawing.GraphicsUnit.Point);this.NetProfitLabel.Location=newSystem.Drawing.Point(188,285);this.NetProfitLabel.Name="NetProfitLabel";this.NetProfitLabel.Size=newSystem.Drawing.Size(0,20);this.NetProfitLabel.TabIndex=15;// // label15// this.label15.AutoSize=true;this.label15.Font=newSystem.Drawing.Font("Segoe UI Variable Display",11.25F,System.Drawing.FontStyle.Bold,System.Drawing.GraphicsUnit.Point);this.label15.Location=newSystem.Drawing.Point(12,285);this.label15.Name="label15";this.label15.Size=newSystem.Drawing.Size(79,20);this.label15.TabIndex=14;this.label15.Text="Net Profit";// // UpdateButton// this.UpdateButton.Location=newSystem.Drawing.Point(273,340);this.UpdateButton.Name="UpdateButton";this.UpdateButton.Size=newSystem.Drawing.Size(75,23);this.UpdateButton.TabIndex=16;this.UpdateButton.Text="Update";this.UpdateButton.UseVisualStyleBackColor=true;this.UpdateButton.Click+=newSystem.EventHandler(this.UpdateButton_Click);// // AccountInfoForm// this.AutoScaleDimensions=newSystem.Drawing.SizeF(7F,15F);this.AutoScaleMode=System.Windows.Forms.AutoScaleMode.Font;this.ClientSize=newSystem.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+=newSystem.EventHandler(this.AccountInfoForm_Load);this.ResumeLayout(false);this.PerformLayout();}#endregionprivateSystem.Windows.Forms.Labellabel1;privateSystem.Windows.Forms.LabelNumberLabel;privateSystem.Windows.Forms.LabelBrokerLabel;privateSystem.Windows.Forms.Labellabel3;privateSystem.Windows.Forms.LabelCurrencyLabel;privateSystem.Windows.Forms.Labellabel5;privateSystem.Windows.Forms.LabelBalanceLabel;privateSystem.Windows.Forms.Labellabel7;privateSystem.Windows.Forms.LabelEquityLabel;privateSystem.Windows.Forms.Labellabel9;privateSystem.Windows.Forms.LabelPositionsNumberLabel;privateSystem.Windows.Forms.Labellabel11;privateSystem.Windows.Forms.LabelOrdersNumberLabel;privateSystem.Windows.Forms.Labellabel13;privateSystem.Windows.Forms.LabelNetProfitLabel;privateSystem.Windows.Forms.Labellabel15;privateSystem.Windows.Forms.ButtonUpdateButton;}}
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.
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:
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.
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.