Подключение матричной формы в ролеориентированный клиент

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

Пожалуй приступим.

Создадим новый проект типа Class Library (File > New > Project). Назовем его NAV4U.ItemMatrix.

Далее добавим ссылки на сборки, которые отвечают за базовую графическую функциональность и создание Windows интерфейса:

  • System.Drawing
  • System.Windows.Forms

Кроме этого нужно добавить ссылку на библиотеку Microsoft.Dynamics.Framework.UI.Extensibility.dll из папки «C:\Program Files\Microsoft Dynamics NAV\60\RoleTailored Client».

Добавим необходимые using директивы:

  1. using Microsoft.Dynamics.Framework.UI.Extensibility;
  2. using Microsoft.Dynamics.Framework.UI.Extensibility.WinForms;
  3. using System.Windows.Forms;
  4. using System.Drawing;

Добавим ссылки на веб-службы:

ItemCardWS http://localhost:7047/DynamicsNAV/WS/CRONUS%20International%20Ltd/Page/ItemCard

LocationListWS http://localhost:7047/DynamicsNAV/WS/CRONUS%20International%20Ltd/Page/LocationList

 Теперь перейдем к написанию кода подключаемого компонента. Как мы помним из первой статьи цикла про подключаемые компоненты нам нужно создать класс, перед определением которого должен быть установлен атрибут ControlAddInExport. В классе будет определено три свойства и ряд методов.

 
[ControlAddInExport("NAV4U.ItemMatrix")] 
public class StyleControl : StringControlAddInBase 
{ 
  private DataGridView pDataGridView; 
  private BindingSource bindingSource1; 
  private ItemCardWS.ItemCard_Service ItemCardService;   
 
protected override Control CreateControl() 
  private void InitializeDataGridView() 
  private void CreateItemLines() 
  private void CreateLocationColumns() 
  private void dataGridView1_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e) 
}

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

 
protected override Control CreateControl() 
{ 
  pDataGridView = new DataGridView(); 
  InitializeDataGridView(); 
  CreateLocationColumns(); 
  CreateItemLines(); 
  return pDataGridView; 
}

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

 
private void InitializeDataGridView() 
{ 
  DataGridViewTextBoxColumn noDataGridViewTextBoxColumn 
  = new DataGridViewTextBoxColumn(); 
  DataGridViewTextBoxColumn descriptionDataGridViewTextBoxColumn 
  = new DataGridViewTextBoxColumn(); 
  DataGridViewTextBoxColumn baseUnitofMeasureDataGridViewTextBoxColumn 
  = new DataGridViewTextBoxColumn(); 
  DataGridViewTextBoxColumn inventoryDataGridViewTextBoxColumn 
  = new DataGridViewTextBoxColumn();   
 
noDataGridViewTextBoxColumn.DataPropertyName = "No"; 
  noDataGridViewTextBoxColumn.Frozen = true; 
  noDataGridViewTextBoxColumn.HeaderText = "No"; 
  noDataGridViewTextBoxColumn.Name = "noDataGridViewTextBoxColumn"; 
  noDataGridViewTextBoxColumn.ReadOnly = true; 
  noDataGridViewTextBoxColumn.Width = 80;   
 
descriptionDataGridViewTextBoxColumn.DataPropertyName = "Description"; 
  descriptionDataGridViewTextBoxColumn.Frozen = true; 
  descriptionDataGridViewTextBoxColumn.HeaderText = "Description"; 
  descriptionDataGridViewTextBoxColumn.Name = "descriptionDataGridViewTextBoxColumn"; 
  descriptionDataGridViewTextBoxColumn.ReadOnly = true;   
 
baseUnitofMeasureDataGridViewTextBoxColumn.DataPropertyName = "Base_Unit_of_Measure"; 
  baseUnitofMeasureDataGridViewTextBoxColumn.Frozen = true; 
  baseUnitofMeasureDataGridViewTextBoxColumn.HeaderText = "Base Unit of Measure"; 
  baseUnitofMeasureDataGridViewTextBoxColumn.Name = "baseUnitofMeasureDataGridViewTextBoxColumn"; 
  baseUnitofMeasureDataGridViewTextBoxColumn.ReadOnly = true; 
  baseUnitofMeasureDataGridViewTextBoxColumn.Width = 60;   
 
inventoryDataGridViewTextBoxColumn.DataPropertyName = "Inventory"; 
  inventoryDataGridViewTextBoxColumn.Frozen = true; 
  inventoryDataGridViewTextBoxColumn.HeaderText = "Inventory"; 
  inventoryDataGridViewTextBoxColumn.Name = "inventoryDataGridViewTextBoxColumn"; 
  inventoryDataGridViewTextBoxColumn.ReadOnly = true; 
  inventoryDataGridViewTextBoxColumn.Width = 80;   
 
pDataGridView.AllowUserToAddRows = false; 
  pDataGridView.AllowUserToDeleteRows = false; 
  pDataGridView.AutoGenerateColumns = false; 
  pDataGridView.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize; 
  pDataGridView.Columns.AddRange(new DataGridViewColumn[] { 
  noDataGridViewTextBoxColumn, 
  descriptionDataGridViewTextBoxColumn, 
  baseUnitofMeasureDataGridViewTextBoxColumn, 
  inventoryDataGridViewTextBoxColumn}); 
  pDataGridView.Dock = DockStyle.Fill; 
  pDataGridView.Location = new Point(0, 0); 
  pDataGridView.Name = "dataGridView1"; 
  pDataGridView.ReadOnly = true; 
  pDataGridView.TabIndex = 0; 
  // 
  bindingSource1 = new BindingSource(); 
  pDataGridView.DataSource = bindingSource1; 
}

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

 
private void CreateLocationColumns() 
{ 
  LocationListWS.LocationList_Service LocationListService 
  = new LocationListWS.LocationList_Service(); 
  LocationListService.UseDefaultCredentials = true;   
 
LocationListWS.LocationList[] locations 
  = LocationListService.ReadMultiple(null, null, 0);   
 
pDataGridView.VirtualMode = true; 
  int offset = 4; 
  foreach (LocationListWS.LocationList location in locations) 
  { 
  pDataGridView.Columns.Insert(offset, new DataGridViewTextBoxColumn()); 
  pDataGridView.Columns[offset].HeaderText = location.Code; 
  offset += 1; 
  } 
  pDataGridView.CellValueNeeded 
  += new DataGridViewCellValueEventHandler(dataGridView1_CellValueNeeded);   
 
}

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

 
private void dataGridView1_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e) 
{ 
  if (e.ColumnIndex > 3) 
  { 
  e.Value = 0;   
 
DataGridViewRow row = pDataGridView.Rows[e.RowIndex]; 
  ItemCardWS.ItemCard item 
  = row.DataBoundItem as ItemCardWS.ItemCard; 
  if (item != null) 
  { 
  e.Value = ItemCardService.WsCalcfield(item.Key, 
  pDataGridView.Columns[e.ColumnIndex].HeaderText); 
  } 
  } 
}

