¿Cómo hacer clic programáticamente en un botón en WPF?

144

Dado que no hay ningún button.PerformClick()método en WPF, ¿hay alguna forma de hacer clic en un botón de WPF mediante programación?

tghoang
fuente

Respuestas:

128

WPF adopta un enfoque ligeramente diferente al de WinForms aquí. En lugar de tener la automatización de un objeto integrado en la API, tienen una clase separada para cada objeto que es responsable de automatizarlo. En este caso necesita el ButtonAutomationPeerpara llevar a cabo esta tarea.

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

aquí hay una publicación de blog sobre el tema.

Nota: la IInvokeProviderinterfaz se define en el UIAutomationProviderensamblaje.

JaredPar
fuente
2
Slick, no estaba al tanto de esto. Podría ser muy útil para pruebas automatizadas. Tenga en cuenta que hay un comentario en ese enlace que sugiere utilizar una fábrica proporcionada para obtener el par de automatización en lugar de crear uno usted mismo.
Greg D
77
Gracias por esto. Luché por encontrar los espacios de nombres correctos en mi aplicación, hasta que agregué una referencia UIAutomationProvider. Luego tuve que agregarusing System.Windows.Automation.Peers; using System.Windows.Automation.Provider;
sergeantKK
55
Una frase para los inclinados:((IInvokeProvider) (new ButtonAutomationPeer(someButton).GetPattern(PatternInterface.Invoke)).Invoke();
Danny Beckett
3
Una cosa a tener en cuenta es que la llamada Invoke es asíncrona. Eso significa que si lo está utilizando en una prueba unitaria y su próxima línea es verificar el resultado esperado de hacer clic en el botón, es posible que tenga que esperar.
denver
3
Solo como referencia, la IInvokeProviderinterfaz se define en el UIAutomationProviderensamblaje.
Steven Rands
188

Como dijo JaredPar, puede consultar el artículo de Josh Smith sobre Automatización. Sin embargo, si mira los comentarios a su artículo, encontrará una forma más elegante de generar eventos contra los controles de WPF

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

Personalmente prefiero el de arriba en lugar de los de automatización.

Denis Vuyka
fuente
26
La solución RaiseEvent solo provoca el evento. No ejecuta el comando asociado con el botón (como dice Skanaar)
Eduardo Molteni
44
Si usa XAML, p. Ej. <Button Name = "X" Click = "X_Click" />, el evento será detectado por el controlador de clic de forma normal. ¡+1 de mi parte!
metao
44
Estoy usando VB ... no estoy seguro si el código es diferente para VB versos C #, pero new RoutedEventArgs(Button.ClickEvent)no funcionó para mí. Tuve que usar new RoutedEventArgs(Primitives.ButtonBase.ClickEvent). De lo contrario, funciona muy bien!
BrianVPS
3
Para profundizar en @EduardoMolteni y Skanaar, pierde la funcionalidad IsEnabled dada por los comandos de esta manera, por lo que a menos que desee que todos sus eventos verifiquen si están habilitados, el AutomationPeer funciona mejor.
Justin Pihony
34

si desea llamar, haga clic en evento:

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

Y si quieres que el botón parezca presionado:

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

y sin presionar después de eso:

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

o use el ToggleButton

Sonorx
fuente
22
this.PowerButton.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
Mahdi
fuente
5

Una forma de "hacer clic" programáticamente en el botón, si tiene acceso a la fuente, es simplemente llamar al controlador de eventos OnClick del botón (o Ejecutar el ICommand asociado con el botón, si está haciendo las cosas de la manera más WPF-y )

¿Por qué estás haciendo esto? ¿Está haciendo algún tipo de prueba automatizada, por ejemplo, o está tratando de realizar la misma acción que realiza el botón desde una sección de código diferente?

Greg D
fuente
No es la única solución a mi problema, pero cuando intenté hacer esto, descubrí que no es tan fácil como button.PerformClick (), por lo que es un poco curioso ... :)
tghoang
Tuve que hacer clic en el menú de otra ventana en el mismo proyecto, y MyOtherWindow.mnuEntry_Click (Me, New Windows.RoutedEventArgs) lo hizo. Muy simple
Andrea Antonangeli
4

Como dijo Greg D , creo que una alternativa a Automationhacer clic en un botón usando el patrón MVVM (evento de clic generado y comando ejecutado) es llamar al OnClickmétodo usando la reflexión:

typeof(System.Windows.Controls.Primitives.ButtonBase).GetMethod("OnClick", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(button, new object[0]);
Max
fuente
Esta es una buena manera de utilizar un evento de un elemento secundario para activar un comando en un elemento primario.
Cool Blue
2

Cuando se usa el patrón de comando MVVM para la función del botón (práctica recomendada), una manera simple de activar el efecto del botón es la siguiente:

someButton.Command.Execute(someButton.CommandParameter);

Esto usará el objeto Command que dispara el botón y pasará el CommandParameter definido por XAML .

KVKConsultor
fuente
Una nota, esto solo activa el efecto que tendrá el botón si se hace clic. No genera ningún evento de clic en la cola de envío de mensajes. Para todos los casos prácticos, esto es lo que deseará y evitará eventos de despachador innecesarios (por ejemplo, más eficaces), pero si realmente requiere los eventos de despachador, otras soluciones anteriores son más apropiadas
KVKConsultancy
0

El problema con la solución de API de automatización es que requiere una referencia al ensamblaje de Framework UIAutomationProvider como dependencia de proyecto / paquete.

Una alternativa es emular el comportamiento. A continuación está mi solución extendida que también considera el patrón MVVM con sus comandos vinculados, implementado como método de extensión :

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);
        }
    }
}
rittergig
fuente