# Урок 2. Редактирование таблицы

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

В этом уроке мы:

* познакомимся с паттерном Updated;
* узнаем, в чем различие между событийными условиями и условиями сравнения.

## Паттерн **Updated**

На прошлом уроке мы столкнулись с проблемой лишнего срабатывания команды **CityDatabaseTableAddRowValueSetCommand** на стартовой форме при закрытии карточки города по крестику (без сохранения). В результате чего происходило добавление пустой строки в таблицу со списком городов. Причина этого в том, что родительская форма не знала, было ли сохранено изменение на дочерней форме, или форму закрыли по крестику. Команда **CityDatabaseTableAddRowValueSetCommand** всегда выполнялась следом за командой **CityAddFormShowCommand**, которая завершала свое выполнение после закрытия дочерней формы.

Для решения этой проблемы есть паттерн **Updated**.

### Дочерняя форма

Перейдем в файл карточки города (TemplateCityEdit.xml).

В параметры формы, тэг `<Parameters>`, добавим параметр:

{% code title="TemplateCityEdit.xml" %}

```markup
<Parameter Name="Updated">False</Parameter>
```

{% endcode %}

Этот параметр и будем использовать для уведомления родительской формы о сохраненных изменениях.

Создадим команду, которая будет присваивать параметру значение **True** в момент сохранения данных:

{% code title="TemplateCityEdit.xml" %}

```markup
<Command Name="UpdatedTrueValueSetCommand" Type="ValueSetCommand" Assembly="Commands">
  <Parameter Name="Updated">True</Parameter>
</Command>
```

{% endcode %}

Добавим вызов этой команды в последовательность команд **SaveSequentialCommand**, которую вызываем по кнопке "Сохранить". В итоге общий синтаксис команды будет иметь вид:

{% code title="TemplateCityEdit.xml" %}

```markup
<Command Name="SaveSequentialCommand" Type="SequentialCommand" Assembly="Commands">
  <Commands>
    <Command Name="CityTitleValueSetCommand" />
    <Command Name="UpdatedTrueValueSetCommand" />
    <Command Name="FormCloseCommand" />
  </Commands>
</Command>
```

{% endcode %}

Отлично! Теперь можно заняться родительской формой, на которой будем проверять этот параметр.

### Родительская форма

Перейдем в файл описания стартовой формы (TemplateStart.xml).

В тэг `<Conditions>` добавим описание условия типа [EqualCondition](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/conditions/comparison_condition/equal_condition), в котором будем проверять значение параметра Updated команды **CityAddFormShowCommand**:

{% code title="TemplateStart.xml" %}

```markup
<Condition Name="CityAddFormUpdatedTrueEqualCondition" Type="EqualCondition" Assembly="Conditions">
  <Items>
    <Item>
      <Command Name="CityAddFormShowCommand" Parameter="Updated" />
    </Item>
    <Item>True</Item>
  </Items>
  <DataType Type="BooleanDataType" />
</Condition>
```

{% endcode %}

Последовательность команд на кнопке **CityAddButton** нужно изменить так, чтобы вызов команды **CityDatabaseTableAddRowValueSetCommand** происходил только в том случае, если дочерняя форма в качестве параметра **Updated** вернула значение **True**. Для этого воспользуемся конструкцией `<If>`:

```markup
<Commands>
  <Command Name="CityAddFormShowCommand" />
  <If>
    <When>
      <Condition Name="CityAddFormUpdatedTrueEqualCondition" />
    </When>
    <Then>
      <Command Name="CityDatabaseTableAddRowValueSetCommand" />
    </Then>
  </If>
</Commands>
```

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

{% code title="TemplateStart.xml" %}

```markup
<MyObject Name="CityAddButton" Type="Button" Assembly="BaseControls">
  <Top>
    <Object Name="CityDatabaseTable">
      <Property Name="Top" />
    </Object>
  </Top>
  <Left>
    <Calculate>
      <Expression>{0} + 5</Expression>
      <Items>
        <Item>
          <Object Name="CityDatabaseTable">
            <Property Name="Right" />
          </Object>
        </Item>
      </Items>
    </Calculate>
  </Left>
  <Width>40</Width>
  <Height>40</Height>
  <Hint>Добавить запись...</Hint>
  <BackgroundImage>Images\24x24\plus.png</BackgroundImage>
  <BackgroundImageLayout>Center</BackgroundImageLayout>
  <FlatStyle>Flat</FlatStyle>
  <FlatBorderSize>1</FlatBorderSize>
  <FlatBorderColor>ButtonFlatBorderColor</FlatBorderColor>
  <FlatMouseDownBackColor>ButtonFlatMouseDownBackColor</FlatMouseDownBackColor>
  <FlatMouseOverBackColor>ButtonFlatMouseOverBackColor</FlatMouseOverBackColor>
  <Commands>
    <Command Name="CityAddFormShowCommand" />
    <If>
      <When>
        <Condition Name="CityAddFormUpdatedTrueEqualCondition" />
      </When>
      <Then>
        <Command Name="CityDatabaseTableAddRowValueSetCommand" />
      </Then>
    </If>
  </Commands>
</MyObject>
```

