WPF UIAutomation Получение дочернего контроля над пользовательским контролем

Я являюсь пользователем NordVPN и использую его без каких-либо проблем. Теперь для некоторых требований мне нужно установить некоторые из его свойств, таких как протокол (флажок) и щелкнуть по кнопкам из другого приложения.

Но эта область приложения выглядит как пользовательский элемент управления, а UIAutomation не может развернуться в нем.

Элементы внутри этого настраиваемого элемента управления не имеют идентификатора автоматизации.

Поэтому мне нужно знать, как мы могли бы проходить через пользовательские элементы управления в приложениях wpf, например, в других частях окна приложения, используя UIAutomation и White Framework.

Я пробовал до сих пор

  • с помощью TreeWalker (неспособный прочитать все элементы)

  • И попробуйте получить элемент из его местоположения AutomationElement.FromPoint(), но он возвращает весь настраиваемый элемент управления (определить из его границ), на котором я еще не могу пройти.

Любое предложение о том, как я мог бы перейти к пользовательскому контролю из UIAutomation.

введите описание изображения здесь

Для записи snoop может читать элементы, но VisualUIAVerify.exe не является.

Ответ 1

РЕДАКТИРОВАТЬ 1 - Уточнение: Для меня контроль не отображается в API автоматизации - если нет возможности однозначно идентифицировать их для автоматизации. Коэффициент идентификации может быть любым из имени, id, родного брата или родителя.


Как и ожидалось, отсутствие идентификаторов автоматизации привело к тому, что элементы управления не были видны в API дерева дерева UI.

Чтобы обойти это и зная, что они видны в приложении Snoop, вы можете использовать базовую логику (которую использует Snoop), чтобы программно автоматизировать эти элементы управления.

Действия

  • Загрузите двоичные файлы для SnoopUI и добавьте их в свой проект. Обязательно сохраните параметр компиляции как "Нет" и скопируйте его в выходной каталог.

    введите описание изображения здесь

  • Следующим шагом будет добавление вспомогательного метода, который использует эти двоичные файлы для ввода вашей DLL с помощью логики автоматизации в целевое приложение (которое является NordVPN) в этом случае. После того, как dll будет введена в целевой процесс, ManagedInjector также вызывает метод, который отправляется как параметр.

    public class Helper
    {
        public static void Inject(IntPtr windowHandle, Assembly assembly, string className, string methodName)
        {
            var location = Assembly.GetEntryAssembly().Location;
            var directory = Path.GetDirectoryName(location);
            var file = Path.Combine(directory, "HelperDlls", "ManagedInjectorLauncher" + "64-4.0" + ".exe");
    
            Debug.WriteLine(file + " " + windowHandle + " \"" + assembly.Location + "\" \"" + className + "\" \"" + methodName + "\"");
            Process.Start(file, windowHandle + " \"" + assembly.Location + "\" \"" + className + "\" \"" + methodName + "\"");
        }
    }
    
  • После того, как в приложение была введена dll автоматизации, доступ к Visual Tree довольно прост, используя Dispatcher и PresentationSources.

    public class Setup
    {
        public static bool Start()
        {
            Dispatcher dispatcher;
            if (Application.Current == null)
                dispatcher = Dispatcher.CurrentDispatcher;
            else
                dispatcher = Application.Current.Dispatcher;
    
            dispatcher.Invoke(AutomateApp);
            return true;
        }
    
        public static void AutomateApp()
        {
            Window root = null;
            foreach (PresentationSource presentationSource in PresentationSource.CurrentSources)
            {
                root = presentationSource.RootVisual as Window;
    
                if (root == null)
                    continue;
    
                if ("NordVPN ".Equals(root.Title))
                    break;
            }
    
  • Получение доступа к VisualTree является простым, но определение элементов управления не так просто, поскольку нет идентификаторов автоматизации или имен (имен), которые могут однозначно идентифицировать эти элементы управления. Но, к счастью, поскольку они используют MVVM, их можно идентифицировать с помощью привязки (ов), прикрепленных к ним.

    public static T GetChildWithPath<T>(this DependencyObject depObj, DependencyProperty property = null, string pathName = null) where T : DependencyObject
    {
        T toReturn = null;
    
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
        {
            var child = VisualTreeHelper.GetChild(depObj, i);
            bool pathNameMatch = (child is T) && child.IsPathNameMatch<T>(property, pathName);
            if (pathNameMatch)
            {
                toReturn = child as T;
                break;
            }
            else
                toReturn = GetChildWithPath<T>(child, property, pathName);
    
            if (toReturn != null)
                break;
        }
        return toReturn;
    }
    
  • После того, как вы получили доступ к элементам управления, теперь можно либо напрямую манипулировать их свойствами, либо обращаться к их соответствующим партнерам по автоматизации и поставщикам для автоматизации этих элементов управления.

    var checkBoxNames = new[]
    {
        "CyberSec", "AutomaticUpdates", "AutoConnect",
        "StartOnStartup", "KillSwitch", "ShowNotifications",
        "StartMinimized", "ShowServerList", "ShowMap",
        "UseCustomDns", "ObfuscatedServersOnly"
    };
    
    foreach(var path in checkBoxNames)
    {
        var chkBox = settingsView.GetChildWithPath<CheckBox>(CheckBox.IsCheckedProperty, path);
        if(chkBox != null && chkBox.IsEnabled)
            chkBox.SimulateClick();
    }
    

Полный рабочий образец загружен в репозиторий Github.

введите описание изображения здесь