Общие сведения о событиях и перенаправленных событиях

Важные API

В этом разделе описана концепция программирования событий в приложении среды выполнения Windows при использовании расширений компонентов C#, Visual Basic или Visual C++ (C++/CX) в качестве языка программирования и XAML для определения пользовательского интерфейса. Обработчики событий можно назначить при объявлении элементов пользовательского интерфейса в XAML. Можно также добавить обработчики в код. Среда выполнения Windows поддерживает перенаправленные события: отдельные события ввода и события данных могут обрабатываться не теми объектами, которые вызвали эти события. Перенаправленные события удобны при определении шаблонов элементов управления или использовании страниц либо контейнеров макета.

События как концепция программирования

В общем случае концепции событий при программировании приложения среды выполнения Windows подобны модели событий в самых популярных языках программирования. Если вы уже умеете работать с событиями Microsoft .NET или C++, вам будет проще. Но для того, чтобы выполнять такие простые задачи, как присоединение обработчиков, глубоких знаний о концепциях модели событий не требуется.

Если в качестве языка программирования вы используете C#, Visual Basic или C++/CX, пользовательский интерфейс определяется при помощи разметки (XAML). В синтаксисе разметки XAML некоторые принципы присоединения событий, возникших в элементах разметки, к сущностям в коде среды выполнения аналогичны другим веб-технологиям, например ASP.NET или HTML5.

Заметка Код, предоставляющий логику среды выполнения для определяемого XAML пользовательского интерфейса, часто называется кодом программной части или файлом программной части. В представлениях решений Microsoft Visual Studio это отношение изображено графически: файл кода программной части является зависимым и вложенным файлом в отличие от страницы XAML, на которую он ссылается.

Button.Click: введение в события и XAML

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

Интерфейс для приложения среды выполнения Windows определяется созданием кода XAML. Этот код XAML обычно выдается поверхностью разработки в Visual Studio. Вы также можете написать код XAML в простом текстовом редакторе или стороннем редакторе XAML. При создании кода XAML вы можете подключить к отдельным элементам пользовательского интерфейса обработчики событий одновременно с определением всех остальных атрибутов XAML, которые устанавливают значения свойств этих элементов интерфейса.

Для подключения событий в XAML укажите в форме строки имя метода обработчика, который уже определен или будет определен позже в коде программной части. Например, следующий код XAML определяет объект Button с некоторыми свойствами (атрибут x:Name, Content), присвоенными в виде атрибутов, и подключает обработчик для события Click кнопки при помощи ссылки на метод, именуемый ShowUpdatesButton_Click:

<Button x:Name="showUpdatesButton"
  Content="{Binding ShowUpdatesText}"
  Click="ShowUpdatesButton_Click"/>

Совет.Подключение событий — это термин из области программирования. Он относится к процессу или коду, при помощи которого вы обозначаете, что события должны вызывать метод обработчика с заданным именем. В большинстве моделей кода процедур подключение событий представляет собой явный или неявный код "AddHandler", задающий имя событию и методу и обычно использующий экземпляр конечного объекта. В языке XAML код "AddHandler" неявный, а подключение событий состоит исключительно в том, что событию присваивается имя, являющееся именем атрибута элемента объекта, а обработчику — имя, являющееся значением этого атрибута.

Вы создаете собственно обработчик событий на языке программирования, который вы используете для всего кода приложения и кода программной части. С помощью атрибута Click="ShowUpdatesButton_Click" вы создаете контракт, чтобы при компиляции и анализе XAML-документа как компиляция разметки XAML при операции сборки IDE, так и последующая операция анализа файла XAML при загрузке приложения могли найти метод с именем ShowUpdatesButton_Click. ShowUpdatesButton_Click должен быть методом, реализующим совместимую сигнатуру метода (на основе делегата) для любого обработчика события Click . Например, следующий код определяет обработчик ShowUpdatesButton_Click.

private void ShowUpdatesButton_Click (object sender, RoutedEventArgs e) 
{
    Button b = sender as Button;
    //more logic to do here...
}
Private Sub ShowUpdatesButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
    Dim b As Button = CType(sender, Button)
    '  more logic to do here...