{% endcode %}

Перезапустите проект. Убедитесь, что формы успешно загружаются, и по кнопке "Добавить запись..." корректно добавляются новые строки.

Отлично! Теперь нет лишнего срабатывания команды **CityDatabaseTableAddRowValueSetCommand**, и в таблицу добавляются нужные строки.

И вроде бы все хорошо, но есть один момент. Когда открыта карточка города, мы не можем вернуться к форме со списком городов. Причина такого поведения в том, что окно карточки города открывается в модальном режиме. Модальное окно не позволяет выполнить другую задачу в приложении, пока оно не будет закрыто.

### Модальные и немодальные окна

Режим открытия окна задается в команде [FormShowCommand](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/commands/form_show_command) с помощью тэга `<Show>`. И в команде **CityAddFormShowCommand** у атрибута `Type` этого тэга задано значение **Modal**:

{% code title="TemplateStart.xml" %}

```markup
<Command Name="CityAddFormShowCommand" Type="FormShowCommand" Assembly="Commands">
  <Xml Type="Path">TemplateCityEdit.xml</Xml>
  <Show Type="Modal" />
</Command>
```

{% endcode %}

С модальными окнами есть небольшой плюс. Команда FormShowCommand на открытие модального окна будет держать процесс и передаст управление следующей команде только после закрытия дочернего окна. Эта особенность и позволяет нам указать в тэге `<Commands>` кнопки **CityAddButton** последовательно вызов команды **CityAddFormShowCommand** и блок `<If>` с вызовом команды **CityDatabaseTableAddRowValueSetCommand**.

Но использование модального окна не всегда оправдано. И в нашем случае оно создает неудобство. Давайте изменим режим открытия формы на **None**. В этом режиме форма откроется параллельно родительской.

{% code title="TemplateStart.xml" %}

```markup
<Command Name="CityAddFormShowCommand" Type="FormShowCommand" Assembly="Commands">
  <Xml Type="Path">TemplateCityEdit.xml</Xml>
  <Show Type="None" />
</Command>
```

{% endcode %}

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

Изменив режим открытия формы с модального на параллельный, мы словили неприятную ситуацию с отложенным добавлением строк в таблицу. На самом деле проблема в том, что при открытии формы в немодальном режиме команда FormShowCommand не будет дожидаться закрытия этой формы, а сразу же передаст управление дальше. И т.к. мы не дождались результатов с дочерней формы, то параметры **Updated** и **CityTitle** команды **CityAddFormShowCommand** будут хранить старые значения. Что и создает эффект отложенного срабатывания команды добавления строк.

Следовательно, при работе с немодальными формами мы не можем в тэге `<Commands>`последовательно вызывать команду FormShowCommand и обрабатывать ее результат. В этом случае обработку результатов с немодальных форм необходимо вынести в конструкцию [Execution](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/executions).

Execution отвечает за автоматическое выполнение последовательности команд при срабатывании условий, на которые этот Execution подписан.

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

```markup
<Execution>
  <ConditionExpression>
    <Condition Name="FormClosingCondition" />
  </ConditionExpression>
  <Commands>
    <Command Name="FormCloseCommand" />
  </Commands>
</Execution>
```

* [FormClosingCondition](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/conditions/event_condition/form_closing_condition) - системное условие, отслеживающее события попытки закрытия формы;
* [FormCloseCommand](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/commands/form_close_command) - системная команда закрытия текущей формы

### Обработка результата немодальных форм

Давайте блок `<If>` из последовательности команд на кнопке **CityAddButton** переделаем на `<Execution>`. Для этого на стартовой форме (TemplateStart.xml) создадим новый `<Execution>`, который будет вызывать команду **CityDatabaseTableAddRowValueSetCommand**, и подпишем его на условие **CityAddFormUpdatedTrueEqualCondition**:

