Como clicar programaticamente em um botão no WPF?


144

Como não há button.PerformClick()método no WPF, existe uma maneira de clicar em um botão WPF programaticamente?

Respostas:


128

O WPF adota uma abordagem ligeiramente diferente da do WinForms aqui. Em vez de ter a automação de um objeto incorporada à API, eles têm uma classe separada para cada objeto responsável por automatizá-lo. Nesse caso, você precisa ButtonAutomationPeerrealizar esta tarefa.

ButtonAutomationPeer peer = new ButtonAutomationPeer(someButton);
IInvokeProvider invokeProv = peer.GetPattern(PatternInterface.Invoke) as IInvokeProvider;
invokeProv.Invoke();

Aqui está um post sobre o assunto.

Nota: a IInvokeProviderinterface é definida na UIAutomationProvidermontagem.


2
Slick, eu não estava ciente disso. Pode ser muito útil para testes automatizados. Observe que há um comentário nesse link que sugere o uso de uma fábrica fornecida para obter o par de automação em vez de criar você mesmo.
8139 Greg D

7
Obrigado por isso. Lutei para encontrar os namespaces corretos no meu aplicativo, até adicionar uma referência a UIAutomationProvider. Em seguida, tive que adicionarusing System.Windows.Automation.Peers; using System.Windows.Automation.Provider;
sargentoKK

5
Um one-liner para os inclinados:((IInvokeProvider) (new ButtonAutomationPeer(someButton).GetPattern(PatternInterface.Invoke)).Invoke();
Danny Beckett

3
Uma coisa a observar é que a chamada de chamada é assíncrona. Isso significa que se você o estiver usando em um teste de unidade e sua próxima linha for verificar o resultado esperado ao clicar no botão, talvez seja necessário aguardar.
Denver

3
Apenas para referência, a IInvokeProviderinterface é definida na UIAutomationProvidermontagem.
Steven Rands 31/01

188

Como JaredPar disse, você pode consultar o artigo de Josh Smith sobre automação. No entanto, se você examinar os comentários de seu artigo, encontrará uma maneira mais elegante de gerar eventos contra os controles do WPF

someButton.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent));

Pessoalmente, prefiro o acima, em vez de pares de automação.


26
A solução RaiseEvent apenas gera o evento. Não executar o comando associado ao botão (como Skanaar digamos)
Eduardo Molteni

4
Se você usar XAML, por exemplo, <Nome do botão = "X" Clique = "X_Click" />, o evento será capturado pelo manipulador ao clicar como normalmente. +1 de mim!
metao

4
Estou usando VB ... não tenho certeza se o código é diferente para VB versos c #, mas new RoutedEventArgs(Button.ClickEvent)não funcionou para mim. Eu tive que usar new RoutedEventArgs(Primitives.ButtonBase.ClickEvent). Caso contrário, funciona muito bem!
BrianVPS

3
Para elaborar o @EduardoMolteni e o Skanaar, você perde a funcionalidade IsEnabled fornecida pelos Commands dessa maneira; portanto, a menos que você queira que todos os seus eventos verifiquem se eles estão ativados, o AutomationPeer funciona melhor.
23612 Justin Pihony

34

se você quiser chamar o evento click:

SomeButton.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));

E se você quiser que o botão pareça estar pressionado:

typeof(Button).GetMethod("set_IsPressed", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(SomeButton, new object[] { true });

e não comprimido depois disso:

typeof(Button).GetMethod("set_IsPressed", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(SomeButton, new object[] { false });

ou use o ToggleButton


22
this.PowerButton.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));

5

Uma maneira de "clicar" programaticamente no botão, se você tiver acesso à fonte, é simplesmente chamar o manipulador de eventos OnClick do botão (ou Executar o ICommand associado ao botão, se você estiver fazendo as coisas da maneira mais WPF-y )

Por que você está fazendo isso? Você está realizando algum tipo de teste automatizado, por exemplo, ou tentando executar a mesma ação que o botão executa em uma seção diferente do código?


Não é a única solução para o meu problema, mas quando eu tentei fazer isso, descobri que não é tão fácil como button.PerformClick (), então apenas um pouco curioso ... :)
tghoang

Eu tive que clicar no menu de outra janela no mesmo projeto, e MyOtherWindow.mnuEntry_Click (Eu, novo Windows.RoutedEventArgs) fez isso. Realmente simples.
Andrea Antonangeli

4

Como Greg D disse, acho que uma alternativa para Automationclicar em um botão usando o padrão MVVM (evento de clique gerado e comando executado) é chamar o OnClickmétodo usando reflexão:

typeof(System.Windows.Controls.Primitives.ButtonBase).GetMethod("OnClick", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(button, new object[0]);

Essa é uma boa maneira de usar um evento de um elemento filho para disparar um comando em um pai.
Cool Blue

2

Ao usar o padrão MVVM Command para a função Button (prática recomendada), uma maneira simples de acionar o efeito do Button é a seguinte:

someButton.Command.Execute(someButton.CommandParameter);

Isso usará o objeto Command que o botão dispara e passa o CommandParameter definido pelo XAML .


Uma observação: isso só aciona o efeito que o botão terá se clicado. Ele não gera nenhum evento de clique na fila de envio de mensagens. Para todos os casos práticos, isto é o que você deseja e vai evitar eventos expedidor desnecessários (por exemplo, maior performance), mas se o seu realmente alguma forma exigindo que os eventos expedidor outras soluções acima são mais apropriadas
KVKConsultancy

0

O problema com a solução da API de automação é que ela requer uma referência ao assembly do Framework UIAutomationProvidercomo dependência de projeto / pacote.

Uma alternativa é emular o comportamento. A seguir, há minha solução estendida que também condena o padrão MVVM com seus comandos vinculados - implementados como método de extensão :

public static class ButtonExtensions
{
    /// <summary>
    /// Performs a click on the button.<br/>
    /// This is the WPF-equivalent of the Windows Forms method "<see cref="M:System.Windows.Forms.Button.PerformClick" />".
    /// <para>This simulates the same behaviours as the button was clicked by the user by keyboard or mouse:<br />
    /// 1. The raising the ClickEvent.<br />
    /// 2.1. Checking that the bound command can be executed, calling <see cref="ICommand.CanExecute" />, if a command is bound.<br />
    /// 2.2. If command can be executed, then the <see cref="ICommand.Execute(object)" /> will be called and the optional bound parameter is p
    /// </para>
    /// </summary>
    /// <param name="sourceButton">The source button.</param>
    /// <exception cref="ArgumentNullException">sourceButton</exception>
    public static void PerformClick(this Button sourceButton)
    {
        // Check parameters
        if (sourceButton == null)
            throw new ArgumentNullException(nameof(sourceButton));

        // 1.) Raise the Click-event
        sourceButton.RaiseEvent(new RoutedEventArgs(System.Windows.Controls.Primitives.ButtonBase.ClickEvent));

        // 2.) Execute the command, if bound and can be executed
        ICommand boundCommand = sourceButton.Command;
        if (boundCommand != null)
        {
            object parameter = sourceButton.CommandParameter;
            if (boundCommand.CanExecute(parameter) == true)
                boundCommand.Execute(parameter);
        }
    }
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.