Закрытие окна через usercontrol

89
05 января 2021, 20:30

Собственно вопрос. Были мысли сделать подобное через relative source, но как это передать в код - не знаю. Желательно с минимальным количеством кода, выходящего за пределы UserControl'a

Answer 1

Вопрос все равно туманный, поэтому я пройдусь по верхам.

Инициатор закрытия: ViewModel

ViewModel не знает про вид, а знает только о себе (ну и о инфраструктуре конечно) и потому может сообщить о необходимости "закрыть" только косвенно:

  1. Завести обычное событие RequestClose, на который подпишется вид.
  2. Завести обычное INPC свойство, которое сможет использовать вид. Например bool DialogResult {get;set;}. (я предпочитаю это, особенно для диалоговых окон)
  3. Да хоть через Messenger (инфраструктура) событие послать (упомянуто "до кучи")

Инициатор закрытия: вид

Можно получить текущее окно из контрола путем Window.GetWindow(this) и вызвать у него Close() и будет эффект, как от кнопки закрытия окна со всеми вытекающими.

Вид знает о ViewModel и потому варианты "кто кого вызывает и опрашивает" ограничивает лишь фантазия. Я лучше приведу несколько примеров.

Окно с процессом операции. Закрытие запрещено

Определяем в окне хендлер события OnClosing и проверяя свойство вида vm.IsBusy отменяем закрытие окна e.handled = true

Окно с процессом операции. Остановить и закрыть

Обычно есть кнопка "Стоп". Биндим ее на команду vm CancelCommand, которая прервет операцию и по результату вызовет RequestClose, и окно закроется. Попутно полезно определить свойство IsStopping и отслеживая его через тригеры менять контент кнопки на "Останавливаемся..."

С кнопкой закрытия окна сложнее. Она вызваем OnClosing. В OnClosing достаем vm и вызываем у него какой нибудь vm.Cancel(). ViewModel прерывает операцию и вызывает RequestClose, на которую подписан вид. Вид получив уведомление от RequestClose закрывает себя Close() - и тут снова вызывается OnClosing (получается зацикливание). Поэтому OnClosing должен проверять флаг вьюмодели IsBusy, что никаких операций не происходит и можно закрыть. (Также можно получить deadlock, который придется разруливать через Task.Yield() - хоть где то он пригодился) Ну и конечно не забыть где нибудь написать "Останавливаемся..." если это не мгновенно.

Обычный диалог с формой и 2 кнопки: "Сохранить" и "Отмена"

Кнопку "Сохранить" биндим на команду SaveCommand. ViewModel при успешном результате вызывает RequestClose и окно закрывается. При неуспешном (например валидация) не вызывает.

Кнопку "Отмена" биндим на команду CancelCommand(CloseCommand), которая просто вызывает RequestClose и окно закрывается

Кнопки закрытия окна по хорошему тут быть не должно, но если есть, то она либо не вредит, если вся работа выполняется в UI, либо мудрить весь огород с OnClosing/IsBusy

ps: Не стоит забывать, что биндинг окна на DialogResult, работу с кнопкой "закрытия окна", Window.GetWindow(this).Close() - все это можно скрыть за набором кастомных attached property и code-behind будет пуст

ps2: Если происходит асинхронная работа, то не забывать про защиту от повторного клика на кнопку/контрол или несвоевременного клика на кнопку "закрытия окна"

READ ALSO
Интеграция ASP.NET Core и MySQL

Интеграция ASP.NET Core и MySQL

Друзья, возможно кто-нибудь интегрировал ASPNET Core сервер и БД MySQL

106
Распаковка файлов с помощью crush32.dll

Распаковка файлов с помощью crush32.dll

У меня есть техническая документация, хранящаяся в файлах *bli, *

121
Ошибка при передаче sql параметра

Ошибка при передаче sql параметра

Есть код, в котором должен передаться параметр stopWord

144