1

So I am new to C# and I would like to make an automation that will click few buttons on Win10 Calculator I downloaded LeanRunner Designer and used it's Model Manager to get Calculator button properties of number 5 and I got:

Auto.getWindow({
  "className": "ApplicationFrameWindow",
  "title": "Calculator"
}).getWindow({
  "className": "Windows.UI.Core.CoreWindow",
  "title": "Calculator"
}).getGeneric({
  "type": "Group",
  "automationId": "NumberPad",
  "name": "Number pad",
  "className": "NamedContainerAutomationPeer"
}).getButton({
  "automationId": "num5Button",
  "name": "Five",
  "className": "Button"
}).click(52, 24);

I transferred getWindow like this and that works

int hwnd=0;
IntPtr hwndChild=IntPtr.Zero;
IntPtr hwndChild2 = IntPtr.Zero;

//Get a handle for the Calculator Application main window
hwnd = FindWindow("ApplicationFrameWindow", "Calculator");
hwndChild = FindWindowEx((IntPtr)hwnd,IntPtr.Zero, "Windows.UI.Core.CoreWindow", "Calculator");             

But now I need to click the button and I'm stuck. I know the end result for click is

SendMessage((int)hwndChild2, BM_CLICK, 0, IntPtr.Zero);

But I'm missing the part to get to the button position. I don't want to use SendKeys. Thank you and sorry if I missed something or made it unclear.

Grigory Zhadko
  • 1,484
  • 1
  • 19
  • 33
  • 1
    You would be better off using _Microsoft UI Automation_ –  Apr 03 '21 at 07:44
  • I will try that, thanks, but still wandering if, and how to do it this way – Little Shorts Apr 03 '21 at 10:37
  • As suggested, use UI Automation and don't look back. Buttons have an [InvokePattern](https://learn.microsoft.com/en-us/dotnet/api/system.windows.automation.invokepattern), you just need to call its `Invoke()` method to click a Button. -- To find the Calculator Window - as all these applets' main Window has the same class name, `ApplicationFrameWindow` (e.g., see the Calendar Window) - find a more specific descendant (in `TreeScope.Descendants`) using an `AndCondition` that includes, e.g., `ControlType` and `Name` conditions. – Jimi Apr 03 '21 at 12:05
  • Note that some of these classes can have localized names (as `Calculator`), so this element name may not be meaningful, unless you just code for a specific language – Jimi Apr 03 '21 at 12:06

1 Answers1

1

As suggested above, this can be done with UIAutomation. For example like this:

public static AutomationElement calcUI;

public void FindAndClickButton(string name)
{
    System.Windows.Automation.Condition condition1 = new PropertyCondition(AutomationElement.ClassNameProperty, "Button");
    System.Windows.Automation.Condition condition2 = new PropertyCondition(AutomationElement.NameProperty, name);
    System.Windows.Automation.Condition condition = new AndCondition(condition1, condition2);
    AutomationElement button = calcUI.FindFirst(TreeScope.Descendants, condition);
    InvokePattern btnPattern = button.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;
    btnPattern.Invoke();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
    var calc = System.Diagnostics.Process.Start("calc");
    //wait for the UI to appear
    calc.WaitForInputIdle(5000);
    System.Threading.Thread.Sleep(2000);

    AutomationElement root = AutomationElement.RootElement;
    System.Windows.Automation.Condition condition = new PropertyCondition(AutomationElement.NameProperty, "Calculator");

    calcUI = root.FindFirst(TreeScope.Children, condition);
    if (calcUI != null)
    {
        // get and click the buttons for the calculation
        FindAndClickButton("Five");
        FindAndClickButton("Plus");
        FindAndClickButton("Nine");
        FindAndClickButton("Multiply by");
        FindAndClickButton("Three");
        FindAndClickButton("Divide by");
        FindAndClickButton("Four");
        FindAndClickButton("Equals");
        //get the result
        System.Windows.Automation.Condition conditionResult = new PropertyCondition(AutomationElement.AutomationIdProperty, "CalculatorResults");
        var result = calcUI.FindFirst(TreeScope.Descendants, conditionResult);
        MessageBox.Show(result.Current.Name);
    }
}

Don't forget to add references to the project

UIAutomationClient
UIAutomationTypes