Jak programowo kliknąć przycisk w WPF?

144

Ponieważ button.PerformClick()w WPF nie ma metody, czy istnieje sposób na programowe kliknięcie przycisku WPF?

tghoang
źródło

Odpowiedzi:

128

WPF przyjmuje tutaj nieco inne podejście niż WinForms. Zamiast automatyzacji obiektu wbudowanej w API, mają oddzielną klasę dla każdego obiektu, która jest odpowiedzialna za jego automatyzację. W takim przypadku potrzebujesz ButtonAutomationPeerdo wykonania tego zadania.

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

Tutaj wpis na blogu na ten temat.

Uwaga: IInvokeProviderinterfejs jest zdefiniowany w UIAutomationProviderzestawie.

JaredPar
źródło
2
Ślizg, nie byłem tego świadomy. Może być bardzo przydatne do testów automatycznych. Zauważ, że istnieje komentarz do tego linku, który sugeruje użycie dostarczonej fabryki do uzyskania partnera automatyzacji zamiast tworzenia go samodzielnie.
Greg D
7
Dzięki za to. Starałem się znaleźć prawidłowe przestrzenie nazw w mojej aplikacji, dopóki nie dodałem odwołania do UIAutomationProvider. Potem trzeba było dodaćusing System.Windows.Automation.Peers; using System.Windows.Automation.Provider;
sierżantKK
5
Jedna linijka dla skłonnych:((IInvokeProvider) (new ButtonAutomationPeer(someButton).GetPattern(PatternInterface.Invoke)).Invoke();
Danny Beckett
3
Należy zauważyć, że wywołanie Invoke jest asynchroniczne. Oznacza to, że jeśli używasz go w teście jednostkowym, a następnym wierszem jest sprawdzenie oczekiwanego wyniku kliknięcia przycisku, być może będziesz musiał poczekać.
Denver
3
W celach informacyjnych IInvokeProviderinterfejs jest zdefiniowany w UIAutomationProviderzestawie.
Steven Rands
188

Jak powiedział JaredPar, możesz odnieść się do artykułu Josha Smitha na temat automatyzacji. Jeśli jednak przejrzysz komentarze do jego artykułu, znajdziesz bardziej elegancki sposób zgłaszania zdarzeń względem kontrolek WPF

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

Osobiście wolę ten powyżej zamiast rówieśników automatyzacji.

Denis Vuyka
źródło
26
Rozwiązanie RaiseEvent tylko podnosi zdarzenie. Nie wykonuje polecenia związanego z przyciskiem (jak mówi Skanaar)
Eduardo Molteni
4
Jeśli używasz XAML, np. <Button Name = "X" Click = "X_Click" />, zdarzenie zostanie przechwycone przez moduł obsługi kliknięcia w normalny sposób. +1 ode mnie!
metao
4
Używam VB ... nie jestem pewien, czy kod jest inny dla wersetów VB C #, ale new RoutedEventArgs(Button.ClickEvent)nie działa dla mnie. Musiałem użyć new RoutedEventArgs(Primitives.ButtonBase.ClickEvent). W przeciwnym razie działa świetnie!
BrianVPS
3
Aby rozwinąć @EduardoMolteni i Skanaar, tracisz w ten sposób funkcjonalność IsEnabled oferowaną przez Commands, więc jeśli nie chcesz, aby wszystkie twoje zdarzenia sprawdzały, czy są włączone, AutomationPeer działa lepiej.
Justin Pihony,
34

jeśli chcesz wywołać zdarzenie kliknięcia:

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

A jeśli chcesz, żeby przycisk wyglądał tak, jakby był wciśnięty:

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

a potem nieprasowane:

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

lub użyj ToggleButton

Sonorx
źródło
22
this.PowerButton.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
Mahdi
źródło
5

Jednym ze sposobów programowego „kliknięcia” przycisku, jeśli masz dostęp do źródła, jest po prostu wywołanie procedury obsługi zdarzeń OnClick przycisku (lub wykonanie polecenia ICommand skojarzonego z przyciskiem, jeśli robisz rzeczy w sposób bardziej WPF-y ).

Dlaczego to robisz? Czy na przykład przeprowadzasz jakieś automatyczne testy lub próbujesz wykonać tę samą czynność, którą wykonuje przycisk z innej sekcji kodu?

Greg D.
źródło
Nie jest to jedyne rozwiązanie mojego problemu, ale kiedy próbowałem to zrobić, stwierdziłem, że nie jest to tak łatwe jak button.PerformClick (), więc trochę zaciekawiony ... :)
tghoang
Musiałem kliknąć menu innego okna w tym samym projekcie, a MyOtherWindow.mnuEntry_Click (Me, New Windows.RoutedEventArgs) to zrobił. Naprawdę proste.
Andrea Antonangeli
4

Jak powiedział Greg D , myślę, że alternatywą dla Automationkliknięcia przycisku za pomocą wzorca MVVM (wywołanie zdarzenia kliknięcia i wykonanie polecenia) jest wywołanie OnClickmetody za pomocą odbicia:

typeof(System.Windows.Controls.Primitives.ButtonBase).GetMethod("OnClick", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(button, new object[0]);
Maks
źródło
Jest to dobry sposób na użycie zdarzenia z elementu podrzędnego do uruchomienia polecenia na rodzicu.
Cool Blue
2

Podczas korzystania ze wzorca polecenia MVVM dla funkcji przycisku (zalecana praktyka), prosty sposób wywołania efektu przycisku jest następujący:

someButton.Command.Execute(someButton.CommandParameter);

Spowoduje to użycie obiektu Command, który wyzwala przycisk i przekaże CommandParameter zdefiniowany przez XAML .

KVKConsultancy
źródło
Jedna uwaga, to tylko wyzwala efekt, jaki będzie miał przycisk po kliknięciu. Nie generuje żadnych zdarzeń kliknięcia w kolejce wysyłania wiadomości. We wszystkich praktycznych przypadkach tego będziesz pragnął i unikniesz niepotrzebnych zdarzeń dyspozytorskich (np. Bardziej wydajnych), ale jeśli faktycznie w jakiś sposób
wymagasz
0

Problem z rozwiązaniem Automation API polega na tym, że wymagało ono odwołania do zestawu Framework UIAutomationProvider jako zależności projektu / pakietu.

Alternatywą jest naśladowanie zachowania. Poniżej znajduje się moje rozszerzone rozwiązanie, które również przewodzi wzorzec MVVM z powiązanymi poleceniami - zaimplementowanymi jako metoda rozszerzająca :

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
źródło