Por que o Button Click Event “Bubble Up Visual Tree” para Stackpanel, como afirma o artigo do MSDN?

StackOverflow https://stackoverflow.com/questions/662201

  •  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.";
        }

    }
}
Foi útil?

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);
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top