> For the complete documentation index, see [llms.txt](https://wfsys.gitbook.io/wt-practice/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://wfsys.gitbook.io/wt-practice/main/lesson_pattern_onclose.md).

# Урок 4. Паттерн onClose

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

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

Также рассмотрим, как обозначать обязательные поля на форме и проверять корректность их заполнения.

{% hint style="success" %}
Если хотите начать практику с этого урока, то вам необходимо развернуть учебный проект по инструкции в статье [Разворачивание проекта](https://wfsys.gitbook.io/workflow-technology/setting-up-dev-environment/manual-deployment-project).

При разворачивании проекта используйте backup базы данных, который можете найти в архиве из раздела [Ответы](/wt-practice/main/lesson_combo_box.md#answer) прошлого урока. Скопируйте папки *Forms*, *Workflow* и *Patterns* в папку с развернутым проектом, например, в папку *D:\WT\Projects\Template\Projects\1. Template*.

Инструкция по подключению шаблонов находится по [ссылке](/wt-practice/main/lesson_list_form.md#podklyuchenie-shablonov-k-proektu).
{% endhint %}

## Паттерн onClose

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

<figure><img src="https://3019442075-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_eBlWEU4C3o2GVEAAr%2Fuploads%2FKS4oLeLkZm7FJOMpv2co%2FonClose-1.png?alt=media&amp;token=de594593-102e-44a4-bc13-374735702565" alt=""><figcaption></figcaption></figure>

По кнопке "Да" мы будем сохранять изменения перед тем, как закрыть форму. По кнопке "Нет" будет закрывать форму без сохранения. А по кнопке "Отмена" будем оставаться на форме и ждать дальнейших действий пользователя.

Перейдем в файл карточки клиента (TemplateClientEdit.xml). На ее примере будем рассматривать паттерн.

### Проверка изменений на форме <a href="#checking-changes-on-form" id="checking-changes-on-form"></a>

Для отслеживания изменений у формы есть get-проперти [FormChanged](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/form#get_form_changed), которое возвращает значение True, если изменилось значение хотя бы одного объекта.

{% hint style="info" %}
Также есть set-проперти [FormChanged](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/form#set_form_changed), с его помощью можно вручную сбрасывать признак изменений на форме, но не сами изменения.

```xml
<Command Name="ResetFormChangedValueSetCommand" Type="ValueSetCommand" Assembly="Commands">
  <Form>
    <Property Name="FormChanged">False</Property>
  </Form>
</Command>
```

{% endhint %}

Давайте создадим условие, в котором будем проверять значение этого свойства:

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

```xml
<Condition Name="FormChangedTrueEqualCondition" Type="EqualCondition" Assembly="Conditions">
  <Items>
    <Item>
      <Form>
        <Property Name="FormChanged" />
      </Form>
    </Item>
    <Item>True</Item>
  </Items>
  <DataType Type="BooleanDataType" />
</Condition>
```

{% endcode %}

{% hint style="info" %}
Значение свойства формы [FormChanged](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/form#get_form_changed) определяется совокупностью get-проперти [ValueChanged](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/objects#get_value_changed) всех объектов на форме.

Свойство ValueChanged обозначает, было ли изменено значение объекта в процессе работы.

Чтобы форма исключала объект из проверки для определения значения свойства FormChanged, необходимо при описании объекта в тэге `<MyObject>` указать необязательный атрибут `ChangeForm` со значением False.

Также у тэга `<MyObject>` есть необязательный вложенный тэг `<Change>`, который задает настройки изменения свойства ValueChanged:

`<Change User="True" Source="False" ValueSet="True" />`

* Атрибут `User` относится к графическому интерфейсу и определяет, будет ли свойство ValueChanged иметь значение True, если пользователь изменит значение объекта;
* Атрибут `Source` определяет, какое значение будет присвоено свойству ValueChanged, если значение объекта обновится из источника. Если атрибут `Source` имеет значение False, и при этом значение из источника обновится, то ValueChanged присваивается значение False;
* Атрибут `ValueSet` также определяет, какое значение будет присвоено свойству ValueChanged, если значение объекта будет присвоено из команды ValueSetCommand. Если атрибут `ValueSet` имеет значение False, и при этом значение было присвоено из команды ValueSetCommand, то свойство ValueChanged будет иметь значение False.
  {% endhint %}

{% hint style="info" %}
У формы есть get-проперти [ChangedObjects](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/form#get_changed_objects), которое возвращает список измененных объектов в виде строки:

```xml
<Form>
  <Property Name="ChangedObjects" />
</Form>
```

С помощью этого get-проперти можно проверить, какие объекты формы изменились и повлияли на результат get-проперти формы [FormChanged](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/form#get_form_changed).
{% endhint %}

Для вывода сообщений пользователю используется команда [MessageBoxCommand](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/commands/message_box_command), которая открывает диалоговое окно сообщения с набором кнопок. Давайте создадим такую команду:

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

```xml
<Command Name="SaveOnCloseMessageBoxCommand" Type="MessageBoxCommand" Assembly="Commands">
  <Caption>Сохранение</Caption>
  <Text>Форма содержит несохраненные изменения.\rСохранить их перед закрытием?</Text>
  <Icon Type="Question" />
  <Buttons Type="YesNoCancel" />
</Command>
```

{% endcode %}

В атрибуте `Type` тэга `<Buttons>` указываем нужный нам набор кнопок.

Теперь нам нужен Execution, который по условию будет вызывать команду **SaveOnCloseMessageBoxCommand**.

На форме уже есть Execution, который обрабатывает событие закрытия формы:

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

```xml
<Execution>
  <ConditionExpression>
    <Or>
      <Condition Name="FormClosingCondition" />
      <Condition Name="EscapeKeyDownCondition" />
    </Or>
  </ConditionExpression>
  <Commands>
    <Command Name="FormCloseCommand" />
  </Commands>
</Execution>
```

{% endcode %}

В нем обрабатывается не только закрытие по крестику, которое отслеживает условие [FormClosingCondition](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/conditions/event_condition/form_closing_condition). Также отслеживается нажатие клавиши **Esc**. За это отвечает условие **EscapeKeyDownCondition** типа [KeyDownCondition](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/conditions/event_condition/key_down_condition).

Давайте переделаем этот Execution так, чтобы он учитывал наличие изменений на форме и вызывал команду с диалоговым окном:

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

```xml
<Execution>
  <ConditionExpression>
    <And>
      <Or>
        <Condition Name="FormClosingCondition" />
        <Condition Name="EscapeKeyDownCondition" />
      </Or>
      <Condition Name="FormChangedTrueEqualCondition" />
    </And>
  </ConditionExpression>
  <Commands>
    <Command Name="SaveOnCloseMessageBoxCommand" />
  </Commands>
</Execution>
```

{% endcode %}

### Обработка ответа пользователя на сообщение

Создадим условия, которые будут проверять, какую кнопку в диалоговом окне нажал пользователь. Команда MessageBoxCommand возвращает значение нажатой кнопки:&#x20;

* **Yes** - нажали кнопку "Да";
* **No** - нажали кнопку "Нет";
* **Cancel** - нажали кнопку "Отмена".

Достаточно создать два условия для кнопок "Да" и "Нет":

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

```xml
<Condition Name="SaveOnCloseMessageBoxCommandYesEqualCondition" Type="EqualCondition" Assembly="Conditions">
  <AlwaysChange Value="True" />
  <Items>
    <Item>
      <Command Name="SaveOnCloseMessageBoxCommand" />
    </Item>
    <Item>Yes</Item>
  </Items>
</Condition>

<Condition Name="SaveOnCloseMessageBoxCommandNoEqualCondition" Type="EqualCondition" Assembly="Conditions">
  <AlwaysChange Value="True" />
  <Items>
    <Item>
      <Command Name="SaveOnCloseMessageBoxCommand" />
    </Item>
    <Item>No</Item>
  </Items>
</Condition>
```

{% endcode %}

Создадим Execution для обработки нажатия кнопки "Да" диалогового окна:

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

```xml
<Execution>
  <ConditionExpression>
    <Condition Name="SaveOnCloseMessageBoxCommandYesEqualCondition" />
  </ConditionExpression>
  <Commands>
    <Command Name="SaveSequentialCommand" />
  </Commands>
</Execution>
```

{% endcode %}

А для обработки нажатия кнопки "Нет" диалогового окна создадим Execution, который помимо этого условия будет отрабатывать закрытие формы без изменений:

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

```xml
<Execution>
  <ConditionExpression>
    <Or>
      <And>
        <Or>
          <Condition Name="FormClosingCondition" />
          <Condition Name="EscapeKeyDownCondition" />
        </Or>
        <Not>
          <Condition Name="FormChangedTrueEqualCondition" />
        </Not>
      </And>
      <Condition Name="SaveOnCloseMessageBoxCommandNoEqualCondition" />
    </Or>
  </ConditionExpression>
  <Commands>
    <Command Name="FormCloseCommand" />
  </Commands>
</Execution>
```

{% endcode %}

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

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

## Обязательные поля

Так как сущность "Клиент" будет одной из основных в нашем приложении, и клиент, что логично, не может быть без имени, поле "Имя (ФИО)" необходимо сделать обязательным для заполнения. Также сделаем обязательным поле "Город", так как дальнейшая логика будет привязываться к городам клиентов.

Дополнительной гарантией корректности данных будут ограничения на колонки в таблице **template.client** в базе данных. Перейдем в программу для управления СУБД PostgreSQL, и для нашей базы данных **template\_project** выполним запросы, представленные ниже. Перед этим очистите таблицу от данных, либо заполните эти колонки во всех строках какой-либо информацией, чтобы не возникло ошибок с заданием ограничений на колонки.

```sql
ALTER TABLE template.client
   ALTER COLUMN city_id SET NOT NULL;
ALTER TABLE template.client
   ALTER COLUMN title SET NOT NULL;
```

### Проверка обязательных полей

Для проверки текстового поля **NameTextBox** понадобится условие типа [IsNullOrEmptyCondition](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/conditions/comparison_condition/is_null_or_empty_condition):

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

```xml
<Condition Name="NameTextBoxIsNullOrEmptyCondition" Type="IsNullOrEmptyCondition" Assembly="Conditions">
  <Items>
    <Item>
      <Object Name="NameTextBox"/>
    </Item>
  </Items>
</Condition>
```

{% endcode %}

А для проверки выпадающего списка создадим условие типа [IsNullCondition](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/conditions/comparison_condition/is_null_condition):

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

```xml
<Condition Name="CityComboBoxIsNullCondition" Type="IsNullCondition" Assembly="Conditions">
  <Items>
    <Item>
      <Object Name="CityComboBox"/>
    </Item>
  </Items>
</Condition>
```

{% endcode %}

Чтобы сообщить пользователю, что поля обязательны для заполнения, воспользуемся конструкцией [Checking](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/checkings). Для этого перед тэгом `<Executions>` разместим тэг `<Checkings>`, в который расположим следующий код:

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

```xml
<Checking>
  <Object Name="NameTextBox" />
  <ConditionExpression>
    <Condition Name="NameTextBoxIsNullOrEmptyCondition" />
  </ConditionExpression>
  <AsteriskHint>Пожалуйста, заполните это поле</AsteriskHint>
</Checking>

<Checking>
  <Object Name="CityComboBox" />
  <ConditionExpression>
    <Condition Name="CityComboBoxIsNullCondition" />
  </ConditionExpression>
  <AsteriskHint>Пожалуйста, выберите элемент в этом списке</AsteriskHint>
</Checking>
```

{% endcode %}

Назначение конструкции `<Checking>`в том, что при выполнении условия, описанного в тэге `<ConditionExpression>`, слева от объекта, указанного в тэге `<Object>`, будет отображаться красная полоска. При наведении курсора мыши на полоску будет всплывать подсказка с текстом из тэга `<AsteriskHint>`.

<figure><img src="https://3019442075-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_eBlWEU4C3o2GVEAAr%2Fuploads%2FxEDR24TkZhoHpP4vFcCJ%2FonClose-2.png?alt=media&amp;token=6b3c0741-1c43-4ef9-8775-5f31974f09ae" alt=""><figcaption></figcaption></figure>

### Предупреждение пользователя

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

Чтобы проверить, все ли поля заполнены, достаточно узнать, сработал ли хотя бы один Checking. Для этого у формы есть свойство [CheckingFired](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/form#get_checking_fired). Создадим [EqualCondition](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/conditions/comparison_condition/equal_condition), в котором проверим это свойство. И если оно равно False, то все поля корректно заполнены.

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

```xml
<Condition Name="MandatoryFieldsAreFilledEqualCondition" Type="EqualCondition" Assembly="Conditions">
  <Items>
    <Item>
      <Form>
        <Property Name="CheckingFired" />
      </Form>
    </Item>
    <Item>False</Item>
  </Items>
  <DataType Type="BooleanDataType" />
</Condition>
```

{% endcode %}

Это условие можно просто указать в тэге `<Enabled>` кнопки **SaveButton**, чтобы она стала неактивной. Так мы поступали с кнопками редактирования и удаления записей на формах списков клиентов и городов. Но такой подход не позволяет пользователю узнать, где он допустил ошибку, и почему кнопка неактивна.

Можно создать команду MessageBoxCommand, которая предупредит пользователя, если он попытается сохранить некорректные данные. Для этого в тэге `<Commands>` кнопки **SaveButton** нужно создать блок `<If>`, который определит, какая команда будет выполняться: либо команда сохранения данных, либо команда отображения сообщения о некорректном заполнении полей.

Но есть более изящный способ. У кнопки [Button](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/objects/button) есть режим [DisabledMode](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/objects/button#disabled_mode), который включается через тэг `<DisabledMode>`. Кнопка всегда остается активной, даже если **Enabled** равен **False**. При нажатии на "неактивную" кнопку (Enabled = False) будет показываться сообщение с текстом из тэга `<DisabledText>`.

Скорректируем описание кнопки "Сохранить" с использованием DisabledMode. Для этого в тэге `<Enabled>` пропишем условие **MandatoryFieldsAreFilledEqualCondition** и  добавим тэги `<DisabledMode>` и `<DisabledText>`.

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

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

```xml
<MyObject Name="SaveButton" Type="Button" Assembly="BaseControls">
  <Top>5</Top>
  <Right>
    <Formula>
      <Minus DataType="IntegerDataType">
        <Item>
          <Object Name="FootPanel">
            <Property Name="Width" />
          </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>
  <TabIndex>1</TabIndex>
  <Enabled>
    <Condition Name="MandatoryFieldsAreFilledEqualCondition" />
  </Enabled>
  <DisabledMode>True</DisabledMode>
  <DisabledText>Одно или несколько полей заполнены некорректно.</DisabledText>
  <Commands>
    <Command Name="SaveSequentialCommand" />
  </Commands>
</MyObject>
```

{% endcode %}

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

## Паттерн onClose и обязательные поля

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

<figure><img src="https://3019442075-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_eBlWEU4C3o2GVEAAr%2Fuploads%2FySfGnBlzx5lhWWTQtLgO%2FonClose-3.png?alt=media&amp;token=5c03b3ed-246b-408a-aae0-cd2cb433613e" alt=""><figcaption></figcaption></figure>

По кнопке "Да" мы будем закрыть форму без сохранения изменений. По кнопке "Нет" будем оставаться на форме и ждать дальнейших действий пользователя.&#x20;

Давайте опишем команду MessageBoxCommand, которая будет отображать это диалоговое окно:

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

```xml
<Command Name="CloseOnCloseMessageBoxCommand" Type="MessageBoxCommand" Assembly="Commands">
  <Caption>Закрытие</Caption>
  <Text>При закрытии все несохраненные изменения будут утеряны.\rВы уверены, что хотите закрыть форму?</Text>
  <Icon Type="Question" />
  <Buttons Type="YesNo" />
</Command>
```

{% endcode %}

Создадим условие проверки нажатия кнопки "Да" диалогового окна:

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

```xml
<Condition Name="CloseOnCloseMessageBoxCommandYesEqualCondition" Type="EqualCondition" Assembly="Conditions">
  <AlwaysChange Value="True" />
  <Items>
    <Item>
      <Command Name="CloseOnCloseMessageBoxCommand" />
    </Item>
    <Item>Yes</Item>
  </Items>
</Condition>
```

{% endcode %}

Скорректируем наш первый Execution следующим образом:

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

```xml
<Execution>
  <ConditionExpression>
    <And>
      <Or>
        <Condition Name="FormClosingCondition" />
        <Condition Name="EscapeKeyDownCondition" />
      </Or>
      <Condition Name="FormChangedTrueEqualCondition" />
    </And>
  </ConditionExpression>
  <Commands> 
    <If>
      <When>
        <Condition Name="MandatoryFieldsAreFilledEqualCondition" />
      </When>
      <Then>
        <Command Name="SaveOnCloseMessageBoxCommand" />
      </Then>
      <Else>
        <Command Name="CloseOnCloseMessageBoxCommand" />
      </Else>
    </If>
  </Commands>
</Execution>
```

{% endcode %}

А в Execution на закрытие формы добавим условие **CloseOnCloseMessageBoxCommandYesEqualCondition**:

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

```xml
<Execution>
  <ConditionExpression>
    <Or>
      <And>
        <Or>
          <Condition Name="FormClosingCondition" />
          <Condition Name="EscapeKeyDownCondition" />
        </Or>
        <Not>
          <Condition Name="FormChangedTrueEqualCondition" />
        </Not>
      </And>
      <Condition Name="SaveOnCloseMessageBoxCommandNoEqualCondition" />
      <Condition Name="CloseOnCloseMessageBoxCommandYesEqualCondition" />
    </Or>
  </ConditionExpression>
  <Commands>
    <Command Name="FormCloseCommand" />
  </Commands>
</Execution>
```

{% endcode %}

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

На этом мы закончили рассматривать паттерн onClose.

## Использование маски в текстовом поле

Давайте для поля "Контактный телефон" реализуем проверку формата введенного номера. Для этого в описание объекта **PhoneTextBox** добавим тэг `<Mask>` со значением **PHONE**. Таким образом, синтаксис текстового поля будет выглядеть так:

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

```xml
<MyObject Name="PhoneTextBox" Type="TextBox" Assembly="BaseControls">
  <Top>
    <Object Name="PhoneLabel">
      <Property Name="Bottom" />
    </Object>
  </Top>
  <Left>
    <Object Name="PhoneLabel">
      <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="PhoneTextBox">
            <Property Name="Left" />
          </Object>
        </Item>
      </Items>
    </Calculate>
  </Width>
  <Text>
    <DataConnection SourceDataConnection="ClientPrimaryGetDataConnection">
      <Fields>
        <Field Name="Phone" />
      </Fields>
    </DataConnection>
  </Text>
  <Mask>PHONE</Mask>
</MyObject>
```

{% endcode %}

Запустите приложение и откройте карточку клиента. Проверьте, как теперь отображается поле "Контактный телефон".

<figure><img src="https://3019442075-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-M_eBlWEU4C3o2GVEAAr%2Fuploads%2Fi8Hj10RjF3wNVetG2KC7%2Fimage.png?alt=media&amp;token=48d3b997-fc07-4f8e-ae6c-b49bdc679b4a" alt=""><figcaption></figcaption></figure>

Маска телефонного номера задает формат и ограничивает ввод символов в текстовое поле. Но нам нужно проверять, что пользователь ввел все необходимые данные. Для этого у объекта [TextBox](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/objects/textbox) есть свойство [MaskCompleted](https://wfsys.gitbook.io/workflow-forms-syntax/workflow_forms/objects/textbox#mask_completed). Создадим условие для проверки этого свойства:

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

```xml
<Condition Name="PhoneMaskNotCompletedCondition" Type="EqualCondition" Assembly="Conditions">
  <Items>
    <Item>
      <Object Name="PhoneTextBox">
        <Property Name="MaskCompleted" />
      </Object>
    </Item>
    <Item>False</Item>
  </Items>
  <DataType Type="BooleanDataType" />
</Condition>
```

{% endcode %}

Так как поле "Контактный телефон" не является обязательным для заполнения, то полноту введенных данных необходимо проверять только в том случае, если пользователь начал заполнять поле. Для этого создадим Condition, в котором будем проверять, пустое ли поле:

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

```xml
<Condition Name="PhoneIsEmptyCondition" Type="EqualCondition" Assembly="Conditions">
  <Items>
    <Item>
      <Object Name="PhoneTextBox" />
    </Item>
    <Item>+</Item>
  </Items>
</Condition>
```

{% endcode %}

Значение объекта **PhoneTextBox** сравниваем с символом "+", т.к. маска начинается с этого символа.

Создадим Checking для **PhoneTextBox**:

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

```markup
<Checking>
  <Object Name="PhoneTextBox" />
  <ConditionExpression>
    <And>
      <Not>
        <Condition Name="PhoneIsEmptyCondition" />
      </Not>
      <Condition Name="PhoneMaskNotCompletedCondition" />
    </And>
  </ConditionExpression>
  <AsteriskHint>Некорректный номер телефона</AsteriskHint>
</Checking>
```

{% endcode %}

Запустите приложение и откройте карточку клиента. Проверьте отображение сообщения об ошибке при заполнении текстового поля "Контактный телефон".

Давайте поправим один момент. Сейчас при сохранении информации о клиенте, если поле "Контактный телефон" оставили пустым, в базу запишется строка "+". Чтобы в таком случае в таблицу **template.client** в базе данных в колонку **phone** писалось значение **NULL**, необходимо в **ClientInsertSetDataConnection** и **ClientUpdateSetDataConnection** изменить описание параметра **Phone** на следующий код:

```markup
<Parameter NativeName="Phone">
  <Value>
    <Switch>
      <Case>
        <When>
          <Not>
            <Condition Name="PhoneIsEmptyCondition" />
          </Not>
        </When>
        <Then>
          <Object Name="PhoneTextBox" />
        </Then>
      </Case>
    </Switch>
  </Value>
</Parameter>
```

## Самостоятельно <a href="#self-work" id="self-work"></a>

* На форме списка клиентов (TemplateStart.xml) на кнопки редактирования и удаления записей добавьте режим DisabledMode;
* На форме списка городов (TemplateCityList.xml) на кнопки редактирования и удаления записей добавьте режим DisabledMode;
* На форме карточки города (TemplateCityEdit.xml) поле "Наименование" сделайте обязательным для заполнения. Не забудьте про ограничение на колонку в таблице в базе данных;
* На форме карточки города (TemplateCityEdit.xml) на кнопку "Сохранить" добавьте режим DisabledMode;
* На форме карточки города (TemplateCityEdit.xml) реализуйте паттерн onClose;
* На форме списка городов (TemplateCityList.xml) реализуйте паттерн onClose.

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

На уроке мы рассмотрели паттерн onClose, суть которого заключается в том, чтобы определить, были ли изменения на форме, и предупредить пользователя, если он захочет закрыть форму без сохранения.&#x20;

Также мы узнали, как можно уведомить пользователя об обязательных полях, и как такие поля повлияют на использование паттерна onClose.

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

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

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