Предположим, что Вы установили инструмент разработки, собственно конструктор программ HiAsm.NET и готовы создать новый элемент для пакета Core. Следуя традиции, сначала элемент будет очень простым, но постепенно будем добавлять в него функционал до максимально возможного уровня, достигнутого на сегодня.
Создание проекта нового элемента
Добавление ссылок на другие сборки
Чтобы «разговаривать» с конструктором на одном языке, элементу надо указать где находятся две сборки конструктора, в которых хранится код классов общения со средой. Воспользуемся панелью обозревателя решения (Solution Explorer) и укажем на файлы конструктора MSDK.dll и ProxyDomain.dll в папке C:\HiAsm.NET\:
Теперь Visual Studio будет нам подсказывать на какие буковки на клавиатуре давить пальцем . Эта функциональность называется IntelliSense. Для сборок конструктора устанавливаем свойство CopyLocal значением False, чтобы в момент компиляции Visual Studio не копировала эти файлы никуда.
В заключении наведём порядок среди ссылок на сборки при помощи контекстного меню, вызываемого правой кнопкой мыши на самих ссылках, чтобы удалить ненужные нам сейчас и напротив добавить одну нужную системную сборку System.Drawing.dll:
Вот так окончательно должен выглядеть почищенный список:
Для эстетической завершенности переименуем класс из бездушного названия в понятное только нам :
Bingo
Установка атрибутов
Теперь надо установить обязательные атрибуты, чтобы проект HiAsm.NET признал элемент и прочитал из него необходимую информацию для взаимодействия с ним. Прежде всего это жестко определённое пространство имён (namespace):
using HiAsm;
...
namespace ElementVirtual
{
...
}
Информационный атрибут с почтой автора и версией элемента:
...
namespace ElementVirtual
{
[About(version = "1.0", author = "Developer Name", mail = "developer@mail.net")]
...
}
...
namespace ElementVirtual
{
...
[Type("Example", "Brief description", tab = "Core")]
...
}
Теперь, после выбора имени класса элемента, зададим атрибут с информацией об иконке элемента:
...
namespace ElementVirtual
{
...
[ToolboxBitmap(typeof(Example), "Resources.picture.ico")]
...
}
Определим элементу свойтво с именем Greeting:
...
namespace ElementVirtual
{
...
[Property("Greeting", "The greeting message", DataType.data_str)]
...
}
Определим элементу левую точку с именем doSayHello:
...
namespace ElementVirtual
{
...
[Point("doSayHello", "Do greeting message", DataType.data_str, PointType.pt_work)]
...
}
- pt_work - левая точка;
pt_event - правая точка;
pt_var - нижняя точка;
pt_data - верхняя точка;
Аналогично добавим элементу по одной точке на каждую сторону:
...
namespace ElementVirtual
{
...
[Point("onGreeting", "Returns greeting message", DataType.data_str, PointType.pt_event)]
[Point("Greeting", "Returns greeting message", DataType.data_str, PointType.pt_var)]
[Point("Data", "Defines greeting message", DataType.data_str, PointType.pt_data)]
...
}
Посмотрим как это выглядит суммарно:
...
namespace ElementVirtual
{
[About(version = "1.0", author = "Developer Name", mail = "developer@mail.net")]
[Type("Example", "Brief description", tab = "Core")]
[ToolboxBitmap(typeof(Example), "Resources.picture.ico")]
[Property("Greeting", "The greeting message", DataType.data_str)]
[Point("doSayHello", "Do greeting message", DataType.data_str, PointType.pt_work)]
[Point("onGreeting", "Returns greeting message", DataType.data_str, PointType.pt_event)]
[Point("Greeting", "Returns greeting message", DataType.data_str, PointType.pt_var)]
[Point("Data", "Defines greeting message", DataType.data_str, PointType.pt_data)]
...
}
Создание кода основного класса элемента
Простые элементы являются наследниками базового класса Element:
...
namespace ElementVirtual
{
[About(version = "1.0", author = "Developer Name", mail = "developer@mail.net")]
...
public class Example : Element
{
...
}
...
}
Добавим конструктор, который будет вызван средой во время создания экземпляра класса элемента:
...
namespace ElementVirtual
{
[About(version = "1.0", author = "Developer Name", mail = "developer@mail.net")]
...
public class Example : Element
{
public Example(PackElement pe, SDK sdk, int x, int y) : base(pe, sdk, x, y)
{
flag |= (int)(ElementFlag.IS_SYSTEM | ElementFlag.IS_VIRTUAL);
}
...
}
...
}
Посмотрим что у нас получилось после всех предыдущих изменений кода:
Если мы теперь построим сборку элемента у Visual Studio не будет к нашему коду никаких претензий:
По умолчанию Visual Studio сохранила файл сборки в папке проекта. Куда именно увидим, посмотрев на свойства проекта:
Чтобы сократить телодвижения по копированию сборки руками в нужное нам место, надо изменить выходной путь. Так как сейчас мы делаем элемент для пакета Core, укажем путь к папке со сборками виртуальных элементов этого пакета:
Ещё установим некоторые важные свойства проекта:
Можем заполнить форму с подробной информацией о сборке и если будете использовать данный проект как шаблон для создания других элементов, то следите чтобы уникальный идентификатор сборки (GUID) оставался уникальным:
Теперь, если опять выполним построение (Build) проекта элемента, то обнаружим в указанной ранее папке файл сборки нашего нового элемента:
Размещение элемента на панели инструментов
В принципе, поместив файл сборки в папку \virt пакета Core, можем использовать элемент в конструкторе, но только пока для отображения схем с его использованием. Элемент надо поместить на панель инструментов, чтобы можно было его вставлять в схемы. Сделать это можно несколькими способами. Не буду описывать их все, а предложу самый простой и быстрый способ. На мой взгляд, проще всего открыть файл сборки в конструкторе через меню Open или набросить его на окно программы. Но прежде чем это делать, надо добавить к элементу ещё один атрибут с информацией о пакетах в которые надо его установить (пакетов может быть несколько):
...
namespace ElementVirtual
{
...
[Install("_base", "Simple demo", "Core")]
...
}
Demo
Добавление иконки элемента в ресурсы сборки
Чтобы добавить иконку элемента в ресурсы сборки надо сначала её создать. Надеюсь на просторах интернета Вы сможете отыскать изображение достойное разрабатываемого элемента. Требования к файлу иконки следующие: 24 пикселя или точки в ширину и высоту, 32 бита глубины цвета и формат иконки Windows. С именем файла иконки мы определились ранее при добавлении атрибута ToolboxBitmap, а именно picture.ico. Теперь надо решить как добавить файл иконки в ресурсы. Есть по крайней мере два способа известных мне (вероятно их больше). Один способ очевидный, а другой нет, но зато быстрый. Давайте посмотрим сначала на очевидный способ добавления через свойства проекта:
Demo
Если теперь перекомпилим проект элемента и запустим конструктор, то увидим новую иконку на панели инструментов и можем добавить её на рабочее поле схемы:
Мы сделали заготовку или шаблон элемента, который отображается на панели инструментов и может быть добавлен в схему пакета Core. Теперь надо научить элемент реагировать на обращения к его точкам других элементов схемы (сейчас он не реагирует никак, так как в нём нет кода, реализующего данный функционал).
Создание кода методов обработки обращений к точкам элемента
При помощи связей (линий) к элементу можно обращаться из других элементов. Причём сам элемент обращается к другим элементам через верхние и правые точки, а к нему обращения приходят на левые и нижние точки. Значит в коде элемента мы должны реализовать два метода-обработчика обращений к левым и нижним точкам и научиться получать данные с левых и верхних точек, а также отправлять результаты своей работы на правые и нижние точки.
Метод-обработчик обращений к левым точкам элемента выглядит так:
public override void do_work(ElementPoint point, ref TData data)
{
base.do_work(point, ref data);
...
}
Метод-обработчик обращений к нижним точкам элемента выглядит так:
public override void read_var(ElementPoint point, ref TData data)
{
base.read_var(point, ref data);
...
}
object x = data.value; // читаем нетипизированные данные с левой точки
// читаем данные как объект типа TData
TData dt = readData(data, getPointByName("Data"), getPropertyByName("Greeting"));
// или читаем данные как строку
string text = readString(data, getPointByName("Data"), getPropertyByName("Greeting"));
// или читаем данные как целое число
int number = readInteger(data, getPointByName("Data"), getPropertyByName("Greeting"));
// или читаем данные как действительное число
double real = readReal(data, getPointByName("Data"), getPropertyByName("Greeting"));
Отправить данные другому элементу через правую точку может следующий код:
// отправляем строку другому элементу через точку к именем onGreeting
on_event(getPointByName("onGreeting"), "Hello!");
// отправляем целое число другому элементу через точку к именем onGreeting
on_event(getPointByName("onGreeting"), 123);
// отправляем действительное число другому элементу через точку к именем onGreeting
on_event(getPointByName("onGreeting"), Math.PI);
// создаём новый объект типа TData и указываем наши данные для отправки как аргумент конструктора
TData dt = new TData("Hello!");
// отправляем данные другому элементу через точку к именем onGreeting
on_event(getPointByName("onGreeting"), ref dt);
Для возвращения данных с нижних точек надо записать наши данные в объект типа TData, ссылку на который нам прислал, обращающийся к нашей точке элемент. Следующий код показывает как можно это сделать:
// записываем наши данные типа TData по ссылке другого элемента
data.assign(new TData("Hello!"));
// или записываем наши данные любого типа по ссылке другого элемента
data.assignT(this);
Теперь мы готовы научить наш элемент реагировать на внешние обращения к нему из других элементов:
private TData local;
public override void do_work(ElementPoint point, ref TData data)
{
base.do_work(point, ref data);
this.local = readData(data, getPointByName("Data"), null);
TData dt = new TData(this.local);
this.on_event(getPointByName("onGreeting"), ref dt);
}
public override void read_var(ElementPoint point, ref TData data)
{
base.read_var(point, ref data);
if (this.local.isEmpty())
data.assign(new TData(Properties.Resources.greeting));
else
data.assign(this.local);
}
Посмотрим на получившийся код элемента:
Можете скачать полный код проекта элемента: MyElementCore.zip
Мы реализовали необходимый функционал элемента и можем проверить как он работает:
Demo
P.S. Продолжение следует. Эта тема будет редактироваться и дополняться новой информацией для разработчиков, которая призвана помочь им преодолеть порог вхождения. Автор будет удалять комментарии не относящиеся к данной теме, чтобы не засорять её бесполезной информацией, для которой на форуме есть специальные разделы. Спасибо за понимание.