{% code title="TemplateStart.xml" %}

```markup
<Execution>
  <ConditionExpression>
    <Condition Name="CityAddFormUpdatedTrueEqualCondition" />
  </ConditionExpression>
  <Commands>
    <Command Name="CityDatabaseTableAddRowValueSetCommand" />
  </Commands>
</Execution>
```

{% endcode %}

В итоге синтаксис кнопки добавления записи в таблицу будет выглядеть так:

{% code title="TemplateStart.xml" %}

```markup
<MyObject Name="CityAddButton" Type="Button" Assembly="BaseControls">
  <Top>
    <Object Name="CityDatabaseTable">
      <Property Name="Top" />
    </Object>
  </Top>
  <Left>
    <Calculate>
      <Expression>{0} + 5</Expression>
      <Items>
        <Item>
          <Object Name="CityDatabaseTable">
            <Property Name="Right" />
          </Object>
        </Item>
      </Items>
    </Calculate>
  </Left>
  <Width>40</Width>
  <Height>40</Height>
  <Hint>Добавить запись...</Hint>
  <BackgroundImage>Images\24x24\plus.png</BackgroundImage>
  <BackgroundImageLayout>Center</BackgroundImageLayout>
  <FlatStyle>Flat</FlatStyle>
  <FlatBorderSize>1</FlatBorderSize>
  <FlatBorderColor>ButtonFlatBorderColor</FlatBorderColor>
  <FlatMouseDownBackColor>ButtonFlatMouseDownBackColor</FlatMouseDownBackColor>
  <FlatMouseOverBackColor>ButtonFlatMouseOverBackColor</FlatMouseOverBackColor>
  <Commands>
    <Command Name="CityAddFormShowCommand" />
  </Commands>
</MyObject>
```

{% endcode %}

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

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

{% hint style="info" %}
Все условия можно поделить на две группы: условия сравнения и событийные условия.

**Условия сравнения** рассылают уведомления своим подписчикам, только если результат условия изменится. К условиям сравнения относятся все условия, в которых происходит сравнение значений: [EqualCondition](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/conditions/comparison_condition/equal_condition), [IsNullCondition](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/conditions/comparison_condition/is_null_condition) и подобные. Также к условиям сравнения относится условие [NestedCondition](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/conditions/nested_condition).

**Событийные условия** рассылают уведомления каждый раз, когда происходит событие. К ним относятся условия [CellDoubleClickCondition](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/conditions/event_condition/cell_double_click_condition), [FormClosingCondition](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/conditions/event_condition/form_closing_condition) и подобные.
{% endhint %}

В нашем случае, когда мы первый раз открыли карточку города и сохранили изменение, значение параметра **Updated** изменилось на **True**. И результат условия **CityAddFormUpdatedTrueEqualCondition** также изменился на **True**. Условие отправило своим подписчикам уведомление об изменении. Execution отловил это уведомление и выполнил свою последовательность команд. Когда мы открыли карточку города второй раз и сохранили новый город, то результат условия **CityAddFormUpdatedTrueEqualCondition** не изменился. Он по-прежнему остался **True**. Поэтому условие не разослало событие об изменении значения, и Execution не сработал. Когда открыли и закрыли дочернюю форму без сохранения, то значение параметра **Updated** осталось **False** (по умолчанию), а результат условия **CityAddFormUpdatedTrueEqualCondition** изменился на **False**.

В платформе есть возможность условие сравнения принудительно сделать событийным условием. Для этого у тэга `<Condition>` есть необязательный тэг `<AlwaysChange>` - признак, определяющий, будет ли условие рассылать событие изменения своего значения в том случае, если операнды условия были изменены, но само значение условия не изменилось.

Давайте наше условие **CityAddFormUpdatedTrueEqualCondition** сделаем принудительно событийным, добавив тэг `<AlwaysChange>`со значение **True**:

{% code title="TemplateStart.xml" %}

```markup
<Condition Name="CityAddFormUpdatedTrueEqualCondition" Type="EqualCondition" Assembly="Conditions">
  <AlwaysChange Value="True" />
  <Items>
    <Item>
      <Command Name="CityAddFormShowCommand" Parameter="Updated" />
    </Item>
    <Item>True</Item>
  </Items>
  <DataType Type="BooleanDataType" />
</Condition>
```

{% endcode %}

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