End Sub
void winrt::MyNamespace::implementation::BlankPage::ShowUpdatesButton_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e)
{
    auto b{ sender.as<Windows::UI::Xaml::Controls::Button>() };
    // More logic to do here.
}
void MyNamespace::BlankPage::ShowUpdatesButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) 
{
    Button^ b = (Button^) sender;
    //more logic to do here...
}

В этом примере метод ShowUpdatesButton_Click основан на делегате RoutedEventHandler. Вы знаете, что это делегат для использования, так как вы увидите этот делегат в синтаксисе метода Click .

Кончик Visual Studio предоставляет удобный способ присвоить имя обработчику событий и определить метод обработчика при редактировании XAML. Когда вы указываете имя атрибута для события в текстовом редакторе XAML, дождитесь отображения списка Microsoft IntelliSense. Если щелкнуть <новый обработчик> событий из списка, Microsoft Visual Studio предложит имя метода на основе x:Name элемента (или имени типа), имени события и числового суффикса. Затем вы можете щелкнуть правой кнопкой мыши имя выбранного обработчика событий и выбрать Перейти к обработчику событий. При этом осуществляется переход прямо к вставленному определению обработчика событий, как показано в представлении вашего файла кода программной части для страницы XAML в редакторе кода. Обработчик событий уже имеет правильную подпись, включая параметр sender и класс данных события, который использует событие. Кроме того, если метод обработчика с правильной сигнатурой уже существует в коде программной части, имя этого метода отображается в раскрывающемся списке автоматического завершения вместе с параметром <"Новый обработчик> событий". Вы также можете нажать клавишу TAB для быстрого вызова вместо того, чтобы щелкать элементы списка IntelliSense.

Определение обработчика событий

Для объектов, принадлежащих элементам пользовательского интерфейса и объявленных в XAML, код обработчика события определен в разделяемом классе, который служит кодом программной части для XAML-страницы. Обработчики событий — это методы, которые вы создаете как часть разделяемого класса, который связан с вашим XAML. Эти обработчики событий основаны на делегатах, которые используются конкретным событием. Методы вашего обработчика событий могут быть общими или частными. Закрытый доступ действует, потому что обработчик и экземпляр, созданные XAML, окончательно соединяются в процессе создания кода. В целом мы рекомендуем делать методы обработчиков событий в классе частными.

Заметка Обработчики событий для C++ не определяются в разделяемых классах, они объявляются в заголовке как член частного класса. Действия при сборке для проекта C++ обеспечивают создание кода, поддерживающего систему типов XAML и модели кода программной части для C++.

Параметр sender и данные события

Обработчик, созданный вами для события, может обращаться к двум значениям, которые доступны как вводные при каждом вызове обработчика. Первое значение — это sender, представляющий собой ссылку на объект, к которому прикреплен обработчик. Параметр sender типизирован как базовый тип Object. Часто используется такой прием, как преобразование sender в тип с большей точностью. Этот прием полезен, если предполагаются проверки или изменения состояния самого объекта sender. Исходя из проекта приложения вы выбираете тип, в который можно безопасно преобразовать sender, учитывая участок прикрепления обработчика или другую специфику проекта.

Второе значение — это данные события, которые обычно включаются в определения синтаксиса как параметр e. Изучив параметр e делегата, который сопоставлен определенному обрабатываемому событию, можно выяснить, какие свойства доступны для данных события, а затем воспользоваться функцией IntelliSense или обозревателем объектов в Visual Studio. Можно также использовать справочную документацию среды выполнения Windows.

Для некоторых событий значения определенных свойств данных события не менее важны, чем сам факт возникновения события. Это особенно верно для событий ввода. Для событий указателя может быть важна позиция указателя в момент возникновения события. Для событий клавиатуры все возможные нажатия клавиш вызывают события KeyDown и KeyUp. Чтобы определить, какую клавишу нажимает пользователь, необходимо обратиться к KeyRoutedEventArgs, который доступен для обработчика событий. Дополнительную информацию об обработке событий ввода см. в разделах Взаимодействие с помощью клавиатуры и Работа с данными указателя. События и сценарии ввода часто имеют дополнительные особенности, которые в данном разделе не рассматриваются — например, захват указателя для событий указателя или клавиши-модификаторы и коды клавиш определенной платформы для событий клавиатуры.

Обработчики событий, использующие шаблон async

