Проект приложения с графическим интерфейсом windows c

В интернете можно обнаружить множество роликов, насколько легко было создавать графические приложения в Visual Studio 2010 через "Windows Forms Application". В VS2015 и VS2017 с инструментами для новичков стало сложнее. Создать приложение можно несколькими способами, наиболее простым является через "CLR Empty Project". Статья предназначена для новичков, но предполагает, что читатель уже не испытывает проблем с созданием консольных приложений вида "Hello World". Бесплатную VS Community 2017 можнос скачать с visualstudio.com.

В интернете можно обнаружить множество роликов, насколько легко было создавать графические приложения в Visual Studio 2010 через «Windows Forms Application». В VS2015 и VS2017 с инструментами для новичков стало сложнее. Создать приложение можно несколькими способами, наиболее простым является через «CLR Empty Project». Статья предназначена для новичков, но предполагает, что читатель уже не испытывает проблем с созданием консольных приложений вида «Hello World». Бесплатную VS Community 2017 можнос скачать с visualstudio.com.

Скриншот панели выбора компонентов для создания графического приложения1. В первую очередь перечислим, какие компоненты должны быть установлены для создания графического приложения на языке С++. Их три, все относятся к разделу «Desktop development with C++»:
— VC++ 2017 v141 toolset (x86,x64)
— Windows 10 SDK (10.0.15063.0) for Desktop C++ x86 and x64
— C++/CLI support

Первые два нужны для создания любого приложения на С++, третий именно для создания графической оболочки программы. Во время исследования данной темы мною подключалось два десятка других — пользы новичкам от них нету.

2. После установки среды разработки переходим
File > New > Project… (^+N)
В появившемся окне отыскиваем Installed > Visual C++ > CLR > CLR Empty Project
Окно выбора типа создаваемого приложения

Поля внизу заполняем традиционным для таких программ способом. В названии проекта лучше не допускать пробелов, иначе вам потом придётся мудрить в коде.

3. Необходимо добавить в приложение главную форму. Есть два равносильных пути достижения этой цели.
Первый: в «Solution Explorer» правой кнопкой мыши на названии проекта, во всплывшем контексном меню Add > New Item…
Второй способ: в главном меню выбираем Project > Add New Item…
Или просто нажимаем ^+A
Добавление в программу нового элемента (способ 1)     Добавление в программу нового элемента (способ 2)

Во появившемся окне Visual C++ > UI > Windows Form
выбор Windows Form

Главная форма программы создана. На некоторых компьютерах в данный момент возможно выскакивание ошибки 0x8000000A, в этом случае нужно просто закрыть вкладку.
Устранение ошибки 0x8000000A, иногда происходящей при создании формы

Эта ошибка хорошо известна ещё по Visual Studio 2015. Можете почитать её обсуждение, к примеру, на сайте Microsoft по ссылке1, ссылке2, ссылке3. И более лучшего решения, чем закрывать вкладку, ещё нет. По всей видимости, команда разработчиков Visual Studio не считает эту ошибку достаточно серьёзным делом, чтобы ломать о неё копья.

4. Но мало просто создать форму, нужно вплести её в создаваемую программу. Для этого в «Solution Explorer» правой кнопкой мыши на названии проекта, во всплывшем контексном меню выбрать Properties.
Выбор в контекстном меню элемента Свойства

В открывшемся окне произвести два действия.
Linker > System > SubSystem, из раскрывающегося списка выбрать «Windows (/SUBSYSTEM:WINDOWS)«
Выбор содсистемы (SubSystem)

Linker > Advanced > Entry Point. В пустое поле вписать «main» (без кавычек).
Прописывание точки привязки (Entry Point)

5. В «Solution Explorer» двойным щелчком открыть в редакторе файл MyForm.cpp. Скопировать в него текст

#include «MyForm.h»
using namespace System;
using namespace System::Windows::Forms;
[STAThread]
void main(array<String^>^ args) {
    Application::EnableVisualStyles();
    Application::SetCompatibleTextRenderingDefault(false);
    
Project1::MyForm form;
    Application::Run(%form);
}

Заменив Project1 на название вашего проекта. (Теперь понятно, почему ранее мною не рекомендовалось использовать в названии проекта пробелы?)
код в главной форме

Отлично, всё готово! Теперь проект компилируем и запускаем. Но если у вас ранее выскакивала 0x8000000A, то быстрее всего вам придётся перезапустить Visual Studio и вновь загрузить в нём проект. Далее ошибка ни в чём не проявится.

6. Для того, чтобы добавить на нашу только что созданную форму новые элементы, понадобится панель Toolbox. Полезно запомнить горячую клавишу ^!X
Вызов панели Toolbox, также показана сама панель

Работа с размещением элементов на форме сложностей вызвать не должна. Работает здесь всё удобнее, чем wxWidgets в CodeBlocks или wxDev-C++. Никаких глюков мною замечено не было.

Для изменения свойств только что созданного элемента интерфейса щёлкните на нём правой кнопкой и в контекстном меню выберите, соответственно, Properties.

Испытаем кнопку в работе. Сделаем так, чтобы по её названию появлялось окно с умным текстом. Двойной щелчок по элементу на форме вызовет редактор кода с уже прописанным в нём шаблоном. Добавим внутрь фигурных скобок команду

    MessageBox::Show(«Hello World»,
        «My heading», MessageBoxButtons::OKCancel,
        MessageBoxIcon::Asterisk);

Работающее простейшее графическое приложение, созданное в VS2017

Запускаем и проверяем!

Если вдруг не запустится, то первым делом проверяем, что выставлено в раскрывающемся списке Solution Configurations. Он находится на панели инструментов (под главным меню). Там должно быть Release (а не Debug).
Список Solution Configuration, выставленный на Release

Дополнительная информация

Альтернативные способы создания графических приложений в Visual Studio 2017.

1. UWP (Universal Windows Platfrom application) — универсальные приложения, способные запускаться на Windows 10, Windows 10 Mobile и аналогичных самых современных платформах от Microsoft. Платформа разработана как расширение Windows Runtime. Всё бы хорошо, но данные приложения не могут запускаться на более старых версиях Windows, даже на восьмёрке.

2. Windows Desktop Application — примерно то же самое, что в более VS2015 называлось «Win32 Project», а в VS2010 «Windows Forms Application». Технология, которую сейчас можно уже называть устаревшей. В VS2010 это был простой и удобный способ создания графических программ. Но затем ребята из Microsoft решили перетаскивать начинающих разработчиков на более современные технологии. При использовании данного инструмента в VS2015 и в VS2017 приложение создаётся, его можно запустить и посмотреть. Но у этого приложения нет готовой главной формы, куда можно было перетаскивать кнопки и прочие компоненты. Мне не удалось обнаружить в сети ни одного источника, где бы рассказывалось о лёгком способе её создать. Только через длинный код, для получения представления о процессе можете посмотреть видео на msdn.microsoft.com и прочитать соответствующее описание.

Пара слов для общего развития о нескольких технологиях, на которые вы будете постоянно натыкаться при чтении документации по разработке GUI в Visual Studio.

ATL (Active Template Library) — набор шаблонных классов языка C++, предназначенных для упрощения написания COM-компонентов.
MFC (Microsoft Foundation Classes) — библиотека объектов, помогающая профессиональным разработчикам создавать десктопные приложения. Что-то вроде более усложнённого и навороченного варианта ATL. ATL и MFC являются хорошими вещами, и с их задействованием также можно создавать графические приложения. Но это инструменты, требующие наличия углублённых знаний тематики.

IncrediBuild — технология ускорения компиляции и сборки приложений с помощью подключения дополнительных компьютеров. К методам создания графических интерфейсов это напрямую не относится.

Примечания:
В интернете можно наткнуться на заголовок «Full C and C++ IDE with Visual Studio».  Оттуда закачиваются те же самые стандартные дистрибутивы Visual Studio, проверено по контрольным суммам.

«Visual C++ 2017 Build Tools» — это комплект различных Windows SDK и .NET Framework. MFC, ATL и CLI support и ещё пара подобных штук. Все эти инструменты присутствуют и в стандартном загрузчике VS2017.

P.S.
Теперь можно немного поэкспериментировать с элементами интерфейса. Ниже показан код простейшего графического калькулятора:
Код и внешний вид простейшего графического калькулятора

А теперь можно попытаться сотворить что-то более похожее на стандартное window-приложение. Пока простейшее.
Простой стандандартный windows-калькулятор

Первое приложение с .NET CLI

Последнее обновление: 11.11.2022

Для создания графических интерфейсов с помощью платформы .NET применяются разные технологии — Window Forms, WPF, UWP.
Однако наиболее простой и удобной платформой до сих пор остается Window Forms или сокращенно WinForms. Данное руководство ставит своей целью дать понимание принципов создания графических интерфейсов с помощью технологии WinForms
и работы основных элементов управления.

Создадим первое приложение на C# и Windows Forms. Что нам для этого потребуется? Прежде всего необходим текстовый редактор для написания кода программы.
Можно взять любой понравившийся текстовый редактор, например, Visual Studio Code

Также для компиляции и запуска программы нам потребуется .NET SDK. Для его установки перейдем на официальный сайт по ссылке
.NET SDK

Загрузка .NET SDK для Windows Forms

После установки .NET SDK для первого проекта определим какую-нибудь папку. Например, в моем случае это будет папка C:dotnetwinformshelloapp.
Откроем терминал/командную строку и перейдем к созданной папке проекта с помощью команды cd

cd C:dotnetwinformshelloapp

В данном случае мы для создания и запуска проекта мы будем использовать встроенную инфраструктуру .NET CLI, которая устанавливается вместе с .NET SDK.

Для создания проекта в .NET CLI применяется команда dotnet new, после которой указывается тип проWindows Formsекта. Для создания проекта
Windows Forms применяется шаблон — winforms. Поэтому введем в терминале команду

Создание первого проекта Windows Forms и C# с помощью .NET CLI

После выполнения этой команды у нас будет создан следующий проект:

Первый проект Windows Forms на C# в Visual Studio Code

Структура проекта Windows Forms

Рассмотрим базовую структуру простейшего стандартного проекта Windows Forms:

  • helloapp.csproj: стандартный файл проекта C#, который соответствует назанию проекта (по умолчанию названию каталога) и описывает все его настройки.

  • helloapp.csproj.user: дополнительный файл проекта C#, который хранит специфичные для текущего пользователя настройки.

  • Form1.cs: содержит класс формы, которая по умолчанию запускается при старте приложения

  • Form1.Designer.cs: он содержит определение компонентов формы, добавленных
    на форму в графическом дизайнере (графический дизайнер Windows Forms на данный момент официально доступен только в Visual Studio)

  • Program.cs: определяет класс Program, который запускается при старте приложения и запускает форму Form1

Например, посмотрим на содержимое файла helloapp.csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net7.0-windows</TargetFramework>
    <Nullable>enable</Nullable>
    <UseWindowsForms>true</UseWindowsForms>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