После создания столбцов, неплохо бы перейти к созданию строк, за это отвечает функция CreateItemLines

 
private void CreateItemLines() 
  { 
  ItemCardService = new ItemCardWS.ItemCard_Service(); 
  ItemCardService.UseDefaultCredentials = true;   
 
pDataGridView.DataSource 
  = ItemCardService.ReadMultiple(null, null, 0);   
 
}

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

Надеюсь вы помните, что нужно делать с компонентом дальше? Напомню, что его нужно:

  • Подписать.
  • Зарегистрировать в Microsoft Dynamics NAV.
  • Добавить в страницу.

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

Вид страницы с подключаемым компонентом из Дизайнера страниц

А из ролеориентированного клиента она выглядит так:

Страница с подключаемым компонентом, содержащим матричную форму

В целом работоспособно, но выглядит не очень. Предлагаю навести марафет.

Для начала уберем название поля (слово MyMatrix).

Для этого переопределим свойство AllowCaptionControl. Это свойство наследуется от класса родителя StringControlAddInBase и отвечает за отображение заголовка.

 
public override bool AllowCaptionControl 
  { 
  get 
  { 
  return false; 
  } 
  }

Далее избавимся от рамки. Потому как рамка Dynamics NAV вместе с рамкой DataGridView смотрятся отвратно. Уберем рамку DataGridView, для этого добавим следующую строку в функцию InitializeDataGridView.

pDataGridView.BorderStyle = BorderStyle.None;

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

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

 
  pDataGridView.Columns.Insert(offset, new DataGridViewTextBoxColumn());

Будет использоваться строка

 
  pDataGridView.Columns.Insert(offset, new DataGridViewLinkColumn());

Там же будет выполнена подписка на событие CellContentClick, которое вызывается при нажатии на гиперссылку:

 
  dataGridView1.CellContentClick 
  += new DataGridViewCellEventHandler(dataGridView1_CellContentClick);

Ниже приведен код функции dataGridView1_CellContentClick, отвечающей за обработку события.

 
  private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e) 
  { 
  if (e.ColumnIndex > 3) 
  { 
  DataGridViewRow row = pDataGridView.Rows[e.RowIndex]; 
  ItemCardWS.ItemCard item 
  = row.DataBoundItem as ItemCardWS.ItemCard;   
 
RaiseControlAddInEvent(e.ColumnIndex, item.No + "#$#" + pDataGridView.Columns[e.ColumnIndex].HeaderText); 
  } 
  }

Далее добавим C\AL код, который будет обрабатывать событие подключаемого компонента, но уже в Dynamics NAV.

C\AL код триггера onControlAddIn

В нем мы разбираем полученную строку и используем ее в качестве фильтра.

И вот что у нас получилось в результате:

Улучшенная страница, содержащая подключаемый компонент

При щелчке по гиперссылке открывается книга товарных операций с фильтрами по товару и складу:

Книга товарных операций с фильтрами по товару и складу

Метки:



Оставьте свой отзыв!