В некоторых случаях вам может потребоваться использовать API, которые используют шаблон async внутри обработчика событий. Например, вы можете использовать Button в AppBar для отображения средства выбора файлов и взаимодействия с ним. Однако следует помнить, что многие API средств выбора файлов являются асинхронными. Их нужно вызывать в области async/awaitable, при этом компилятор применит их. Попробуйте сделать следующее: добавьте ключевое слово async к обработчику событий, при этом обработчик становится asyncvoid. Теперь обработчику событий разрешено выполнять вызовы async/awaitable.

Пример обработки событий взаимодействия с пользователем с помощью шаблона async см. в статье Доступ к файлам и средства выбора файлов (входит в цикл Создание первого приложения среды выполнения Windows на C# или Visual Basic). См. также [Вызов асинхронных API в C].

Добавление обработчиков событий в программный код

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

В C# синтаксис предусматривает использование оператора +=. Для регистрации обработчика справа от оператора добавляется ссылка на имя метода обработчика событий.

Если для добавления обработчиков событий к объектам, которые отображаются в пользовательском интерфейсе среды выполнения, используется код, обычно такие обработчики добавляют в ответ на событие жизненного цикла объекта или обратный вызов, например Loaded или OnApplyTemplate, чтобы обработчики событий в соответствующих объектах были готовы к инициируемым пользователем событиям во время выполнения. Данный пример показывает каркас XAML для структуры страницы и затем предоставляет синтаксис языка C# для добавления к объекту обработчика событий.

<Grid x:Name="LayoutRoot" Loaded="LayoutRoot_Loaded">
  <StackPanel>
    <TextBlock Name="textBlock1">Put the pointer over this text</TextBlock>
...
  </StackPanel>
</Grid>
void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
{
    textBlock1.PointerEntered += textBlock1_PointerEntered;
    textBlock1.PointerExited += textBlock1_PointerExited;
}

Заметка Существует более подробный синтаксис. В 2005 году в язык программирования C# была добавлена концепция вывода делегата, которая позволяет компилятору выводить новый экземпляр делегата и использовать более ранний простой синтаксис. По функциональности подробный синтаксис идентичен приведенному в предыдущем примере, но явно создает новый экземпляр делегата до его регистрации, не пользуясь, таким образом, преимуществами вывода делегатов. Этот подробный синтаксис не получил широкого распространения, но он все же представлен в некоторых примерах кода.

void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
{
    textBlock1.PointerEntered += new PointerEventHandler(textBlock1_PointerEntered);
    textBlock1.PointerExited += new MouseEventHandler(textBlock1_PointerExited);
}

Синтаксис Visual Basic предусматривает две возможности добавления обработчика. Первая аналогична синтаксису C# и прикрепляет обработчики непосредственно к экземплярам. Для этого используется ключевое слово AddHandler и оператор AddressOf, который разыменовывает метод обработчика события.

Второй вариант синтаксиса Visual Basic заключается в использовании ключевого слова Handles в обработчиках событий. Этот способ подходит для случаев, когда предполагается существование обработчиков в объектах во время загрузки и на протяжении всего жизненного цикла объекта. Для использования Handles в объекте, определенном в XAML, необходимо указать Name / x:Name. Это имя становится квалификатором экземпляра, который необходим для части Instance.Event синтаксиса Handles. В этом случае для того, чтобы прикрепить другие обработчики событий, нам не нужен обработчик событий, рассчитанный на жизненный цикл объекта; подключения Handles создаются при компиляции XAML-страницы.

Private Sub textBlock1_PointerEntered(ByVal sender As Object, ByVal e As PointerRoutedEventArgs) Handles textBlock1.PointerEntered
' ...
End Sub

Заметка Visual Studio и ее область конструктора XAML обычно повышают метод обработки экземпляров вместо ключевого слова Handles . Причина заключается в том, что установка подключения обработчика событий в XAML является частью стандартного рабочего процесса дизайнер-разработчик, а подход с использованием ключевого слова Handles несовместим с подключением обработчиков событий в XAML.

В C++/CX также используется += синтаксис, но существуют отличия от базовой формы C#:

  • Вывод делегатов не поддерживается, поэтому для экземпляра делегата необходимо использовать ref new.
  • Конструктор делегата имеет два параметра и требует в качестве первого параметра целевой объект. Обычно нужно определить this.
  • Конструктору делегата в качестве второго параметра необходим адрес метода, поэтому перед именем метода указан оператор ссылки &.
textBlock1().PointerEntered({this, &MainPage::TextBlock1_PointerEntered });
textBlock1->PointerEntered += 
ref new PointerEventHandler(this, &BlankPage::textBlock1_PointerEntered);

Удаление обработчиков событий из кода

Даже если вы добавили обработчики событий в код, то их не всегда требуется удалять. В соответствии с логикой времени жизни для большинства объектов среды выполнения Windows, например страниц и элементов управления, объекты уничтожаются при их отделении от основного объекта Window и его визуального дерева. При этом уничтожаются и ссылки делегата. В среде .NET объекты уничтожаются во время сборки мусора, а в среде выполнения Windows с компонентами расширения Visual C++ (C++/CX) по умолчанию используются слабые ссылки.

В некоторых случаях требуется явное удаление обработчиков событий. К ним относятся следующие объекты.

  • Обработчики статических событий, которые невозможно удалить обычным способом. Примеры статических событий в API среды выполнения Windows — события классов CompositionTarget и Clipboard.
  • Проверяйте код, если синхронизация удаления обработчиков должна быть незамедлительной, а также в тех случаях, когда вы переключаете старый обработчик события на новый во время выполнения.
  • Реализация пользовательского метода доступа remove.
  • Пользовательские статические события.
  • Обработчики для перемещения по страницам.

Триггеры событий FrameworkElement.Unloaded или Page.NavigatedFrom имеют подходящие позиции в управлении состоянием и время жизни объекта. Вы можете использовать их для удаления обработчиков других событий.

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

textBlock1.PointerEntered -= textBlock1_PointerEntered;
RemoveHandler textBlock1.PointerEntered, AddressOf textBlock1_PointerEntered

Также вы можете удалять обработчики, если событие было добавлено через атрибут XAML, что означает добавление обработчика в сформированный код. Если у вас есть значение Name для элемента, для которого был прикреплен обработчик, то удалить его будет проще, так как в коде создается ссылка на объект. Если объект не имеет Name, возможен вариант обхода дерева объектов для обнаружения ссылки на объект.

Если требуется удалить обработчик событий в C++/CX, то вам необходим токен регистрации, который может быть получен из возвращаемого значения регистрации обработчика событий +=. В синтаксисе C++/CX значение, используемое в правой стороне отмены регистрации -=, является токеном, а не названием метода. В C++/CX невозможно удалить обработчики, добавленные через атрибут XAML, так как в сформированном коде C++ токены не сохраняются.

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

Среда выполнения Windows c C#, Microsoft Visual Basic или C++/CX поддерживает концепцию перенаправленных событий для набора событий, которые имеются в большинстве элементов пользовательского интерфейса. Эти события выполняются для сценариев ввода и взаимодействия с пользователем и реализованы в базовом классе UIElement. Список событий ввода, которые являются перенаправленными:

Перенаправленное событие — это событие, которое можно передать (перенаправить) от дочернего объекта к каждому из его родительских объектов в последовательности дерева объектов. Это дерево приблизительно соответствует XAML-структуре пользовательского интерфейса, причем корневой элемент дерева является корневым элементом в XAML. Реальное дерево объектов может отличаться от элементов вложений XAML, так как это дерево объектов не содержит средств языка XAML, например тегов элементов свойств. Перенаправленные события можно представить как восходящую маршрутизацию событий от дочерних элементов объекта XAML, которые вызывают событие, к родительскому элементу объекта, который их содержит. Событие и его данные могут обрабатываться несколькими объектами по маршруту события. Если элементы не имеют обработчиков, маршрут потенциально длится до достижения корневого элемента.

Если у вас есть опыт использования веб-технологий, например Dynamic HTML (DHTML) или HTML5, возможно, концепция восходящей маршрутизации событий вам уже знакома.

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

Свойство OriginalSource элемента RoutedEventArgs

Когда событие передается вверх по маршруту события, объект, в котором возникло событие, перестает быть sender. Вместо этого sender становится объект, к которому прикреплен вызываемый в данный момент обработчик.

В некоторых случаях вас может интересовать не sender, а информация о том, к какому из возможных дочерних объектов подведен указатель при запуске события указателя или какой объект пользовательского интерфейса оказывается в фокусе, когда пользователь нажимает клавишу на клавиатуре. В таких случаях используется значение свойства OriginalSource. В каждой точке маршрута OriginalSource сообщает информацию о первоначальном объекте, в котором возникло событие, а не об объекте, к которому прикреплен его обработчик. Но для событий ввода UIElement этот первоначальный объект часто оказывается объектом, который не виден на уровне страницы определения пользовательского интерфейса XAML. Такой исходный объект может являться шаблонной частью элемента управления. Например, если пользователь подводит указатель мыши к самому краю Button, для большинства событий указателя OriginalSource является частью шаблона Border в Template, а не самой Button.

Кончик Если вы создаете шаблонный элемент управления, это особенно полезно для событий ввода. Любому элементу управления, использующему шаблон, пользователь может назначить новый шаблон. При попытке повторно создать рабочий шаблон пользователь может непреднамеренно исключить некоторые обработки событий, объявленные в шаблоне по умолчанию. Тем не менее можно обеспечить обработку событий на уровне элемента управления, прикрепив обработчики в процессе переопределения OnApplyTemplate в определении класса. Далее можно перехватывать события ввода, следующие вверх по маршруту к корню элемента управления, в момент создания экземпляра.

Свойство Handled

Некоторые классы данных события для определенных перенаправленных событий содержат свойство Handled. Например, PointerRoutedEventArgs.Handled, KeyRoutedEventArgs.Handled, DragEventArgs.Handled. Во всех случаях Handled — это настраиваемое свойство логического типа.

Если задать для свойства Handled значение true, это повлияет на поведение системы событий. Когда значение свойства Handled равно true, для большинства обработчиков событий перенаправление прекращается. Событие не передается дальше по маршруту и не уведомляет остальные прикрепленные обработчики об этом конкретном случае события. Какое событие считается "обработанным" и как приложение отвечает на обрабатываемое событие, решать вам. По сути Handled представляет собой простой протокол, который позволяет коду приложения заявлять, что событию не требуется передаваться любым контейнерам. Логика приложения решает, какие действия должны быть выполнены. И наоборот, необходимо тщательно следить за тем, чтобы не обрабатывать события, которые, скорее всего, должны передаваться, чтобы можно было выполнить поведение встроенной системы или элемента управления. Так, может оказаться нежелательной обработка событий низкого уровня в частях или элементах управления выбором. Элемент управления выбором может выполнять поиск событий ввода для изменения выбора.

Не для всех перенаправляемых событий можно отменить маршрут указанным способом. Такие события можно распознать, так как у них отсутствует свойство Handled. Например, события GotFocus и LostFocus передаются, но они всегда полностью проходят весь маршрут до корневого элемента, а их классы данных события не имеют свойства Handled, которое могло бы изменить это поведение.

Обработчики событий ввода в элементах управления

Специальные элементы управления среды выполнения Windows иногда неявным образом используют концепцию Handled для событий ввода. Может показаться, что событие ввода никогда не происходит, потому что ваш пользовательский код не может обработать его. Например, класс Button содержит логику, при помощи которой специально обрабатываются обычные события ввода PointerPressed. Это сделано потому, что кнопки порождают событие Click, которое инициируется вводом при помощи мыши, а также другими режимами ввода, например обработкой таких клавиш, как ВВОД, которые могут вызвать срабатывание кнопки, когда она находится в фокусе. В рамках проектирования классов Button необработанное событие ввода концептуально обрабатывается, и потребители класса — например, ваш пользовательский код, — могут вместо этого взаимодействовать с соответствующим элементу управления событием Click. В разделах, посвященных классам элементов управления в API среды выполнения Windows, часто описывается поведение обработки событий, которое реализуется классом. В некоторых случаях поведение можно изменить, переопределив методы OnEvent . Например, вы можете изменить реакцию производного класса TextBox на ввод с клавиатуры, переопределив Control.OnKeyDown.

Регистрация обработчиков для уже обработанных перенаправленных событий

Ранее говорилось, что установка значения Handled для параметра true отменяет вызов большинства обработчиков. Но метод AddHandler предоставляет способ, позволяющий прикрепить обработчик, который всегда вызывается для маршрута, даже если другие обработчики на предыдущих этапах маршрута установили значение true для Handled в общих данных события. Этот способ полезен в том случае, если используемый элемент управления уже обработал событие внутренним образом или вследствие особой логики элемента управления, но вы тем не менее хотите ответить на это событие в экземпляре элемента управления или выше по маршруту. Используйте этот способ с осторожностью, потому что он может вступить в противоречие с назначением Handled и, возможно, нарушить механизм взаимодействия элемента управления.

Способ обработки событий при помощи AddHandler можно использовать только для перенаправленных событий, имеющих соответствующий идентификатор перенаправленного события, так как этот идентификатор является обязательным входным значением для метода AddHandler. Список событий, имеющих идентификатор перенаправленного события, можно найти в справочной документации по AddHandler. Этот список событий почти совпадает со списком, представленным ранее. Исключение составляют события GotFocus и LostFocus, у которых нет идентификатора перенаправленных событий, поэтому для них нельзя применить AddHandler.

Перенаправленные события вне визуального дерева

Некоторые объекты являются участниками отношения с главным визуальным деревом, что концептуально равносильно существованию наложений поверх основных визуальных элементов. Такие объекты не являются частью обычных родительско-дочерних отношений, соединяющих все элементы дерева с визуальным корнем. Это справедливо для любых отображаемых Popup или ToolTip. Если вам нужно обрабатывать перенаправленные события от Popup или ToolTip, разместите обработчики в конкретных элементах пользовательского интерфейса, размещенных в Popup или ToolTip, а не в самих элементах Popup или ToolTip. Не полагайтесь на перенаправление внутри любой компоновки, которая выполняется для содержимого Popup или ToolTip, так как маршрутизация событий для перенаправленных событий работает только вдоль главного визуального дерева. Popup и ToolTip не рассматриваются как родительские или дочерние элементы пользовательского интерфейса и никогда не получают перенаправленное событие, даже если попытаться использовать что-нибудь вроде фона по умолчанию Popup в качестве области захвата для события ввода.

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

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

  • Для его свойства Visibility установлено значение Visible.
  • Значение свойства элемента Background или Fill не равно null. Значение null свойства Brush означает, что элемент прозрачен и проверка нажатия не может быть выполнена. (Чтобы сделать элемент прозрачным, но доступным для проверки нажатия, используйте значение свойства кисти Transparent, а не null.)

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

  • Если элемент является элементом управления, для его свойства IsEnabled должно быть установлено значение true.
  • У элемента в макете должны быть фактические размеры. Элемент, ActualHeight и ActualWidth которого равны 0, не вызывает события ввода.

Некоторые элементы управления имеют особые правила для проверки нажатия. Например, TextBlock не обладает свойством Background, но доступен для проверки нажатия в пределах границ его площади. Image и MediaElement доступны для проверки нажатия в своих определенных прямоугольных формах независимо от прозрачного содержимого, как, например, альфа-канал в отображаемом исходном файле мультимедиа. Элементы управления WebView располагают специальным поведением проверки нажатия, поскольку ввод может обрабатываться размещенным HTML и вызывать события сценария.

Большинство классов Panel и Border недоступны для проверки нажатия в фоновом режиме, но могут обрабатывать события ввода, перенаправляемые от элементов, которые они содержат.

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

Система команд

Немногие элементы пользовательского интерфейса поддерживают командный интерфейс. Командный интерфейс использует относящиеся к вводу перенаправленные события в своей базовой реализации и обеспечивает обработку относящегося к пользовательскому интерфейсу ввода (конкретное действие указателя, конкретное сочетание клавиш) вызовом единого обработчика команд. Если для элемента пользовательского интерфейса доступен командный интерфейс, обдумайте использование соответствующих API командного интерфейса вместо любых дискретных событий ввода. Обычно используется ссылка Binding на свойства класса, который определяет модель просмотра данных. Свойства содержат именованные команды, которые реализуют языковой шаблон команд ICommand. Подробнее: ButtonBase.Command.

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

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

  • Для языков C# и Visual Basic вы определяете событие среды CLR. Вы можете использовать стандартный шаблон событий .NET, если вы не используете пользовательские методы доступа (добавить/удаление). Дополнительные советы.
  • Для C++/CX см. События в C++/CX.
    • Присваивайте имена ссылкам даже при использовании собственных пользовательских событий. Не используйте лямбда-выражения для пользовательских событий — может возникнуть циклическая ссылка.

Невозможно объявить пользовательское перенаправленное событие для среды выполнения Windows. Перенаправленные события ограничены набором из среды выполнения Windows.

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