</Project>

Для компиляции приложения Windows Forms указаны следующие настройки:

  • OutputType: определяет выходной тип проекта. Должен иметь значение WinExe — то есть выполняемое приложение с
    расширением exe под Windows

  • TargetFramework: определяет применяемую для компиляции версию фреймворка .NET. Поскольку при создании проекта
    была выбрана версия .NET 7, а сам проект зависит от компонентов Windows, то здесь должно быть значение net7.0-windows

  • Nullable: подключает в проект функционалность ссылочных nullable-типов

  • UseWindowsForms: указывает, будет ли проект использовать Windows Forms (для этого устанавливается значение true)

  • ImplicitUsings: подключает в проект функциональность неявно подключаемых глобальных пространств имен

Запуск проекта

Проект по умолчанию не представляет какой-то грандиозной функциональности, тем не менее этот проект мы уже можем запустить. Итак, запустим проект. Для этого выполним команду

запуск проекта Windows Forms и C# с помощью .NET CLI

При запуске запускается графическая форма, код которой определяет класс Form1:

Первое приложение на Windows Forms на С# с .NET CLI

Запуск приложения

Файл Program.cs определяет точку входа в приложение:

namespace helloapp;

static class Program
{
    [STAThread]
    static void Main()
    {
        ApplicationConfiguration.Initialize();
        Application.Run(new Form1());
    }    
}

Метод Main снабжен атрибутом [STAThread]. Этот атрибут, грубого говоря,
необходим для корректной работы компонентов Windows. В самом методе сначала вызывается метод

ApplicationConfiguration.Initialize()

который устанавливает некоторую базовую конфигурацию приложения

Затем вызывается метод

Application.Run(new Form1());

в который передается объект отображаемой по умолчанию на экране формы.

То есть, когда мы запустим приложение, сработает метод Main, в котором будет вызван метод Application.Run(new Form1()),
благодаря чему мы увидим форму Form1 на экране.

Определение формы

Теперь посмотрим на определение формы и немного изменим его. Для этого откроем файл Form1.cs в текстовом редакторе. По умолчанию он выглядит следующим образом:

namespace helloapp;

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
}

Класс формы — Form1 представляет графическую форму — фактически то окно, которое мы увидим на экране при запуске проекта.

Этот класс определяется как частичный (с модификатором partial) и наследуется от встроенного класса Form,
который содержит базовую функциональность форм.

В самом классе Form1 определен по умолчанию только конструктор, где вызывается метод InitializeComponent(), который выполняет инициализацию компонентов формы из файла дизайнера
Form1.Designer.cs. По сути именно код этого файла передается выше через вызов InitializeComponent()

Теперь изменим его код следующим образом:

namespace helloapp;

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        // определяем кнопку
        Button button = new Button();
        // текст кнопки
        button.Text ="Click";
        // положение кнопки
        button.Location = new Point(50, 50);
        // размер кнопки
        button.Size = new Size { Width = 80, Height = 30 };
        // обработчик нажатия кнопки
        button.Click += (o, e) => MessageBox.Show("Hello METANIT.COM!");
        // добавление кнопки на форму
        this.Controls.Add(button);
    }
}

В данном случае на форму добавляется кнопка, и у нее настраиваются различные свойства, как текст, положение на форме, размер, а также обработчик нажатия.
Заново запустим проект, и нам отобразится та же форма, которая теперь будет содержать кнопку. И по нажатию на кнопку появится маленькое окно с сообщением.

Первое приложение на Windows Forms на С#


Создание первого приложения с графическим интерфейсом

Доброго времени суток! В этом уроке мы создадим Ваше первое приложение с графическим интерфейсом в MS Visual Studio. Это будет своего рода «Hello World» для графических приложений. Скажу сразу, что использование Windows Forms — не единственный способ создания графических приложений (приложения с графическим интерфейсом пользователя) для C# программистов, но начать изучение лучше именно с этого. И так, запускаем Visual Studio.

Запустили? Тогда к делу! Идем в главное меню и выбираем пункт «Файл — Создать — Проект», как показано на рисунке ниже.

Создание нового проекта

Создание нового проекта

В появившемся окне:

  • в левой части выбираем «Шаблоны — Visual C# — Windows»;
  • в основной области выбираем элемент «Приложение Windows Forms»;
  • в нижней части окна вводим имя проекта и указываем его расположение на диске.

В общем, как показано на рисунке ниже.

Опции создания проекта

Опции создания проекта

Указали что нужно? Тогда нажимайте на кнопку «OK». Теперь вы должны увидеть примерно следующее (основные области выделены прямоугольниками):

Только что созданный проект

Только что созданный проект

На рисунке выше, я обозначил основные области: область дизайнера (слева вверху), область обозревателя решений (справа вверху) и область свойств (справа внизу). С этими областями, мы будем работать чаще всего.

В области дизайнера, сейчас располагается пустая «форма», это так называемое окно, в данном случае, главное окно нашей программы. В области свойств, отображаются свойства выделенного в дизайнере элемента, в данном случае, нашей формы, ну а область обозревателя решений, содержит файлы проекта, в том числи, относящиеся и к формам (окнам) программы. А теперь, давайте немного изменим нашу форму, и запустим это первое приложение.

Для этого, выделим в дизайнере форму (для этого, можно просто кликнуть левой кнопкой мыши по форме) и перейдем в блок свойств, в котором найдем строку «Text» (слово текст, ищем в левом столбце), как показано на рисунке ниже.

Свойство "Text" основной формы приложения

Свойство «Text» основной формы приложения

Обратите внимание, в левом столбце указано имя (название свойства), а в правом — его значение.

В данном случае, мы имеем дело с текстовым свойством, и его значение, отображается в заголовке окна, так что, давайте теперь укажем там что-то свое, например, что-то вроде: «Главное окно», как показано на рисунке ниже:

Установка свойства формы

Установка свойства формы

Теперь, можно собрать проект и запустить. Для этого идем в главное меню и выбираем пункт «Сборка — Собрать решение». А потом запускаем приложение, для этого выбираем пункт «Отладка — Запуск без отладки» в главном меню. В результате Вы должны увидеть окно следующее окно.

Окно первого графического приложения

Окно первого графического приложения

В следующем уроке, мы рассмотрим простую работу с дизайнером форм, и настройку элементов графического интерфейса, а на этот урок подошел к концу, мы создали первое графическое приложение, собрали и запустили его.

Перейти к следующему уроку

I strongly advise against using plain Win32 because it’s pretty hard to make it work OK in all situations, it’s pretty dull and tedious work and the Common Controls library isn’t that complete. Also, most of the work has been done for you.

Every time I end up doing plain Win32 I have to spent at least a couple of hours on the most trivial tasks because I have to look up all the parameters, flags, functions, macros and figure out how to hook them up properly. I’d generally prefer a simple drag-and-drop don’t-make-me-use-my-brains type of solution and just slam the thing together in 2 minutes.

As a lightweight toolkit I’d suggest omgui which has a clean and pretty API. It doesn’t, however, come with any tools.

If you need tool support, you’ll probably end up wanting to go for either MFC (resource editor built into Visual Studio) or Qt. I don’t know if wxWidgets has any tools, but I presume it has.

Edit: David Citron mentions that apparently the resource editor in Visual Studio generates Win32 compatible resource files, so that’s probably the preferred way to do things if you wanted to keep things simple.

I strongly advise against using plain Win32 because it’s pretty hard to make it work OK in all situations, it’s pretty dull and tedious work and the Common Controls library isn’t that complete. Also, most of the work has been done for you.

Every time I end up doing plain Win32 I have to spent at least a couple of hours on the most trivial tasks because I have to look up all the parameters, flags, functions, macros and figure out how to hook them up properly. I’d generally prefer a simple drag-and-drop don’t-make-me-use-my-brains type of solution and just slam the thing together in 2 minutes.

As a lightweight toolkit I’d suggest omgui which has a clean and pretty API. It doesn’t, however, come with any tools.

If you need tool support, you’ll probably end up wanting to go for either MFC (resource editor built into Visual Studio) or Qt. I don’t know if wxWidgets has any tools, but I presume it has.

Edit: David Citron mentions that apparently the resource editor in Visual Studio generates Win32 compatible resource files, so that’s probably the preferred way to do things if you wanted to keep things simple.

Создание
Windows-приложений в среде
Visual Studio

Цель
работы

Знакомство с методами визуального
программирования. Приобретение навыков
разработки приложений с графическим
интерфейсом в среде Visual
Studio
.Net
на
языке C#.

Ведение в
визуальное программирование

Визуальное
программирование
 —
способ создания программы для ЭВМ с
помощью манипулирования графическими
видимыми (визуальными) объектами вместо
написания программного кода. Одно из
основных назначений визуальных методов
программирования — разработка графических
интерфейсов пользователя приложения.

Средства для
организации взаимодействия с пользователем,
например окна, кнопки, меню и другие
элементы управления, называют графическим
интерфейсом пользователя (Graphical User
Interface, GUI). Windows — графическая операционная
система, поэтому говорят, что она
обеспечивает графический интерфейс
пользователя.

Средства визуальной
разработки служат сокращению затрат
времени и средств на разработку
приложения.

Среда быстрой
разработки визуального интерфейса
берет на себя рутинную работу, связанную
с подготовкой программы к работе,
автоматически генерирует соответствующий
программный код и позволяют сосредоточиться
не на оформлении интерфейса, а на логике
работы будущей программы.

Процесс
создания Windows-приложения
состоит из двух основных этапов:

1.
Визуальное
проектирование, то
есть задание внешнего облика приложения.

2.
Определение
поведения приложения
путем написания процедур обработки
различных событий.

Визуальное
проектирование
состоит в выборе и размещении различных
элементов управления, таких, как кнопки,
переключатели, значки и т.д. на плоскости
окна будущего приложения. Такие элементы
называются объектами (компонентами),
их можно перетаскивать в проектируемом
окне с помощью мыши. В итоге программисту
остается только написать программный
код, определяющий, что должна делать
программа при наступлении определенного
события:

  • • при щелчке мышью
    на той или иной кнопке;

  • • при выборе
    определенного пункта меню;

  • • по прошествии
    определенного интервала времени;

  • • и вообще при
    наступлении какого-либо иного события,
    которое может произойти с программой
    или с операционной системой, под
    управлением которой она работает.

Такое программирование
визуального интерфейса и программного
кода называют событийно-ориентированным.

Windows Forms
— это часть.NET Framework, которая поддерживает
создание приложений со стандартным GUI
на платформе Windows. Различные элементы
управления интерфейса (кнопки, меню,
переключатели, списки, элементы ввода
и т. д.) размещаются на форме. Форма —
это окно Windows. На этапе разработки
приложения форма – это экранный объект
или контейнер, на поверхности которого
размещаются компоненты. Когда
разрабатываемая программа будет
откомпилирована и запущена на исполнение,
форма превратится в обычное окно Windows
и будет реагировать на определенные в
ней события. Таких окон в программе
может быть сколько угодно.