### Проблемы событийных условий в Execution

Рассмотрим следующую ситуацию. У нас есть два событийных условия:&#x20;

* Condition1 - например, проверка параметра Updated у команды открытия формы на создание записи;
* Condition2 - например, проверка параметра Updated у команды открытия формы на редактирование записи.

И есть Execution, который при срабатывании одного из условий должен выполнять какую-то команду AnyCommand:

```markup
<Execution>
  <ConditionExpression>
    <Or>
      <Condition Name="Condition1" />
      <Condition Name="Condition2" />
    </Or>
  </ConditionExpression>
  <Commands>
    <Command Name="AnyCommand" />
  </Commands>
</Execution>
```

Когда мы открыли форму на создание записи и сохранили изменения, в параметре Updated команды будет значение True. Следовательно, условие Condition1 изменит свое значение на True и разошлет событие своим подписчикам. Execution поймает это событие и проверит свое условие: Condition1 (True) или Condition2 (False) - результат будет True. Команда AnyCommand выполнится.

Теперь мы откроем форму на редактирование записи и закроем без сохранения. В параметре Updated команды открытия формы будет значение False. А так как условие Condition2 событийное, то оно все равно разошлет уведомление своим подписчикам. Execution поймает это событие и проверит свое условие. Condition1 по-прежнему имеет значение True, а значение условия Condition2 равно False. И результат проверки Execution будет True. Что приведет к лишнему выполнению команды AnyCommand.&#x20;

Чтобы избежать подобной проблемы данный Execution следует разбить на два отдельных Execution-а, которые будут подписаны на разные условия.

## Форма на редактирование

С проблемой из прошлого урока разобрались. Теперь можно приступить к реализации возможности редактирования записей в таблице.

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

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

### Режим формы Edit

Перейдем в файл карточки города (TemplateCityEdit.xml).

В каком режиме открыта карточка города: на редактирование или создание - мы будем определять через параметр **Edit**. Давайте в тэг `<Parameters>` формы добавим этот параметр:

{% code title="TemplateCityEdit.xml" %}

```markup
<Parameter Name="Edit">False</Parameter>
```

{% endcode %}

Подредактируем значение параметра **Title**, чтобы заголовок формы информировал пользователя о режиме карточки города:

{% code title="TemplateCityEdit.xml" %}

```markup
<Parameter Name="Title">
  <Switch>
    <Case>
      <When>
        <Parameter Name="Edit" />
      </When>
      <Then>Редактирование города</Then>
    </Case>
    <Case>Добавление города</Case>
  </Switch>
</Parameter>
```

{% endcode %}

Для передачи наименование города с родительской формы будем использовать существующий параметр **CityTitle**. И чтобы значение параметра можно было прочитать, укажем этот параметр в тэге `<Text>` текстового поля **TitleTextBox**. Таким образом, синтаксис **TitleTextBox** будет иметь вид:

{% code title="TemplateCityEdit.xml" %}

```markup
<MyObject Name="TitleTextBox" Type="TextBox" Assembly="BaseControls">
  <Top>
    <Object Name="TitleLabel">
      <Property Name="Bottom" />
    </Object>
  </Top>
  <Left>
    <Object Name="TitleLabel">
      <Property Name="Left" />
    </Object>
  </Left>
  <Width>
    <Calculate>
      <Expression>{0} - {1} - 10</Expression>
      <Items>
        <Item>
          <Object Name="ContentPanel">
            <Property Name="Width" />
          </Object>
        </Item>
        <Item>
          <Object Name="TitleTextBox">
            <Property Name="Left" />
          </Object>
        </Item>
      </Items>
    </Calculate>
  </Width>
  <Text>
    <Parameter Name="CityTitle" />
  </Text>
</MyObject>
```

{% endcode %}

### Передача данных на дочернюю форму

Вернемся в файл стартовой формы (TemplateStart.xml). Добавим на форму команду на открытие карточки города для редактирования:

{% code title="TemplateStart.xml" %}

```markup
<Command Name="CityEditFormShowCommand" Type="FormShowCommand" Assembly="Commands">
  <Xml Type="Path">TemplateCityEdit.xml</Xml>
  <Show Type="None" />
  <Parameters>
    <Parameter Name="Edit"> </Parameter>
    <Parameter Name="CityTitle"> </Parameter>
  </Parameters>
</Command>
```

{% endcode %}

