Por que o Button Click Event “Bubble Up Visual Tree” para Stackpanel, como afirma o artigo do MSDN?
-
20-08-2019 - |
Pergunta
No artigo do MSDN Entendendo eventos e comandos roteados no WPF, ele afirma
Um evento borbulhará (propagar) a árvore visual do elemento de origem até que seja manipulada ou atinge o elemento raiz.
No entanto, neste exemplo, quando você clica no botão, Não "borbulham a árvore visual" para ser tratado pelo evento Stackpanel dos pais, ou seja, clicando no botão não dispara nenhum evento.
Por que não? O que eles significam então por "borbulhar" se não isso?
Xaml:
<Window x:Class="TestClickEvents456.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<StackPanel x:Name="TheStackPanel"
Background="Yellow"
MouseDown="TheStackPanel_MouseDown">
<Button x:Name="TheButton"
Margin="10"
Content="Click This"/>
<TextBlock x:Name="TheMessage"
Text="Click the button or the yellow area"/>
</StackPanel>
</Window>
Código por trás:
using System.Windows;
using System.Windows.Input;
namespace TestClickEvents456
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void TheStackPanel_MouseDown(object sender, MouseButtonEventArgs e)
{
TheMessage.Text = "StackPanel was clicked.";
}
}
}
Solução
O evento borbulha, até que seja tratado ...
Como o botão faz algo com o mouse clica, ele absorve o evento do mouse e o transforma em um clickevent.
Se você usar o visualizador, verá que o Stackpanel recebe o evento antes do botão.
Outras dicas
Como outros disseram, é porque o MouseDown
evento é tratado pelo Button
Antes que possa ser borbulhado ainda mais. Você pode ver isso no refletor, em ButtonBase.OnMouseLeftButtonDown
:
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
if (this.ClickMode != ClickMode.Hover)
{
e.Handled = true;
// SNIP...
}
base.OnMouseLeftButtonDown(e);
}
Uma solução é ouvir para um MouseDown
evento e indicar que você não se importa se o evento for tratado. Você pode fazer isso com o AddHandler
método. Possui uma sobrecarga booleana que permite ouvir eventos que já são tratados.
Se você fizer isso em algum lugar em vez de definir o manipulador de Mousedown em Xaml:
TheStackPanel.AddHandler(MouseDownEvent, new MouseButtonEventHandler(TheStackPanel_MouseDown), true);
Você receberá tudo MouseDown
eventos em TheStackPanel
, independentemente de terem sido tratados.
Além disso, se você deseja que o Stackpanel receba o evento, altere o Stackpanel Xaml para:
<StackPanel x:Name="TheStackPanel"
Background="Yellow"
Button.Click="TheStackPanel_MouseDown" />
e a assinatura do evento para:
private void TheStackPanel_MouseDown(object sender, RoutedEventArgs e)
Nesse caso, o Stackpanel receberá o evento de clique do botão. No entanto, clicar no próprio StackPanel não demitirá nenhum evento, pois ele ouve especificamente para um botão clicar.
É porque todas as mensagens estão sendo capturadas manuseado por botão e mensagens param a mensagem para borbulhando lá. A resposta está certa no texto da sua pergunta:
Um evento borbulhará (propagar) a árvore visual do elemento de origem Até que tenha sido tratado ou atinge o elemento raiz.
EDITAR:
Edward Tanguay (OP) comentou sobre esta resposta e estou copiando o comentário dele aqui porque é muito relevante:
"Não vejo que o botão esteja lidando com o evento, ou seja, não tenho manipulador de cliques no botão, eu tenho um manipulador de cliques (Mousedown) no Stackpanel e, portanto, acho O botão não lida e é tratado pelo Stackpanel, o que faz, certo? "
Você está certo. O botão não está lidando com o evento Mousedown porque nenhum manipulador foi especificado para ele nesse controle.
Mas, então, Mousedown é particular de alguma forma. Pelo menos nos formulários do Windows, é usado para iniciar ações como desenho e arrasto, quando um controle obtém o evento, ele passa a prender todas as mensagens subsequentes do mouse, mesmo que você não tenha manipuladores definidos para ele. Essa armadilha é feita quando os conjuntos de controle capturam a propriedade para true e isso efetivamente impede que os eventos subsequentes sejam borbulhados. A propriedade Capture é voltada para os Formulários False pelo Windows quando obtém um evento MouseUp.
Repito, é assim que funciona nos formulários do Windows, você pode verificar duas vezes, mas IMHO não há razão para que isso seja diferente para o WPF.
Para referência: ver a seção "Processamento de formulários do Windows" em http://blogs.msdn.com/jfoscoding/archive/2005/07/28/44647.aspx (Role ligeiramente para baixo do meio da página).
NOTA: Veja meu comentário à resposta de Arcturu para obter uma referência sobre eventos de bolhas e tunelamento, aumente as sequências.
Evento de botão suprime o mousedown e o mouseup porque o evento de botão é um evento de alto nível e possui um bit de código que dão a lidar com o true, essa causa suprimida para o mousdown para resolver esse problema, você pode adicionar este código no construtor da janela
TheButton.AddHandler(
UIElement.MouseDownEvent,
new MouseButtonEventHandler(TheStackPanel_MouseDown),
true);