Среда Visual Studio.NET
предоставляет большое количество
элементов, которые можно сгруппировать
по нескольким функциональным группам.

Таблица 3.3 — Основные
группы элементов управления

Категория

Интерфейсные
элементы

Редактирование
текста

TextBox,
RichTextBox

Отображение
текста

Label,
LinkLabel, StatusBar

Выбор
из списка

CheckedListBox,
ComboBox, DomainUpDown, ListBox,

ListView,
NumericUpDown, TreeView

Отображение
графики

PictureBox

Хранение
графики

ImageList

Ввод
значений

CheckBox,
CheckedListBox, RadioButton, TrackBar

Ввод
даты

DateTimePicker,
MonthCalendar

Диалоговые
панели

ColorDialog,
FontDialog, OpenFileDialog, PrintDialog,

PrintPreviewDialog,
SaveFileDialog

Создание
меню

MenuStrip,
ContextMenuStrip

Команды

Button,
LinkLabel, NotifyIcon, ToolBar

Объединение
компонентов

Panel,
GroupBox, TabControl

Создание приложения
с графическим интерфейсом на основе
Windows Forms

Для создания
Windows-приложения,
после запуска Microsoft
Visual
Studio,
выберите в меню пункт FileNewProject.
На экране появится окно вида:

В этом и последующем
окне создания проекта следует выбрать:

  • Project
    Types
    :
    Visual C#;

  • Templates:
    Windows
    Application
    ;

  • Name:
    пишем то имя, с которым будет сформирован
    .exe файл;

  • Location:
    директория в которой будет помещен
    проект;

  • Можно поставить
    галочку «Create Directory for Solution»
    или задать директорию для данного
    решения в выбранной папке (кнопка
    Browse…).

  • Нажимаем OK.

После выполнения
указанных действий экран примет вид:

На панелях
инструментов

находятся элементы управления
(компоненты), перетаскивая которые на
форму, можно создавать графический
интерфейс приложения Windows. Элементы
управления разбиты на категории с
логичными названиями, например Menus and
Toolbars (меню и панели инструментов), Data
(данные), Common Dialogs (диалоговые окна) и
т.д. Чтобы добавить элемент управления
на форму Windows, достаточно щелкнуть его
мышью и перетащить на форму.

Каждый компонент
является объектом в смысле
объектно-ориентированного программирования
– любой из них обладает свойствами и
методами обработки событий, связанными
с этими компонентами. Компонент,
расположенный на панели элементов,
является общим описанием класса.
Компонент, расположенный на форме,
является экземпляром этого класса.
Экземпляру можно придать индивидуальные
качества – определить индивидуальные
свойства и методы. Поэтому однотипные
элементы на форме могут иметь различный
вид и вызывать различные действия.

С помощью окна
свойств

можно просматривать и изменять свойства
и события, связанные с компонентами
приложения. Окно свойств является
контекстно-зависимым: пункты меню в нем
зависят от редактируемых разработчиком
параметров. Например, при изменении
цвета фона формы Windows появляется палитра
цветов, в которой можно выбрать другое
значение.

Окно свойств также
можно использовать для управления
событиями Events.
Чтобы сделать это, необходимо щелкнуть
значок с изображением молнии
,
расположенный в верхней его части. С
каждым событием (например, клик мышью)
из списка событий можно связать заданный
программный код, если дважды щелкнуть
имя события.

Рассмотрим процесс
разработки простого приложения с
графическим интерфейсом.

Шаг 1. Поместите
курсор мыши на закладку ToolBox
(Панель элементов). Панель элементов
раскроется как показано на рисунке.

Шаг 2.
Раскройте раздел Common
Controls
(Обычные элементы управления) и перетащите
элемент Button
на Форму.

Шаг 3.
Убедитесь что эта кнопка выделена в
форме и отображена в окне Properties
(Свойства). Содержимое в поле свойства
Text
измените на «Щелкните здесь».

Шаг 4.
Щелкните дважды по кнопке на форме. Это
должно вызвать редактор кода, где
необходимо вписать следующий код:

private void
button1_Click(object
sender, EventArgs e)

{

Form2 obj = new Form2();

obj.Show();

Этот код

вписываем
сами

}

Шаг 5.
Добавьте вторую форму к Вашему проекту
для этого щелкните правой кнопкой мыши
на имени решения в Solution
Explorer
(Обозревателе решений). В появившемся
меню выберите Add
(Добавить) и затем Windows
Form
(Форму Windows
Form).

Шаг 6. В
появившемся диалоговом окне Add
New
Item
(Добавление нового элемента) выберите
Windows
Form
и нажмите Add
(Добавить). Появится новая форма. В
Обозревателе решений (Solution
Explorer)
появится новый пункт – Form2.cs

Шаг 7.
Выберите вторую форму. В окне Properties
(Свойства) изменить свойство FormBorderStyle
на FixedToolWindow.

Шаг 8.
Из панели элементов перетащите на форму
элемент Label.

Шаг 9.
Измените свойство Text
объекта Label1
на «Сообщение для Вас. Щелкните по
кнопке ниже, чтобы получит его».

Шаг 10.
Перетащите на форму элемент Button
и измените свойство Text
на «Click
Me».

Шаг 11.
Щелкните дважды на кнопке — раскроется
редактор кода:

private void
button1_Click(object
sender, EventArgs e)

{

MessageBox.Show(«Всем
привет от C#»);

Этот код

вписываем
сами

}

Шаг 12.
Сохраните и запустите проект на компиляцию
и исполнение.

Рассмотрим пример
разработки Windows-
приложения, вычисляющего одно из трех
выражений:

;

,

,

в зависимости от
желания пользователя.

Шаг 1.
Создайте новое Windows-приложение
С#.

Шаг 2.
Установите на форму элементы управления
как это показано на рисунке

Шаг 3.
Измените свойство Text
элементов label1,
label2,
label3
и label4
на x
=
, y
=
, Функция
и F
=
соответственно.

Шаг 4.
Измените свойство Text
элемента button1
на текст Вычислить,
button2
Выход.

Шаг 5.
Измените свойство Text
элемента comboBox1
на текст Выбор
функции
.

Затем нажмите на
кнопке

рядом со свойством Items
элемента comboBox1
и введите три строки: F1,
F2
и F3.

Шаг 6.
Щелкните дважды на кнопке Выход,
чтобы раскрылось окно редактора кода
и впишите в обработчик события
button2_Click
код: Close(
);
Этот
оператор обеспечит завершение работы
приложения при нажатии на кнопку
button2.

Шаг 7. Вернитесь
к редактору формы и щелкните дважды на
кнопке Вычислить.
Впишите в обработчик события button1_Click
следующий код:

private
void button1_Click(object sender, EventArgs e)

{

double
F=0,x,y;

if
(comboBox1.SelectedIndex == -1)

{

MessageBox.Show(«Вы
не
выбрали
вычисляемую
функцию»);

}

else

{

try

{

x
= Convert.ToDouble(textBox1.Text);

y
= Convert.ToDouble(textBox2.Text);

switch
(comboBox1.SelectedIndex)

{

case
0:

F
= (Math.Sin(x) * Math.Pow(y — 2, 0.5) )/ (x + 1.0);

break;

case
1:

F
= Math.Cos(x) / x * y * y;

break;

case
2:

F
= Math.Pow(x, 3.0 / 2.0) / 7 * y;

break;

}

textBox3.Text
= Convert.ToString(F);

}

catch
(System.FormatException)

{

MessageBox.Show(«Неверный
формат
введенных
данных»);

}

}

}

Здесь
сначала проверяется выбор пользователем
вида вычисляемой функции в элементе
comboBox1.

Блок try
содержит защищаемый код, в котором могут
происходить исключения — ошибки.
При возникновении
исключения среда CLR ищет оператор catch,
который обрабатывает это исключение.

Для определения
значений параметров x
и y
программный код обработчика обращается
к свойству Text
соответствующих элементов ввода данных
textBox1
и textBox2
и с помощью метода ToDouble()
пытается преобразовать текст в
вещественное значение.

После вычисления
функции F
результат выводится в элемент textBox3.
Для этого вещественное значение F
преобразуется в текст с помощью метода
ToString()
и вносится в свойство Text
элемента textBox3.

Шаг 8.
Запустите приложение и проверьте его
работоспособность.

Порядок выполнения
работы

  1. Изучить теоретическую
    часть

  2. Повторите процесс
    создания приложения с графическим
    интерфейсом, описанный во 2-м примере.

  3. Разработайте одно
    приложение с графическим интерфейсом
    для решения задач предыдущей работы,
    выполненных в форме консольных
    приложений.

  4. Пояснить применение
    принципов событийно-ориентированного
    подхода при разработке программы.

  5. Разберите
    программный код обработчиков событий
    в созданном приложении.

  6. Ответить на
    контрольные вопросы

  7. Защитить работу
    у преподавателя.

Контрольные
вопросы

  1. Что такое визуальное
    программирование?

  2. Какие цели
    достигаются с помощью средств визуальной
    разработки?

  3. Какие приемы
    используют при конструировании
    графического интерфейса с помощью
    средств визуальной разработки?

  4. Что такое форма,
    компонент?

  5. Какие этапы
    включает в себя разработка GUI
    приложения?

  6. Какие основные
    компоненты применяются в среде Visual
    Studio
    .Net
    на языке C#
    и каково их назначение?

  7. Почему компоненты,
    принадлежащие одному классу, способны
    по-разному выглядеть и вызывать разную
    реакцию приложения?

  8. Какие свойства и
    методы определены для компонента
    Button?

  9. Какие компоненты
    применяются для ввода и вывода данных?

  10. Какие события
    способны обрабатывать компоненты?

  11. Какие средства
    Visual
    Studio
    .Net
    применяются для настройки компонентов
    формы?

  12. Как
    написать/отредактировать программный
    код обработчика события?

10 апреля 2020

Время чтения: 5 минут

Превью к статье о создании C++ Windows Forms проекта

Windows Forms — интерфейс программирования приложений, отвечающий за графический интерфейс пользователя. Он является частью .Net Framework и создан для того, чтобы упростить взаимодействие пользователя с элементами Win API. Причём не просто упростить, а буквально полностью скрыть низкоуровневое взаимодействие с графическими элементами путём создания набора базовых компонентов и классов. При этом используемые классы не привязаны к языку разработки, благодаря чему данный проект может использоваться как на родном для Microsoft C#, так и на других языках, например, C++, VB Net и F#. Но не смотря на свою кроссплатформенность в мире языков программирования, Windows Forms проекты легко создаются на C#, однако при попытке создания проекта на C++ возникает множество проблем.

Шаг 0. А вдруг получится сразу?

В настоящее время IDE, поддерживающих Windows forms, не так много — буквально одна только Visual Studio, более известная как просто «студия». Поэтому будем рассматривать создание и решение проблем именно в этой среде разработки. Первым шагом запустим студию, начнём создавать новый проект и попытаемся найти Windows forms проект для C++:

Создаём новый проект в студии

Создаём новый проект в студии

Ищем Winfows Forms для C++

Ищем Winfows Forms для C++

Если у вас более старая версия Visual Studio, то интерфейс будет выглядеть немного иначе, однако данная функциональность будет той же. Также не исключено, что у Вас может быть данный тип проекта для C++ (на некоторых версиях формы для C++ были доступны сразу после установки IDE). Если же у Вас, как и у нас поиск не дал нужных результатов, то переходим к следующему шагу.

Шаг 1. Создание CLR проекта

Поскольку непосредственно Windows Forms проекта у нас не оказалось, мы обхитрим студию и создадим пустой CLR проект на С++. Для этого в том же окне поиска необходимо найти и выбрать Новый CLR проект, ввести имя (если нужно, то поменять директорию расположения проекта) и немного подождать, пока студия сделает свою работу.

Ищем пустой CLR проект (.Net Framework)

Ищем пустой CLR проект (.Net Framework)

Создаём новый пустой CLR проект

Создаём новый пустой CLR проект

В результате Visual Stido создаст новый C++ CLR проект, который будет выглядеть примерно так:

Результат создания нового CLR проекта

Результат создания нового CLR проекта

Шаг 2. Добавить форму

Чтобы сделать CLR проект проектом Windows Forms, нужно просто добавить в него форму. Для этого в верхнем меню нужно выбрать ПроектДобавить новый элемент и в появившемся окне выбрать категорию Visual C++UI и затем выбрать Форма Windows Forms.

Проект -> Добавить новый элемент

Проект -> Добавить новый элемент

Visual C++ -> UI -> Форма Windows Forms

Visual C++ -> UI -> Форма Windows Forms

После данной операции нас ждёт разочарование в виде ошибки Исключение из HRESULT: 0x8000000A:

Вместо формы получили ошибку

Вместо формы получили ошибку

Шаг 3. Исправляем появившуюся ошибку

Данная ошибка появляется из-за того, что для создания окна формы приложению необходима основная программа, создающая форму и переключающая управление на неё, однако после добавления новой формы файл Form1.cpp предсказуемо создаётся пустым. Поэтому необходимо добавить основную программу в файл с формой:

#include "Form1.h"

#include <Windows.h>

using namespace имя_вашего_проекта;

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) {
    Application::EnableVisualStyles();
    Application::SetCompatibleTextRenderingDefault(false);
    Application::Run(gcnew Form1);
    return 0;
}