Необязательный тэг `<Parameters>` ожидает список параметров, которые будут передаваться на дочернюю форму. Для параметра **Edit** укажем значение **True**. А чтобы в параметре **CityTitle** передать наименование города, выбранного в таблице, воспользуемся get-проперти [SelectedRowCellValueByColumnName](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/objects/databasetable#get_selected_row_cell_value_by_column_name) объекта [DatabaseTable](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/objects/databasetable):

```markup
<Object Name="CityDatabaseTable">
  <Property Name="SelectedRowCellValueByColumnName">
    <Parameters>
      <Parameter Name="ColumnName">Title</Parameter>
    </Parameters>
  </Property>
</Object>
```

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

Общий синтаксис команды выглядит так:

{% code title="TemplateStart.xml" %}

```markup
<Command Name="CityEditFormShowCommand" Type="FormShowCommand" Assembly="Commands">
  <Xml Type="Path">TemplateCityEdit.xml</Xml>
  <Show Type="None" />
  <Parameters>
    <Parameter Name="Edit">True</Parameter>
    <Parameter Name="CityTitle">
      <Object Name="CityDatabaseTable">
        <Property Name="SelectedRowCellValueByColumnName">
          <Parameters>
            <Parameter Name="ColumnName">Title</Parameter>
          </Parameters>
        </Property>
      </Object>
    </Parameter>
  </Parameters>
</Command>
```

{% endcode %}

### Открытие формы редактирования записи

Для кнопок нам понадобятся иконки. Скачайте архив с изображениями и разархивируйте его в папку проекта **\Template\Projects\1. Template\Forms\Images\24x24**.

{% file src="<https://3019442075-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M_eBlWEU4C3o2GVEAAr%2F-Mjc0t2FTFTt4Elx4g2n%2F-Mjc1dX3tCwxWbqHOgRW%2Flesson2-Images.zip?alt=media&token=d76c9e5f-e8a1-4b15-9587-93051a4f3639>" %}
Иконки для кнопок
{% endfile %}

Создадим кнопку для редактирования города. Для этого разместим приведенный ниже код в **ContentPanel** следом за описанием объекта **CityAddButton**.

{% code title="TemplateStart.xml" %}

```markup
<MyObject Name="CityEditButton" Type="Button" Assembly="BaseControls">
  <Top>
    <Calculate>
      <Expression>{0} + 5</Expression>
      <Items>
        <Item>
          <Object Name="CityAddButton">
            <Property Name="Bottom" />
          </Object>
        </Item>
      </Items>
    </Calculate>
  </Top>
  <Left>
    <Object Name="CityAddButton">
      <Property Name="Left" />
    </Object>
  </Left>
  <Width>
    <Object Name="CityAddButton">
      <Property Name="Width" />
    </Object>
  </Width>
  <Height>
    <Object Name="CityAddButton">
      <Property Name="Height" />
    </Object>
  </Height>
  <Hint>Редактировать запись...</Hint>
  <BackgroundImage>Images\24x24\pencil.png</BackgroundImage>
  <BackgroundImageLayout>Center</BackgroundImageLayout>
  <FlatStyle>Flat</FlatStyle>
  <FlatBorderSize>1</FlatBorderSize>
  <FlatBorderColor>ButtonFlatBorderColor</FlatBorderColor>
  <FlatMouseDownBackColor>ButtonFlatMouseDownBackColor</FlatMouseDownBackColor>
  <FlatMouseOverBackColor>ButtonFlatMouseOverBackColor</FlatMouseOverBackColor>
  <Commands>
    <Command Name="CityEditFormShowCommand" />
  </Commands>
</MyObject>
```

{% endcode %}

У объектов на форме есть признак **Enabled**, который определяет, будет ли объект активен. В нашем случае кнопка редактирования должна быть неактивной, если в таблице не выбрана запись. Для этого нам нужно создать условие типа [GreaterCondition](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/conditions/comparison_condition/greater_condition), в котором проверим количество выделенных строк в таблице. Для получения количества выделенных строк у объекта DatabaseTable воспользуемся get-проперти [SelectedRowsCount](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/objects/databasetable#get_selected_rows_count).

{% code title="TemplateStart.xml" %}

```markup
<Condition Name="CitySelectedCondition" Type="GreaterCondition" Assembly="Conditions">
  <Items>
    <Item>
      <Object Name="CityDatabaseTable">
        <Property Name="SelectedRowsCount" />
      </Object>
    </Item>
    <Item>0</Item>
  </Items>
  <DataType Type="IntegerDataType" />
</Condition>
```

{% endcode %}

{% hint style="info" %}
В условии CitySelectedCondition можно проверять индекс выделенной строки, полученный через get-проперти [SelectedRowIndex](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/objects/databasetable#get_selected_row_index).\
Индексы строк в таблице начинаются с 0.
{% endhint %}

Укажем это условие в тэге `<Enabled>` кнопки **CityEditButton**. Таким образом, синтаксис кнопки редактирования города будет иметь вид:

{% code title="TemplateStart.xml" %}

```markup
<MyObject Name="CityEditButton" Type="Button" Assembly="BaseControls">
  <Top>
    <Calculate>
      <Expression>{0} + 5</Expression>
      <Items>
        <Item>
          <Object Name="CityAddButton">
            <Property Name="Bottom" />
          </Object>
        </Item>
      </Items>
    </Calculate>
  </Top>
  <Left>
    <Object Name="CityAddButton">
      <Property Name="Left" />
    </Object>
  </Left>
  <Width>
    <Object Name="CityAddButton">
      <Property Name="Width" />
    </Object>
  </Width>
  <Height>
    <Object Name="CityAddButton">
      <Property Name="Height" />
    </Object>
  </Height>
  <Hint>Редактировать запись...</Hint>
  <BackgroundImage>Images\24x24\pencil.png</BackgroundImage>
  <BackgroundImageLayout>Center</BackgroundImageLayout>
  <FlatStyle>Flat</FlatStyle>
  <FlatBorderSize>1</FlatBorderSize>
  <FlatBorderColor>ButtonFlatBorderColor</FlatBorderColor>
  <FlatMouseDownBackColor>ButtonFlatMouseDownBackColor</FlatMouseDownBackColor>
  <FlatMouseOverBackColor>ButtonFlatMouseOverBackColor</FlatMouseOverBackColor>
  <Enabled>
    <Condition Name="CitySelectedCondition" />
  </Enabled>
  <Commands>
    <Command Name="CityEditFormShowCommand" />
  </Commands>
</MyObject>
```

{% endcode %}

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

<figure><img src="https://3019442075-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_eBlWEU4C3o2GVEAAr%2Fuploads%2F0GjnC61fTpSu1OxuStDO%2Fimage.png?alt=media&#x26;token=0618804c-081d-4b47-818d-d43a6a53c439" alt=""><figcaption></figcaption></figure>

Открывать карточку города на редактирование можно и по двойному клику по строке таблицы. Для отлавливания такого событие есть условие [CellDoubleClickCondition](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/conditions/event_condition/cell_double_click_condition). Добавим следующий код на стартовую форму:

{% code title="TemplateStart.xml" %}

```markup
<Condition Name="CityDatabaseTableCellDoubleClickCondition" Type="CellDoubleClickCondition" Assembly="Conditions">
  <Table Name="CityDatabaseTable" />
</Condition>
```

{% endcode %}

Теперь создадим Execution, который подпишется на это условие и будет вызывать команду на открытие формы для редактирования записи:

{% code title="TemplateStart.xml" %}

```markup
<Execution>
  <ConditionExpression>
    <Condition Name="CityDatabaseTableCellDoubleClickCondition" />
  </ConditionExpression>
  <Commands>
    <Command Name="CityEditFormShowCommand" />
  </Commands>
</Execution>
```

{% endcode %}

### Обновление данных в строке таблицы

На прошлом уроке мы узнали, что у таблицы [DatabaseTable](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/objects/databasetable) есть set-проперти [AddRow](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/objects/databasetable#set_add_row), [AddRows](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/objects/databasetable#set_add_rows), [UpdateRow](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/objects/databasetable#set_update_row), [UpdateRows](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/objects/databasetable#set_update_rows) и [DeleteRowsByIndices](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/objects/databasetable#set_delete_rows_by_indices). Сейчас нам понадобится свойство UpdateRow. Создадим команду ValueSetCommand для использования этого свойства:

{% code title="TemplateStart.xml" %}

```markup
<Command Name="CityDatabaseTableUpdateRowValueSetCommand" Type="ValueSetCommand" Assembly="Commands">
  <Object Name="CityDatabaseTable">
    <Property Name="UpdateRow">
      <Parameters>
        <Parameter Name="RowIndex"> </Parameter>
        <Parameter Name="ColumnNames"> </Parameter>
        <Parameter Name="Values"> </Parameter>
      </Parameters>
    </Property>
  </Object>
</Command>
```

{% endcode %}

Помимо знакомых нам параметров **ColumnNames** и **Values**, свойство имеет параметр **RowIndex** - индекс строки, значения которой будут обновлены. Этот параметр ожидает целочисленное значение. Если этот параметр отсутствует, то обновятся все строки.

У объекта DatabaseTable есть get-проперти [SelectedRowIndex](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/objects/databasetable#get_selected_row_index), с его помощью и получим индекс редактируемой строки:

```markup
<Parameter Name="RowIndex">
  <Object Name="CityDatabaseTable">
    <Property Name="SelectedRowIndex"/>
  </Object>
</Parameter>
```

Готовый синтаксис команды обновления значений в выбранной строке будет иметь вид:

{% code title="TemplateStart.xml" %}

```markup
<Command Name="CityDatabaseTableUpdateRowValueSetCommand" Type="ValueSetCommand" Assembly="Commands">
  <Object Name="CityDatabaseTable">
    <Property Name="UpdateRow">
      <Parameters>
        <Parameter Name="RowIndex">
          <Object Name="CityDatabaseTable">
            <Property Name="SelectedRowIndex" />
          </Object>
        </Parameter>
        <Parameter Name="ColumnNames">
          <Structure Type="List">
            <Item>Title</Item>
          </Structure>
        </Parameter>
        <Parameter Name="Values">
          <Structure Type="List">
            <Item>
              <Command Name="CityEditFormShowCommand" Parameter="CityTitle" />
            </Item>
          </Structure>
        </Parameter>
      </Parameters>
    </Property>
  </Object>
</Command>
```

{% endcode %}

Теперь самостоятельно создайте условие EqualCondition для проверки параметра **Updated** у команды **CityEditFormShowCommand**. А затем создайте Execution, который по этому условию будет выполнять команду **CityDatabaseTableUpdateRowValueSetCommand**.

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

### Сохранение изменений в таблицу в базе данных

#### Отправка данных на сервер

На прошлом уроке мы создали сохраняющее соединение с данными **CityDatabaseTableSetDataConnection**. Давайте внесем в него пару изменений.

Первым делом добавим новый параметр **CityId**, по которому будем обновлять наименование в нужной записи:

```markup
<Parameter NativeName="CityId">
  <Column Name="CityId" />
</Parameter>
```

Также в тэг `<SqlQueries>` добавим update-запрос, который назовем, например, **CityUpdateSqlQuery**. Таким образом, общий синтаксис соединения с данными для отправки будет выглядеть так:

{% code title="TemplateStart.xml" %}

```markup
<DataConnection Name="CityDatabaseTableSetDataConnection" Type="DatabaseTableSetDataConnection" Assembly="ComplexDataConnections">
  <Workflow Name="Template" />
  <DatabaseTable Name="CityDatabaseTable" />
  <Parameters>
    <Parameter NativeName="CityId">
      <Column Name="CityId" />
    </Parameter>
    <Parameter NativeName="Title">
      <Column Name="Title" />
    </Parameter>
  </Parameters>
  <SqlQueries>
    <SqlQuery Name="CityInsertSqlQuery" Type="Insert" />
    <SqlQuery Name="CityUpdateSqlQuery" Type="Update" />
  </SqlQueries>
</DataConnection>
```

{% endcode %}

#### Запрос на обновление данных

Перейдем в файл серверной xml (Template.xml). И добавим запрос на обновление данных в таблице **city** в базе данных:

{% code title="Template.xml" %}

```markup
<SqlQuery Name="CityUpdateSqlQuery">
  <Text>
    UPDATE template.city
    SET
      title = {Title}
    WHERE
      city_id = {CityId};
  </Text>
</SqlQuery>
```

{% endcode %}

Добавим этот запрос в ранее созданный **CityEditSqlQueryPermission**:

{% code title="Template.xml" %}

```markup
<Permission Name="CityEditSqlQueryPermission" Type="SqlQueryPermission">
  <SqlQueries>
    <SqlQuery Name="CityInsertSqlQuery" />
    <SqlQuery Name="CityUpdateSqlQuery" />
  </SqlQueries>
</Permission>
```

{% endcode %}

Перезапустите проект. Убедитесь, что формы успешно загружаются, а изменения корректно сохраняются в базе данных.

## Удаление записей

Самостоятельно реализуйте удаление записи из таблицы.

Для кнопки используйте иконку из файла \Images\24x24\delete.png. Не забудьте, что кнопка должна быть неактивной, если в таблице не выбрано строки. Для удаления строки из таблицы используйте свойство [DeleteRowsByIndices](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/objects/databasetable#set_delete_rows_by_indices) самой таблицы. А для удаления записи из таблицы в базе данных расширьте сохраняющее соединение **CityDatabaseTableSetDataConnection**.

По завершению этого задания у вас должна получиться форма подобного вида:

<figure><img src="https://3019442075-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_eBlWEU4C3o2GVEAAr%2Fuploads%2FEOo8vgw9qybq6aSkoen7%2Fimage.png?alt=media&#x26;token=30b705e0-028b-4489-a119-b0d79a8cd911" alt=""><figcaption></figcaption></figure>

## Обновление данных из базы данных

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

Причина этого в том, что форма не получила из базы id новой записи, и в запрос на удаление таблица передала значение NULL. Хотя таблица на форме удалила у себя эту строку.

Чтобы на форме получить актуальные данные из таблицы в базе данных, нам необходимо обновить **CityPrimaryGetDataConnection**. Для этого создадим команду типа [DataConnectionRefreshCommand](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/commands/dc_refresh_command), которая при вызове будет перезагружать наше соединение с данными:

{% code title="TemplateStart.xml" %}

```markup
<Command Name="CityDataConnectionRefreshCommand" Type="DataConnectionRefreshCommand" Assembly="Commands">
  <DataConnections>
    <DataConnection Name="CityPrimaryGetDataConnection" Packet="True" />
  </DataConnections>
</Command>
```

{% endcode %}

И добавим вызов этой команды на кнопку **SaveButton** после команды **CityDatabaseTableSaveCommand**:

{% code title="TemplateStart.xml" %}

```markup
<MyObject Name="SaveButton" Type="Button" Assembly="BaseControls">
  <Top>5</Top>
  <Right>
    <Formula>
      <Minus DataType="IntegerDataType">
        <Item>
          <Object Name="FootPanel">
            <Property Name="Right" />
          </Object>
        </Item>
        <Item>5</Item>
      </Minus>
    </Formula>
  </Right>
  <Height>30</Height>
  <Width>200</Width>
  <TextAlign>MiddleCenter</TextAlign>
  <FontStyle>ButtonFontStyle</FontStyle>
  <ImageAlign>MiddleLeft</ImageAlign>
  <Image>Images\24x24\content-save.png</Image>
  <FlatStyle>Flat</FlatStyle>
  <FlatBorderSize>1</FlatBorderSize>
  <FlatBorderColor>ButtonFlatBorderColor</FlatBorderColor>
  <FlatMouseDownBackColor>ButtonFlatMouseDownBackColor</FlatMouseDownBackColor>
  <FlatMouseOverBackColor>ButtonFlatMouseOverBackColor</FlatMouseOverBackColor>
  <Text>Сохранить</Text>
  <Commands>
    <Command Name="CityDatabaseTableSaveCommand" />
    <Command Name="CityDataConnectionRefreshCommand" />
  </Commands>
</MyObject>
```

{% endcode %}

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

## Итоги <a href="#results" id="results"></a>

В конце прошлого урока мы определили две проблемы. Напомню их:

* При закрытии карточки города по крестику (без сохранения), в таблицу добавляются пустые строки. Происходит лишнее срабатывание команды CityDatabaseTableAddRowValueSetCommand;
* Нет проверки корректности заполнения текстового поля "Наименование".

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

В рамках паттерна Updated мы рассмотрели особенности использования модальных и немодальных форм (окон). Также узнали, что условия могут быть условиями сравнения и событийными условиями. И что в платформе есть возможность условия сравнения сделать принудительно событийными условиями.

На следующем уроке мы:

* создадим еще один список, который будет хранить данные по клиентам;
* рассмотрим пример выпадающего списка на форме;
* рассмотрим пример использования [SetDataConnection](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/dataconnections/set_dc) для сохранения данных.

## Ответы <a href="#answer" id="answer"></a>

Ниже представлен архив с xml-файлами форм и серверным xml-файл, а также бэкап базы данных. С помощью файлов можете проверить себя.

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td>lesson2-answer.zip</td><td><a href="https://wfsys.ru/download/wt_practice_desktop_answers/lesson2-answer.zip">https://wfsys.ru/download/wt_practice_desktop_answers/lesson2-answer.zip</a></td></tr></tbody></table>
