Поведение при подписке на события

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

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

sample

Логика работы приложения выглядит следующим образом. При выборе элемента из списка картинка предпросмотра обновляется на произвольную из выбранного альбома. Согласитесь, что все очень просто, так?

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

Код в реальности выглядит вот так. Т.е. это то, как вы его напишите с первого раза. Собственно я написал так же.

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

Итак, что вы ожидаете от такого кода? Посмотрите внимательно на названия методов, событий – решите для себя, что вы ожидаете.

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

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

Это очень странно на первый взгляд, так как я подписываюсь на событие и ожидаю, что его получат все заинтересованные компоненты, и обработка не прервется на самом интересном месте. Для того, чтобы сработало передвижение на новый элемент из listBox, необходимо дописать IsHandled = false:

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

На мой взгляд надо сделать два события:

  • ArrowNavigationCommandReceived – нет возможность использовать IsHandled для прерывания цепочки обработки.
  • ArrowNavigationCommandReceiving – есть возможность использовать IsHandled.

Ближайшая аналогия прослеживается с событиями Closed и Closing для окон (WinForm, WPF).

Что вы думаете по этому поводу? Комментарии как всегда приветствуются.

UPD: голосование пока что запилить не могу.

[poll id=»2″]

Hard’n’Heavy!

5 комментариев на “Поведение при подписке на события

  1. использование Received и Receiving — подход стандартный, его и стоит лучше использовать. По крайней мере, он вписывается в концепцию и не требует дополнительного изучения.
    Но мне в целом не нравится установка IsHandled = false. Лучше уж явно затребовать остановку обработки, если это необходимо.

  2. Сдается мне что вызов ArrowNavigationCommandReceived перед тем как случилось событие, а не после этого это не самая страшная беда. Разумеется это должен быть ArrowNavigationCommandReceiving.

    Важнее что IsHandled = true либо по умолчанию, либо выставляется таким при обработке первого обработчика события. И то и другое концептуальная ошибка. Должно быть значение по умолчанию false. И выставляться только по желанию того, кто осуществляет подписку на событие.

    • Да, IsHandled = true по умолчанию — гораздо большим косяком мне видится тоже. Но тем не менее событие было бы полезно запилить тоже.

Оставить комментарий