В результате код файла Form1.cpp будет выглядеть следующим образом:

Добавление основной программы к форме

Добавление основной программы к форме

Шаг 4. Переоткрыть проект

Всё, что теперь осталось сделать — это закрыть текущее решение, а затем открыть его снова. Для этого можно закрыть саму студию, а можно выбрать в верхнем меню ФайлЗакрыть решение, после чего в появившемся окне выбрать только что созданный проект и открыть его заново.

Форма создалась, можно добавлять компоненты

Форма создалась, можно добавлять компоненты

Благодаря добавленному коду основной программы, студия сможет создать форму и на экране появится изображение формы, на которую можно будет добавлять компоненты, задавать их свойства, а также свойства самой формы.

Фото Перминова Андрея, автора этой статьи

Программист, сооснователь programforyou.ru, в постоянном поиске новых задач и алгоритмов

Языки программирования: Python, C, C++, Pascal, C#, Javascript

Выпускник МГУ им. М.В. Ломоносова

Введение

С октября 2018 года MQL5 стал нативно поддерживать интеграцию с библиотеками Net Framwork. Нативная поддержка означает что типы, методы и классы, размещенные в библиотеке .Net теперь, доступны из MQL5 программы напрямую, без предварительной декларации вызывающих функций и их параметров, а также сложного приведения типов двух языков друг к другу. Это действительно может считаться определенным прорывом, т.к. теперь гигантская кодовая база .Net Framework и мощь языка C# доступна практически «из коробки» всем пользователям MQL5.

Возможности Net Framework не ограничиваются только ей самой. Благодаря интегрированной, условно бесплатной, среде разработки VisualStudio, создание многих вещей становится гораздо проще. Например, с ее помощью в режиме drag-n-drop можно создать полноценное приложение windows, с формой и элементами на ней, которые будут вести себя привычным образом как и любое другое графическое windows-приложение. Это то, чего так не хватало в MQL.

Конечно, за годы существования этого языка, появилась ни одна и не две библиотеки, существенно облегчающие построение графического приложения внутри MQL программы. Однако, все эти библиотеки, как бы хороши они не были, представляют из себя набор кода, принцип работы с которым нужно понимать, а также уметь его интегрировать с кодом своих советников и индикаторов. Иными словами, пользователи, не знакомые с программированием, едва ли могли использовать эти библиотеки на практике. Разрыв между простотой создания форм в Visual Studio и сложностью конфигурирования графических библиотек в MQL, сохранялся бы и поныне, однако благодаря новой интеграции с библиотеками .Net Framework, простое создание форм становится реальностью. 

Данная статья посвящена построению пользовательских графических интерфейсов для торговых экспертов или индикаторов, написанных на MQL5. Под графическими интерфейсами мы будем понимать стандартные windows формы, содержащие тот или иной набор, опять-таки, стандартных графических элементов, каждый из которых будет тесно взаимосвязан с торговой логикой самого эксперта.

Для создания графического приложения, нам понадобится интегрировать MQL-программу с библиотеками .Net. О том, как это сделать и как вообще это работает, также будет подробно описано в данной статье. Поэтому она будет интересна не только тем, кто хочет создать какую-либо графическую форму для своей mql программы, но и тем, кто интересуется темой интеграции со сторонней кодовой базой, написанной на .Net.

Особый упор в статье сделан на простоту предложенного подхода. Основная задача сделать как можно более простое взаимодействие с кодом, написанным на C#. При этом само взаимодействие организовать таким образом, чтобы код, написанный на C#, создавался автоматически, без участия пользователя! Благодаря развитым языковым средствам C#, а также богатым возможностям Visual Studio, это возможно.

Таким образом, каких-либо познаний в C# читателю данной статьи не потребуется. Главная идея будет заключаться в том, чтобы пользователь в режиме конструктора разместил графические элементы управления вроде кнопок или текстовых меток так, как ему необходимо, после чего уже на языке MQL снабдил каждый элемент соответствующей логикой. При этом все необходимые действия по интеграции панели с программой на MQL будут происходить «за кулисами», в автоматическом режиме. 

Схема взаимодействия с графическими интерфейсами .Net, общие принципы

.Net — это патентованное название общеязыковой платформы от компании Microsoft. Она была создана в 2002 году как альтернатива популярной в то время, впрочем как и сейчас, платформе Java. Основу платформы составляет так называемая общеязыковая среда исполнения  Common Language Runtime или CLR. В отличии от классической программы, которая компилируется непосредственно в машинный код, и может быть запущена на компьютере напрямую, программа написанная для .Net запускается на виртуальной машине CLR.  Таким образом .Net — это некая среда, с помощью которой программа, написанная на языке высокого уровня, выполняется на машине пользователя.

Язык программирования C# является основным языком программирования в Net. Когда говорят о C# подразумевают Net, и напротив — Net четко ассоциируется с C#. Упрощенно можно сказать, что Net — это среда исполнения программ, написанных как правило на C#. Наша статья не будет исключением. Весь код, предложенный в статье, будет написан на этом языке.

После того, как программа для платформы .Net написана, она компилируется в некий промежуточный байт код низкоуровневого языка CIL (Common Intermediate Language), который исполняет виртуальная машина CLR. Сам код упаковывается в стандартные сущности windows-программ: исполняемые модули exe или динамические библиотеки dll. Скомпилированный код для виртуальной машины Net имеет высокоуровневую структуру, его свойства легко исследовать, можно понять какие типы данных он содержит. Эту замечательную особенность используют последние версии компилятора MQL. Компилятор, на этапе компиляции, загружает динамическую библиотеку Net, и читает общедоступные статические методы, определенные в ней. Помимо открытых статических методов, компилятор MQL понимает базовые типы данных языка программирования C#. К этим типам данным относятся:

  • Все целочисленные типы данных: long/ulong, int/uint, byte, short/ushort;
  • Вещественные числа с плавающей запятой float/double;
  • Символьный тип данных char (в отличии от MQL, где char и uchar означают байтовый тип данных, в С# данный тип используется для определения символа);
  • Строковые типы string;
  • Простые структуры, содержащие в качестве своих полей базовые типы перечисленные выше.

Помимо перечисленных типов компилятор MQL видит массивы C#. Однако на текущий момент получить стандартный доступ к элементам массива по индексатору ‘[]’ в программе MQL пока невозможно. Можно сказать уверено, что в будущем поддержка типов будет расширяться, однако сегодняшних возможностей уже вполне достаточно для организации полноценного взаимодействия.

В нашем проекте мы будем создавать формы с помощью технологии Windows Forms. Это довольно простой набор API, который позволяет быстро, а главное просто нарисовать графический интерфейс даже неподготовленному пользователю. Его особенностью является событийно-ориентированный подход. Это значит, что когда пользователь нажимает на кнопку или вводит текст в окне ввода — в этот момент генерируется соответствующее событие. Обработав такое событие, программа написанная на C# определяет, что тот или иной графический элемент формы был изменен пользователем. Работа с событиями достаточно сложный процесс для пользователя, незнакомого с C#, поэтому необходимо написать специальный промежуточный код, который будет обрабатывать события происходящие в форме и передавать их MQL-программе, запущенной в терминале MetaTrader 5.

Таким образом, в нашем проекте будет существовать три независимых объекта, которые будут взаимодействовать друг с другом:

  • Программа в виде эксперта или индикатора написанная на MQL (файл EX5), которая будет получать события от графического окна или передавать их ему с помощью специального контроллера;
  • Контроллер в виде динамической библиотеки Net (DLL-файл), к которой будет обращаться MQL-программа;
  • Графическое окно, созданное пользователем с помощью C#, в виде независимой программы (EXE) или динамической библиотеки (DLL), события которого будет анализировать контроллер.

Все три объекта будут взаимодействовать между собой через системы сообщений. Между программой написанной на MQL и контроллером будет действовать одна система сообщений, а между контроллером и окном пользователя — другая.

Рис. 1. Общая схема взаимодействия между MQL-программой и графическим приложением C#

Схема представлена в самом общем виде, и пока не раскрывает конкретики взаимодействия между описанными частями нашего будущего графического приложения. Однако, исходя из предложенной схемы, становится ясно, что наша система будет высокораспределенной: каждый модуль будет независимым и не будет требовать вмешательства в свой код, если какой-либо другой модуль претерпел изменения. В следующих разделах будет подробно описан сам процесс взаимодействия между этими частями и те средства, с помощью которых это разделение будет реализовано.

Установка и настройка Visual Studio

Теперь, когда общая схема реализации подготовлена, настало время приступить к непосредственной реализации нашего проекта. Для этого на компьютере необходимо иметь рабочую версию Visual Studio. Если эта программа у Вас уже установлена, Вы можете пропустить данный раздел. Однако для новичков или тех, кто еще никогда не сталкивался с данной программой, этот раздел будет полезен. 

Visual Studio представляет из себя профессиональную среду разработки в самых разнообразных областях программирования. Данный комплекс представлен в нескольких редакциях. Нас будет интересовать редакция Community. Данная версия является условно-бесплатной. После тридцати дней использования, ее необходимо бесплатно зарегистрировать, для чего пройти стандартную процедуру верификации с помощью одного из сервиса Microsoft. Мы покажем основные шаги по скачиванию, установке и регистрации платформы, чтобы начинающие пользователи могли безболезненно начать использовать ее функционал в кратчайшие сроки. 

Итак, приведем пошаговое руководство для установки Visual Studio на компьютер. Далее будут представлены скриншоты для международной английской версии инсталлятора. Конкретный внешний вид в Вашем случае может отличаться, в зависимости от региональных настроек Вашего компьютера. 

Первое, что необходимо сделать — это перейти на официальный сайт Visual Studio visualstudio.microsoft.com и выбрать подходящий дистрибутив. Напоминаю, что нам необходимо выбрать версию Community:

 

Рис. 2. Выбор дистрибутива VisualStudio.

После выбора начнется процесс скачивания установщика Visual Studio. Если сайт предложит Вам зарегистрироваться, пропустите этот шаг. Мы выполним эту процедуру позже.

После запуска инсталлятора появится окно, которое предупредит о том, что потребуется выполнить определенную конфигурацию Вашего инсталлятора. Необходимо согласится с предложением и нажать кнопку Сontinue:

Рис. 3. Согласие на продолжение установки

Далее начнется процесс скачивания необходимых файлов для установки. Он может занять некоторое время, в зависимости от пропускной способности Вашего канала связи. После того, как процесс будет выполнен, появится окно конфигурации установки. Из предложенных компонентов, нам необходимо выбрать опцию «.Net desktop development»: 

Рис. 4. Выбор компонентов

После того, как опция выбрана, необходимо нажать на кнопку Install. Начнется процесс инсталляции, который также займет некоторое время:

Рис. 5. Процесс инсталяции.

После завершения инсталляции Visual Studio запустится автоматически. Если этого не произошло, запустите ее вручную. При первом запуске Visual Studio попросит войти в Ваш аккаунт или создать новый. Если у Вас нет аккаунта, лучше зарегистрировать его сейчас, для чего нажать ссылку «Create One»:

Рис. 6. Создание новой учетной записи

После перехода по ссылке «Create one», начнется процесс регистрации нового почтового ящика, который будет в дальнейшем привязан ко всем сервисам Microsoft. Вам необходимо пройти регистрацию, последовательно выполняя предложенные действия. Сам процесс регистрации стандартный, поэтому мы не будет останавливаться на нем подробно.

Если регистрация Вам по каким-то причинам не подходит, просто пропустите этот шаг, нажав на ссылку «Not now, maybe later». Однако помните, что через тридцать дней Visual Studio потребует регистрацию, иначе прекратит свою работу.

Создание первой формы. Быстрый старт.

После  регистрации и входа в учетную запись, запустится Visual Studio. Давайте попробуем создать нашу первую визуальную форму и подключить ее к MetaTrader. Прочитав этот раздел до конца, Вы убедитесь, как на самом деле просто это сделать.

Итак, необходимо создать новый проект. Для этого выберем в меню  File -> New -> Project. Откроется диалоговое окно выбора типа проекта:

Рис 7.

Нам необходимо выбрать тип «Windows Form App (.Net Framework)». В поле Name необходимо ввести имя проекта. Переименуем стандартное название, выдаваемое по умолчанию, назовем наш проект GuiMT. После чего нажмем кнопку «OK». После этого в Visual Studio автоматически отобразить визуальный конструктор с автоматически созданной формой:

 

Рис. 8. Создание графической формы в окне Visual Studio.

В окне Solution Explorer содержится структура нашего созданного проекта. Обратите внимание на название Form1.cs — это файл, содержащий программный код, создающий графическое представление формы, которое мы видим в окне графического конструктора Form1.cs[Disign]. Запомните это название, оно нам еще понадобится.

Визуальный конструктор с помощью мыши позволяет менять размер формы. Также на форме можно размещать пользовательские элементы. Сейчас этого будет вполне достаточно для наших первых экспериментов. Откроем вкладку Toolbox, на боковых табах слева главного окна и в разделе All Windows Form выберем элемент Button:

Рис. 9. Выбор кнопки

Перетащим его с помощью мышки на основную поверхность нашей формы Form1:

Рис. 10. Первая форма

Размер кнопки можно также менять. Вы можете поэкспериментировать с размерам основного окна и расположением этой кнопки. Теперь, когда на форме есть кнопка, будем считать что наше первое приложение готово. Давайте скомпилируем его. Сделать это можно по-разному, но сейчас мы просто запустим его в режиме отладки, для чего нажмем кнопку Start:

Рис 11. Кнопка запуска приложения в режиме отладки.  

После нажатия этой кнопки произойдет компиляция приложения и его автоматический запуск. После того как приложение запущено, его можно остановить, например просто нажать на крестик закрытия окна, либо остановить отладку в Visual Studio, нажав на кнопку Stop:

Рис. 11. Кнопка остановки отладки

Итак, наше первое приложение готово. Последнее что нам необходимо сделать, это выяснить абсолютный путь к запускаемой программе, которую мы только что создали. Самый простой способ — это просто посмотреть на путь в поле Project Folder окна Properties, при этом в окне Solution Explorer должен быть выделен проект GuiMT:

Рис. 12. Абсолютный путь к проекту приложения в графе Project Folder

Путь в этом окне относится к самому проекту. Конкретная сборка нашей программы будет располагаться в одном из подкаталогов, в зависимости от режима компиляции. В нашем случае это будет .bindebug<Название_нашего_проекта.exe>. Таким образом, полный путь к нашей программе будет: C:Users<Имя_пользователя>sourcereposGuiMTGuiMTbindebugGuiMT.exe. После того, как мы выяснили путь, его нужно будет где-то записать или запомнить, потому что позже его необходимо будет вставить в наш код на MQL.

Получение последней версии GuiController.dll. Работа с GitHub

В файлах, прикрепленных к данной статье, содержится библиотека GuiController.dll. Эту библиотеку необходимо разместить в каталоге MQL5Libraries. Однако часто бывает так, что библиотека продолжает обновляться и развиваться, в то время как к статье прикреплен уже давно устаревший архив. Чтобы избежать такой и многих других подобных ситуаций, лучше всего использовать системы контроля версий, когда новый код автоматически становится доступен для получателей. Наш проект не является исключением. Для того, чтобы гарантировано  получить самую последнюю версию GuiController, воспользуемся открытым депозитарием хранения открытых кодов GitHub.com. Исходный код этого контроллера уже содержится в данном репозитории, все что необходимо сделать — это скачать его проект и скомпилировать контроллер в динамическую библиотеку. Если по каким-то причинам Вы не можете воспользоваться этой системой или просто не хотите, то просто пропустите этот раздел. Вместо этого просто скопируйте файл GuiController.dll в каталог MQL5Libraries. 

Итак, если у Вас еще открыто текущее решение, закройте его, выполнив команду File -> Solution. Теперь перейдем на вкладку Team Explorer и нажмем ссылку Clone. В желтой графе  введите адрес, по которому  располагается проект:

https://github.com/PublicMqlProjects/MtGuiController

В следующей графе будет указан локальный путь, по которому будет сохранен скачанный проект. Он выбирается автоматически, согласно имени скачиваемого проекта, поэтому мы его изменять не будем. На скриншоте ниже показаны значения, которые должны быть введены в Team Explorer:

Рис. 13. Подключение к удаленному репозиторию исходного кода

Теперь, когда все готово, нажмем кнопку Clone. Через некоторое время у Вас по указанному адресу появится проект с последней версией MtGuiController. Откройте его через команду в меню File -> Open -> Project/Solution. После того как проект загружен и открыт, его необходимо скомпилировать, для чего можно нажать клавишу «F6» или выбрать в меню команду Build -> Build Solution. Найдите скомпилированный файл MtGuiController.dll в папке MtGuiControllerbindebug и скопируйте его в каталог библиотек MetaTrader 5:  MQL5Libraries.

Если по каким-то причинам Вам не удалось получить последнюю версию через github, не отчаивайтесь. В этом случае скопируйте контроллер из архива, прикрепленный к данной статье.

Интеграция первого приложения с MetaTrader 5

Теперь, когда у нас есть первое приложение и контроллер, который будет транслировать сигналы графического окна в MetaTrader, нам осталось выполнить заключительную часть: написать программу на MQL в виде эксперта, которая бы получала события от нашего окна через контроллер. Создадим новый эксперт в MetaEditor с именем GuiMtController со следующим содержимым:





#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
#import  "MtGuiController.dll"
string assembly = "С:\Users\Bazil\source\repos\GuiMT\GuiMT\bin\Debug\GuiMT.exe";



int OnInit()
  {

   EventSetMillisecondTimer(200);
   GuiController::ShowForm(assembly, "Form1");

   return(INIT_SUCCEEDED);
  }



void OnDeinit(const int reason)
  {

   GuiController::HideForm(assembly, "Form1");
   EventKillTimer();   
  }



void OnTick()
{

}



void OnTimer()
  {

   for(static int i = 0; i < GuiController::EventsTotal(); i++)
   {
      int id;
      string el_name;
      long lparam;
      double dparam;
      string sparam;
      GuiController::GetEvent(i, el_name, id, lparam, dparam, sparam);
      if(id == ClickOnElement)
         printf("Click on element " + el_name);
   }
  }

Напоминаю, что для того чтобы скомпилировать данный код, в каталоге MQL5Libraries должна быть размещена библиотека MtGuiController.dll. Также абсолютный путь, указанный в строке

string assembly = "С:\Users\Bazil\source\repos\GuiMT\GuiMT\bin\Debug\GuiMT.exe";

нужно скорректировать на путь к фактическому расположению Вашей программы с окном.
Если все сделано правильно, эксперт скомпилируется, а после его запуска на фоне главного окна MetaTrader появится наше окошко:

Рис. 14. Эксперт с интегрированным графическим приложением на C#.

Обратите внимание, что если нажать на кнопку button1, то эксперт выведет на вкладке Experts надпись «Click on element button1», сигнализируя о том, что он получил событие нажатие кнопки. 

Взаимодействия MQL-программы с GuiController, событийна модель

Чтобы понять, как работает сделанная нами программа, давайте разберем подробно предыдущий листинг MQL кода.

Итак, первое что мы видим — это деректива import и строка assembly:

#import  "MtGuiController.dll"
string assembly = "C:\Users\Bazil\source\repos\GuiMT\GuiMT\bin\Debug\GuiMT.exe";

Первая строка говорит компилятору о том, что будет использованы вызовы к открытым статическим методам класса, располагающимся в MtGuiController.dll. Обратите внимание, что указывать к каким именно методам мы будет обращаться в этой сборке не нужно. Эту работу выполнит за нас компилятор в автоматическом режиме.

Вторая строка содержит путь к форме, которой мы будем управлять. Этот адрес должен соответствовать фактическому расположению Вашей формы.

Далее содержится стандартный код процедуры инициализации советника OnInit:



int OnInit()
  {

   EventSetMillisecondTimer(200);
   GuiController::ShowForm(assembly, "Form1");

   return(INIT_SUCCEEDED);
  }

В ней происходит установка высокочастотного таймера и первый вызов одного из метода нашего класса. Функцию таймера мы опишем чуть позже, а сейчас обратимся собственно к вызову ShowForm:

GuiController::ShowForm(assembly, "Form1");

В C# функции не могут существовать отдельно от классов. Таким образом у каждой функции (метода) есть свой класс, в котором она определена. В MtGuiController.dll определен единственный класс GuiController. Он содержит статические методы, с помощью которых можно управлять тем или иным окном. Других классов в MtGuiController.dll нет, а значит все управление централизовано происходит через этот класс. Это очень удобно, т.к. пользователь работает с одним единственным интерфейсом взаимодействия и не ищет нужную ему функцию из набора разрозненных определений.

Первое что делается в блоке инициализации — это вызов метода ShowForm. Как нетрудно догадаться из его названия, он занимается тем, что запускает процесс отображения формы. Первый параметр метода задает абсолютный путь к файлу, в котором определена форма, а второй задает имя самой формы. Дело в том, что в одном файле может быть опредено сразу несколько форм. Поэтому и необходимо указать какую именно форму в файле мы хотим запустить. Названием формы в данном случае является название класса формы, которое Visual Studio назначила по-умолчанию форме созданной нами. Если открыть наш ранее созданный проект в Visual Studio и открыть файл Form1.Designer.cs в режиме просмотра кода, то мы увидим название класса, которое нам нужно:

partial class Form1
{
        
        
        
        private System.ComponentModel.IContainer components = null;
        ...
}

В дальнейшем, необходимо будет давать более осмысленные названия классов. В Visual Studio это достаточно легко сделать просто переименовав класс и все упоминания о нем. В этом случае значение второго параметра метода ShowForm придется также поменять.

Следующая функция к которой мы перейдем, это OnTimer. Согласно установки таймера она вызывается пять раз в секунду. Внутри нее содержится наиболее интересный код всего нашего проекта. Тело функции состоит из цикла for, который перебирает порядковые номера событий:

for(static int i = 0; i < GuiController::EventsTotal(); i++)
   {
      int id;
      string el_name;
      long lparam;
      double dparam;
      string sparam;
      GuiController::GetEvent(i, el_name, id, lparam, dparam, sparam);
      if(id == ClickOnElement)
         printf("Click on element " + el_name);
   }

Событие, с точки зрения контроллера, это любое действие пользователя над формой. Например, когда пользователь нажимает на кнопку или вводит текст в текстовое окно, контроллер получает соответствующее событие и помещает его в список событий. Количество событий в списке транслируется статическим методом GuiController::EventsTotal(), который может вызвать наша MQL-программа.

В Windows Forms существует огромное количество событий. Каждый элемент, такой как форма, кнопка, или текстовое окно, содержит несколько десятков событий. Не все события представляется возможным обработать, да и не все они нужны. Поэтому GuiController обрабатывает только самые важные события. В текущей версии обрабатываемых событий всего три. Это следующие события:

  • Событие нажатия кнопки;
  • Событие окончания вода текста;
  • Событие горизонтального скролла.

В дальнейшем этот список планируется расширить, но для целей данной статьи предложенного списка пока достаточно.

Итак, после того как произойдет событие, которое поддерживает наш GuiController, оно будет им обработано и добавлено в список событий. Обработка события заключается в создании данных, получив которые MQL-программа могла бы относительно легко определить тип события и его параметры. Именно поэтому формат данных каждого события имеет очень схожую структуру с событийной моделью функции OnChartEvent. Благодаря этому сходству, пользователю, работающему с GuiController не нужно учить формат новой событийном модели. Конечно, в представленном подходе есть и свои трудности, например, непростые события вроде того же скролла крайне сложно уместить в предложенный формат, но и эти проблемы легко решаемы с помощью языковых средств C# и его развитой объектно-ориентированной модели программирования. Сейчас же предложенной модели будет вполне достаточно для решения наших задач.

Каждый раз при поступлении нового события, его данные становятся доступными для получения через ссылочные типы с помощью статического метода GuiController::GetEvent. Этот метод имеет следующий прототип:

public static void GetEvent(int event_n, ref string el_name, ref int id, ref long lparam, ref double dparam, ref string sparam)

Опишем его параметры по порядку: 

  • event-n — порядковый номер события, который необходимо получить. Благодаря возможности указать порядковый номер события становится легко контролировать новые события, в каком бы количестве они не поступали;
  • el_name — имя элемента, который сгенерировал данное событие;
  • id — Тип события.
  • lparam — целочисленное значение, которое имеет данное событие;
  • dparam — вещественное значение, которое имеет данное событие;
  • sparam — строковое значение, которое имеет данное событие.

Как видите, событийная модель GuiController сильно напоминает событийную модель OnChartEvent. Любое событие в GuiController всегда имеет порядковый номер и источник (имя элемента), которое это событие сгенерировал. Остальные параметры являются опциональными. Так некоторые события, вроде нажатия кнопки, вообще не имеют дополнительных параметров (lparam, dparam, sparam), другие же их содержат. Например событие окончание текста в параметре sparam содержит текст, который был введен в поле пользователем.

Ниже приведена таблица, которая содержит события и их параметры, подерживаемые на текущий момент:

Название события Идентификатор (id)  Параметры
Exception  0 sparam — содержит сообщение, вызвавшего исключения
ClickOnElement  1
TextChange  2 sparam — новый текст, введенный пользователем 
ScrollChange  3

lparam — предыдущий уровень скролла

dparam — текущий уровень скролла

Теперь, когда мы разобрались с событийной моделью в GuiController, мы можем понять код, представленный внутри цикла for. Строка:

GuiController::GetEvent(i, el_name, id, lparam, dparam, sparam);

Получает событие по индексу i. Если тип события соответствует нажатию кнопки, то в консоль терминала выводится название кнопки и сообщение о ее нажатии:

if(id == ClickOnElement)
   printf("Click on element " + el_name);

Обратите внимание, что id сравнивается с константой ClickOnElement, которая нигде в MQL-коде программы не определена. Действительно, данная константа является частью перечисления определенного в самом GuiController на C#^



public enum GuiEventType
{
    Exception,
    ClickOnElement,
    TextChange,
    ScrollChange
}

Как видите, компилятор понимает внешние перечисления определенные в Net-библиотеках и умеет с ними работать. 

Обратим еще раз внимание на то, как происходит получение сообщений. Для этих целей используется таймер. Хотя в действительности может использоваться любая другая функция, вызывающаяся периодически, например OnTick. Однако периодичности очень трудно контролировать. Нельзя наверняка сказать сколько времени будет происходить между двумя последовательными вызовами OnTick.

Более того, нельзя определенно гарантировать периодичность вызова даже OnTimer. Например в тестере стратегий предельная частота вызова OnTimer сильно отличается от той, что можно выставить этой функции в режиме реальной работы. Все эти эффекты приводят к тому, что между двумя вызовами функций пользователем может быть сгенерировано несколько событий подряд. Например, он может нажать кнопку дважды или трижды, прежде чем MQL-программа успеет отреагировать на первое нажатие.

Именно для этих целей организована очередь событий. Каждое событие поступает в список, а затем ждет, когда его параметры будут извлечены MQL-программой. Сама же программа запоминает последний номер события с помощью определения статической переменной в функции, и при следующем запуске получает поступившие вновь события! Именно поэтому цикл for имеет нестандартную сигнатуру:

for(static int i = 0; i < GuiController::EventsTotal(); i++)

События можно как получать с помощью метода GuiController::GetEvent, так и отправлять их с помощью GuiController::SendEvent. Второй метод используется, кода нужно отправить какие-нибудь данные в окно и изменить его содержимое. Он имеет тот же прототип, что и GetEvent, единственная разница, что он не содержит порядковый номер события, так как в этом случае такой номер бессмысленен. Мы не будет останавливаться на нем подробно, однако покажем работу с ним на примере, который будет представлен в заключительной части статьи.

Последний метод, который мы не разобрали, это GuiController::HideForm. Его сигнатура аналогична ShowForm, а действие зеркально противоположное: данный метод скрывает окно, для чего необходимо указать его расположение и имя.

Как видите, код на MQL, который занимается отображением формы и анализом поступающих от ней событий, получился очень компактным и простым. По сути код описывает три простых действия:

  1. Отобразить окно при запуске программы;
  2. Получать новые события от окна;
  3. Скрыть окно при выходе из программы;

Более простую схему сложно было бы придумать. Обратите внимание также на сам код формы, которую мы создали. Хотя форма окна содержит этот самый код, сами мы не написали ни строчки на C#. Всю работу за нас сделали развитые средства автогенерации кода в Visual Studio и собственно GuiController. Именно так проявляется мощь технологий Net, ведь конечной цель мощных сред как раз и является простота.

Под капотом у GuiController

Если Вы недостаточно хорошо разбираетесь в C#, данный раздел Вы можете смело пропустить. Он будет интересен тем, кто хочет разобраться как устроен GuiController и как происходит доступ к отдельным, изолированным приложениям Net.

GuiController представляет из себя разделяемый класс, состоящий из двух частей: статической и части экземпляра. В статической части класса сосредоточены открытые статические методы для взаимодействия с MetaTrader. Эта часть класса реализует интерфейс между MetaTrader 5 и самим контроллером. Вторая часть экземплярная — это значит, что данные и методы этой части существуют только на уровне экземпляра. В их задачу входит взаимодействие с независимыми сборками Net, в которых располагаются графические окна. Само графическое окно в Windows Forms — это класс, унаследованный от базового класса Form. Таким образом, с каждым пользовательским окном можно работать на более высоком и абстрактном уровне класса Form.

Внутри сборок Net, таких как DLL или EXE, содержатся типы Net, которые являются открытыми по своей сути. Получить доступ к ним, их свойствам и даже методам довольно просто. Это можно сделать с помощью механизма, называемого в Net  рефлексией или отражением. Благодаря этому механизму, каждый файл вроде DLL или EXE, созданный в Net, можно исследовать на предмет наличия нужного нам элемента. Именно этим и занимается класс GuiController. Когда в его метод ShowForm передают абсолютный путь к сборке в Net, то контроллер загружает эту сборку с помощью специального механизма, после чего находит в ней графическое окно, которое необходимо отобразить. Приведем код метода GetGuiController, который выполняет эту работу:






private static GuiController GetGuiController(string assembly_path, string form_name)
{
    
    Assembly assembly = Assembly.LoadFile(assembly_path);
    
    Form form = FindForm(assembly, form_name);
    
    GuiController controller = new GuiController(assembly, form, m_global_events);
    
    return controller;
}

Данная процедура напоминает так называемый граббер ресурсов: специальную программу, с помощью которой можно извлечь медиа-контент вроде иконок и изображений из двоичного кода программы.

Поиск формы осуществляется с помощью рефлексии. Метод FindForm получает все типы, определенные в переданной ему сборке. Среди этих типов он ищет те, чей базовый тип соответствует типу Form. Если название такого найденного типа также соответствует нужному, то создается экземпляр этого типа, который и возвращается в виде формы:





private static Form FindForm(Assembly assembly, string form_name)
{
    Type[] types = assembly.GetTypes();
    foreach (Type type in types)
    {
        
        if (type.BaseType == typeof(Form) && type.Name == form_name)
        {
            object obj_form = type.Assembly.CreateInstance(type.FullName);
            return (Form)obj_form;
        }
    }
    throw new Exception("Form with name " + form_name + " in assembly " + assembly.FullName + "  not find");
}

Наиболее захватывающим моментом является создание самого приложения и его запуск. Ведь по сути из внешнего набора бинарных данных «оживает» настоящая программа, которая начинает работать как независимая программа.

После того как экземпляр создан, ему назначается контроллер. Контроллер — это экземплярная часть класса GuiController, которая следит за переданной ей формой. В обязанности контроллера входит отслеживать и передавать события для формы.

Запуск формы и ее удаление осуществляются в параллельном потоке. Благодаря этому текущий поток не блокируется в ожидании завершения текущей операции. Представим, что мы запустили окно в  текущем потоке. Так как окно продолжает работать, то вызвавший его внешний процесс зависает в ожидании закрытия окна. Запуск окна в отдельном потоке решает эту проблему.

За запуск и удаление окна отвечают соответствующие методы контроллера:




public static void ShowForm(string assembly_path, string form_name)
{
    try
    {
        GuiController controller = GetGuiController(assembly_path, form_name);
        string full_path = assembly_path + "/" + form_name;
        m_controllers.Add(full_path, controller);
        controller.RunForm();
    }
    catch(Exception e)
    {
        SendExceptionEvent(e);
    }
}
        



public static void HideForm(string assembly_path, string form_name)
{
    try
    {
        string full_path = assembly_path + "/" + form_name;
        if (!m_controllers.ContainsKey(full_path))
            return;
        GuiController controller = m_controllers[full_path];
        controller.DisposeForm();
    }
    catch(Exception ex)
    {
        SendExceptionEvent(ex);
    }
}

Последнее, что нам необходимо рассмотреть из работы контроллера — это работа с событиями. Когда с помощью рефлексии создается новая форма, то она передается методу, который подписывается на ее события — вернее, только на те из них что может обработать контроллер. Для этих целей создается сопоставление <элемент — список обработчиков событий>, где каждый обработчик событий подписан на нужное ему событие: 




private void SubscribeOnElements(Form form)
{
    Dictionary<Type, List<HandlerControl>> types_and_events = new Dictionary<Type, List<HandlerControl>>();
    types_and_events.Add(typeof(VScrollBar), new List<HandlerControl>() { vscrol => ((VScrollBar)vscrol).Scroll += OnScroll });
    types_and_events.Add(typeof(Button), new List<HandlerControl>()  { button => ((Button)button).Click += OnClick });
    types_and_events.Add(typeof(Label), new List<HandlerControl>());
    types_and_events.Add(typeof(TextBox), new List<HandlerControl>() { text_box => text_box.LostFocus += OnLostFocus, text_box => text_box.KeyDown += OnKeyDown });
    foreach (Control control in form.Controls)
    {
        if (types_and_events.ContainsKey(control.GetType()))
        {
            types_and_events[control.GetType()].ForEach(el => el.Invoke(control));
            m_controls.Add(control.Name, control);
        }
    }
}

Каждая форма имеет открытый список элементов, которая она содержит. Перебирая список элементов, метод находит те из них, что контроллер способен поддержать, и подписывается на нужные ему события. Если элемент на форме не поддерживается контроллером, то он просто будет проигнорирован. События, связанные с ним, не будут доставлены в MQL-программу, а сама MQL-программа не сможет взаимодействовать с этим элементом.

Торговая панель на основе графических интерфейсов

Теперь, когда мы рассмотрели все части нашей системы, настала пора создать что-то действительно полезное. Мы сделаем аналог стандартной торговой панели, которая содержится в верхнем левом углу чарта:

Рис. 15. Встроенная торговая панель MetaTrader 5.

Конечно, наша панель будет состоять из стандартных графических элементов окна Windows, поэтому будет иметь более простой дизайн, однако функционал, заложенный в ней будет идентичный.

Можно поступить по-разному, например, начать создавать такую панель с чистого нуля. Однако описывать работу визуального конструктора будет излишним в этой статье. Поэтому мы поступим проще и загрузим проект, содержащий эту панель в Visual Studio. Сделать это можно двумя способами: либо скопировать проект из архива и открыть его в Visual Studio, либо скачать его из удаленного репозитория Git по следующему адресу: 

https://github.com/PublicMqlProjects/TradePanelForm

Работа с git в этом случае будет такой же, как описывалась в соответствующем разделе, поэтому не будем повторяться.

После того, как проект будет загружен и открыт, Вы должны будете увидеть следующую форму:

Рис. 16. Окно TradePanel в визуальном конструкторе Visual Studio

Проект содержит макет торговой панели. В реальных проектах вроде этого нам потребуется постоянно получать доступ к элементам, размещенным на этой форме, а также отправлять им события. Для этих целей будет необходимо обращаться к каждому элементу по его имени. Поэтому имена элементов должны быть осмысленными и запоминающимися. Давайте посмотрим как называются элементы, которые мы будем использовать. Чтобы посмотреть имя каждого элемента нужно найти свойство Name в окне Properties, предварительно выделив этот элемент. Так например кнопка с надписью Buy будет обладать именем ButtonBuy:

Рис. 17. Имя элемента в окне Properties.

Следует различать текст, который изображен на элементе и имя самого элемента. Это разные значения, хотя они зачастую имеют схожий смысл.

Приведем список элементов, которые содержит наша торговая панель:

  • Основное графическое окно (Form) c именем TradePanelForm, на котором располагаются все остальные элементы управления.
  • Красная текстовая метка (Label), с именем AskLabel. Данная метка будет отображать цену Ask текущего инструмента;
  • Cиняя текстовая метка (Label), с именем BidLabel. Данная метка будет отображать цену Bid текущего инструмента;
  • Поле ввода текста (TextBox) с именем CurrentVolume. В данное поле пользователь будет вводить нужный объем сделки;
  • Вертикальный Скролл (VScrollBar) с именем IncrementVol. Данный скролл будет увеличивать или уменьшать объем на один шаг. Величина шага будет определятся MQL-программой, на основе текущего торгового окружения.
  • Кнопка покупки с именем ButtonBuy. Нажав на эту кнопку пользователь купит заданный им объем по цене Ask — той, что отображается на красной текстовой метке.
  • Кнопка продажи с именем ButtonSell. Нажав на эту кнопку пользователь продаст заданный им объем по цене Bid, которая будет отображаться на синей текстовой метке.

Хотя элементов, которые мы используем, совсем немного, комбинируя их, мы получаем довольно продвинутый интерфейс. Заметим, что как и в предыдущем примере, наше решение не содержит ни одной строки кода на C# которую пришлось бы добавлять. Все нужные свойства элементов выставлены в окне Properties, а расположение и размер элементов установлены с помощью drag-n-drop техники, т.е. с помощью мышки!

Интеграция графического окна с кодом эксперта

Теперь, когда наше окно готово, его необходимо интегрировать с торговым экспертом. Мы напишем торговую логику на языке программирования MQL, которая будет взаимодействовать с элементами нашего интерфейса. Полный код эксперта представлен ниже:





#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
#import  "MtGuiController.dll"
#include <TradeTrade.mqh>
string assembly = "c:\Users\Bazil\source\repos\TradePanel\TradePanel\bin\Debug\TradePanel.dll";
string FormName = "TradePanelForm";
double current_volume = 0.0;


CTrade Trade;  



int OnInit()
{

   EventSetMillisecondTimer(200);
   GuiController::ShowForm(assembly, FormName);
   current_volume = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN);
   GuiController::SendEvent("CurrentVolume", TextChange, 0, 0.0, DoubleToString(current_volume, 2));

   return(INIT_SUCCEEDED);
}



void OnDeinit(const int reason)
  {

   EventKillTimer();
   GuiController::HideForm(assembly, FormName);

  }



void OnTick()
{

   double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
   double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
   GuiController::SendEvent("AskLabel", TextChange, 0, 0.0, DoubleToString(ask, Digits()));
   GuiController::SendEvent("BidLabel", TextChange, 0, 0.0, DoubleToString(bid, Digits()));

}




void OnTimer()
{

   for(static int i = 0; i < GuiController::EventsTotal(); i++)
   {
      int id;
      string el_name;
      long lparam;
      double dparam;
      string sparam;
      GuiController::GetEvent(i, el_name, id, lparam, dparam, sparam);
      if(id == TextChange && el_name == "CurrentVolume")
         TrySetNewVolume(sparam);
      else if(id == ScrollChange && el_name == "IncrementVol")
         OnIncrementVolume(lparam, dparam, sparam);
      else if(id == ClickOnElement)
         TryTradeOnClick(el_name);
   }

}



double ValidateVolume(double n_vol)
{
   double min_vol = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN);
   double max_vol = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MAX);
   
   if(n_vol < min_vol)
      return min_vol;
   
   if(n_vol > max_vol)
      return max_vol;
   
   double vol_step = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_STEP);
   double steps = MathRound(n_vol / vol_step);
   double corr_vol = NormalizeDouble(vol_step * steps, 2);
   return corr_vol;
}



bool TrySetNewVolume(string nstr_vol)
{
   double n_vol = StringToDouble(nstr_vol);
   current_volume = ValidateVolume(n_vol);
   string corr_vol = DoubleToString(current_volume, 2);
   GuiController::SendEvent("CurrentVolume", TextChange, 0, 0.0, corr_vol);
   return true;
}



bool TryTradeOnClick(string el_name)
{
   if(el_name == "ButtonBuy")
      return Trade.Buy(current_volume);
   if(el_name == "ButtonSell")
      return Trade.Sell(current_volume);
   return false;
}



void OnIncrementVolume(long lparam, double dparam, string sparam)
{
   double vol_step = 0.0;
   
   if(dparam > lparam)
      vol_step = (-1.0) * SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_STEP);
   
   else if(dparam < lparam)
      vol_step = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_STEP);
   
   else if(lparam == 0)
      vol_step = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_STEP);
   
   else
      vol_step = (-1.0) * SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_STEP);
   double n_vol = current_volume + vol_step;
   current_volume = ValidateVolume(n_vol);
   string nstr_vol = DoubleToString(current_volume, 2);
   GuiController::SendEvent("CurrentVolume", TextChange, 0, 0.0, nstr_vol);
}

Представленный код выполняет основную рабочую нагрузку нашей формы. Однако примечательно, что весь этот функционал написан именно на MQL5, внутри стандартных функций-обработчиков событий. Давайте более подробно проанализируем представленный код.

Первое, что делается в функции OnInit — это устанавливается таймер с разрешением 200 миллисекунд. Затем отображается окно с помощью метода ShowForm:

GuiController::ShowForm(assembly, FormName);

где assembly — путь к сборке, в которой находится наше окно, а FormName — имя класса нашей формы.

Сразу после того, как окно запущено, мы устанавливаем минимальный объем в текстовое поле «CurrentVolume»:

GuiController::SendEvent("CurrentVolume", TextChange, 0, 0.0, DoubleToString(current_volume, 2));

Сам минимальный объем рассчитывается исходя из текущего торгового окружения, с помощью функции SymbolInfoDouble.

При закрытии советника, окно формы закрывается. Делается это в функции OnDeinit, с помощью метода GuiController::HideForm; 

Функция OnTick реагирует на изменение текущей цены Ask/Bid. Таким образом, если получать текущие цены в этой функции и передавать их в соответствующие текстовые метки формы, панель будет оперативно отображать все изменения текущей цены.

double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);

double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);

GuiController::SendEvent("AskLabel", TextChange, 0, 0.0, DoubleToString(ask, Digits()));

GuiController::SendEvent("BidLabel", TextChange, 0, 0.0, DoubleToString(bid, Digits()));

В функции OnTimer происходит отслеживания трех действий, который пользователь может выполнить с формой, а именно:

  • Ввести новый объем в текстовую метку CurrentVolume;
  • Нажать на кнопку увеличения или уменьшения шага объема выполненную в виде скролла;
  • Нажать на кнопку Buy или Sell, тем самым отправляя приказ на совершения сделки.

В зависимости от того, какое действие было выполнено пользователем, будет происходить определенный набор инструкций. Событие нажатие кнопок скролла, которые должны увеличивать или уменьшать текущий объем на минимально-допустимый шаг, мы еще не анализировали, поэтому давайте остановимся на нем более подробно.

Событие скролла в текущей событийной модели состоит из двух параметров lparam и dparam. Первый параметр содержит условную величину, которая характеризует сдвиг каретки относительно нулевого уровня до нажатия кнопок скролла пользователем. Второй параметр содержит эту же величину, но уже после нажатия. Сам скролл имеет определенный диапазон работы, например от 0 до 100. Так если значение lparam равно 30, а значение dparam равно 50, то это значит, что вертикальный скролл был передвинут вниз с 30 до 50% (горизонтальный скролл соответственно передвинется не вниз, а вправо на аналогичную величину). В нашей панели определять где именно находится скролл не требуется. Нам необходимо лишь знать по какой кнопке нажал пользователь. Для этого нужно проанализировать предыдущее и текущее значение. Именно этим и занимается функция OnIncrementVolume. Определив тип нажатия скролла, она увеличивает или уменьшает текущий объем на минимальный шаг объема, который узнает с помощью системной функции SystemInfoDouble.

Устанавливать новый торговый объем можно не только с помощью стрелок скролла. Также его можно вводить в текстовую метку напрямую. Когда пользователь вводит новый символ, windows forms генерирует соответствующее событие. Однако нам важно проанализировать конечную строку, а не каждый символ по отдельности. Поэтому GuiController реагирует на нажатие клавиши ‘Enter’ или на смену фокуса текстовой метки. Именно эти события считаются окончанием ввода текста. Когда происходит одно из них, сформированный текст передается в очередь событий, которую последовательно читает наш эксперт. Добравшись до события изменения текста в метке, MQL-программа разбирает его новое значение и устанавливает новый объем согласно заданному. Анализ осуществляется с помощью функции ValidateVolume. Она контролирует следующие параметры введенного объема:

  • Объем должен быть между минимально и максимально допустимым;
  • Величина объема должна быть кратна его шагу. Так если шаг 0.01 лота, а пользователь введет цифру 1.0234, то она будет скорректирована до 1.02;

Заметим, что данные параметры возможно проконтролировать только с помощью текущего торгового окружения. Таким образом, весь контроль вводимых пользователем значений осуществляется самой MQL-программой, а не созданной пользователем формой. 

Давайте запустим торговую панель на графике и попробуем совершить несколько сделок с ее помощью:

Рис. 18. Работа панели в режиме реального времени. 

Как видите, торговая панель работает и отлично выполняет возложенные на нее функции.

Работа графических интерфейсов в тестере стратегий

Тестер стратегий в MetaTrader 5 имеет ряд особенностей, которые должен учитывать разработчик графических интерфейсов на языке программирования MQL. Главной особенностью является тот факт, что функция обработки графических событий OnChartEvent не вызывается вовсе. Эта особенность логична, т.к. графическая форма подразумевает работу с пользователем в режиме реального времени. Однако есть тип панелей, который было бы крайне интересно реализовать именно в тестере. Это так называемые торговые плееры, с помощью которых люди могли бы тестировать свои торговые стратегии в ручном режиме. Например, тестер стратегий в ускоренном режиме генерировал текущие рыночные цены, а пользователь нажимал бы на кнопки купить или продать и тем самым симулировал свои торговые действия на истории. Именно к такому типу панелей можно отнести созданную нами TradePanel. Не смотря на свою простоту, она вполне может быть простым плеером торговли с самым необходимым функционалом. 

Но давайте подумаем, как наша панель будет работать в тестере стратегий MetaTrader 5. Графическое окно панели TradePanel существует в виде независимой сборки Net. Следовательно оно никак не зависит от текущего окружения MetaTrader 5 и даже самого терминала. Строго говоря, его можно запустить из любой другой программы, а сборки размещенные в exe-контейнере может запустить даже сам пользователь.

Таким образом, нашей программе не требуется вызов OnChartEvent. Более того, обновлять данные в окне и получать новые приказы от пользователей можно в любой функции-обработчике событий, которая регулярно запускается в тестере стратегий. К таким функциям относятся OnTick и OnTimer. Именно через них и работает наша панель. Следовательно, разработанная для работы в режиме реального времени, наша панель будет также хорошо работать и в тестере стратегий. Делать какие-либо изменения для этого не потребуется. Давайте проверим данное утверждение, запустив нашу панель в тестере и совершим несколько сделок в ней:

Рис. 19. Работа панели в режиме симуляции, в тестере стратегий.

Получается что разработка графических интерфейсов с помощью C# дает нам неожиданный бонус при работе в тестере стратегий. Для приложения Windows Forms тестер стратегий не накладывает никаких ограничений. Особенности работы событийной модели в нем не затрагивают ни панели ни способы работы с ними. Переделывать программу под работу в тестере стратегий также не нужно. 

Заключение

В статье был предложен подход с помощью которого быстро, а главное — легко, можно создать свою собственную визуальную форму. Данный подход разделяет графическое приложение на три независимые части: MQL-программу, адаптер GuiController и собственно визуальную панель. Все части приложения не зависят друг от друга. MQL-программа работает в торговом окружении MetaTrader и выполняет торговые или аналитические функции исходя из тех параметров, что она получает от панели через GuiController. Сам GuiController является назависимой программой, которую не требуется изменять при изменении формы или ее элементов. Наконец, графическая панель создается самим пользователем, с помощью развитых визуальных средств Visual Studio. Благодаря этому, для создания довольно сложной формы может даже не потребоваться знание языка программирования C#.

Сами созданные формы никак не зависят от той программы, которая их запускает. Это может быть сам MetaTrader 5 или его тестер стратегий — окно и в том и в другом случае будет работать согласно заложенной в него логике. Помимо этого, окно не зависит и от того, в какой именно функции оно вызвано. Благодаря этому графические интерфейсы одинаково хорошо работают как в самом MetaTrader 5 так и в его тестере стратегий, при этом без разницы работает ли с окном эксперт или индикатор. Во всех случаях поведение окна будет одинаковым.

Благодаря вышеперечисленным особенностям, предложенный подход обязательно найдет своих поклонников. В первую очередь его полюбят те, кому нужно сделать полуавтоматическую форму: торговый привод или плеер, информационную панель или любую другую визуальную форму, в виде стандартного графического интерфейса. Подход придется по душе и тем, кто не достаточно хорошо разбирается в программировании. Чтобы создать свою форму достаточно лишь в общих чертах знать MQL5. 

У предложенного подхода, как и у любой технологии, есть свои недостатки. Основной из них это невозможность работы в Маркете, т.к. вызов сторонних DLL запрещен. Во-вторых, запуск незнакомых DLL или EXE может быть небезопасным, т.к. эти модули могут содержать вредоносные функции. Однако данная проблема решается открытостью проекта. Пользователь знает, что созданная им программа априори не содержит иных элементов, кроме заданных им самим, а GuiController — это публичный проект с открытым исходным кодом. Еще одним недостатком является то, что межпрограммное взаимодействие достаточно сложный процесс. Такое взаимодействие может вызвать зависание или непредвиденное завершение работы программы. Многое зависит от разработчика, который будет разрабатывать интерфейс и взаимодействие с ним. Такую систему легче вывезти из строя, чем монолитную, написанную на чистом MQL5.

Сейчас проект находится в стадии становления. Многие пользователи не увидели в этой статье нужных им элементов управления, а текущие возможности по взаимодействию с графическими окнами, прямо скажем, еще очень ограничены. Все это верно. Однако главная задача статьи была выполнена. Мы показали, что создавать windows forms и взаимодействовать с ними проще, чем кажется. Если данный материал окажется полезным MQL-сообществу, то мы обязательно продолжим развивать это направление.

Понравилась статья? Поделить с друзьями:
  • Проект по теме история развития операционной системы windows
  • Проект одноэтажного дома windows 90м2 fh 90
  • Проект дома windows 90 квадратов скачать
  • Проект астория для windows phone 10 скачать
  • Продукт не относится к каналу kms client пропускается windows 10