使用 UI 自动化订阅 ComboBox 选择更改事件

Subscribe to ComboBox selection changed event with UI Automation

我是 UI-自动化领域的新手。
有一个 ComboBox 类型的 AutomationElement。
我正在寻找一种方法来订阅 ComboBox 更改其 Name 属性.

时引发的事件

这是我正在尝试做的,但它不起作用:

Automation.AddAutomationPropertyChangedEventHandler(
    elementComboBox,
    TreeScope.Element,
    new AutomationPropertyChangedEventHandler(OnUIAutomationPropChanged),
    NameProperty
);

看来您已经知道如何访问 ComboBox 元素,所以让我们设置一个 AddAutomationPropertyChangedEventHandler to detect ExpandCollapsePattern.ExpandCollapseStateProperty 更改事件。

ComboBox 控件是由以下部分组成的复合对象:

  • 外部容器,
  • 内部编辑控件,用于输入selection/search一个值,当ComboBox类型为DropDown
  • 时可用
  • 内部 ListControl,用于呈现 ComboBox 中包含的项目列表,显示为 ComboBox 下拉元素。

项目选择属于 ListControl,我们需要从这个控件中获取值。
List Automation Element supports the SelectionPattern.
SelectionPatter.GetSelection()方法returnListItem个元素的集合:Name属性项returns 当前选择值。

使用 AutomationElement 标识感兴趣的 ComboBox,何时可以为其 ExpandCollapseState 属性 设置事件处理程序,如下所示:

bool success = SetExpandCollapseEventHandler(elementComboBox);
// [...]
// Remove the handler when you're done with it
RemoveExpandCollapseEventHandler(elementComboBox);

这里,string selectedValue包含ComboBox关闭时选择的值:
当然,当ExpandCollapseState为ExpandedCollpsed时,你都可以读取值,比较当前值和选择的值

private AutomationPropertyChangedEventHandler ExpandCollapsedHandler = null;

public bool SetExpandCollapseEventHandler(AutomationElement element)
{
    if ((bool)element.GetCurrentPropertyValue(AutomationElement.IsExpandCollapsePatternAvailableProperty)) {
        Automation.AddAutomationPropertyChangedEventHandler(element, TreeScope.Element,
            ExpandCollapsedHandler = new AutomationPropertyChangedEventHandler(OnExpandCollapeChanged),
            ExpandCollapsePattern.ExpandCollapseStateProperty);
        return true;
    }
    return false;
}

private void OnExpandCollapeChanged(object elm, AutomationPropertyChangedEventArgs e)
{
    var element = elm as AutomationElement;
    if (element.TryGetCurrentPattern(ExpandCollapsePattern.Pattern, out object value)) {
        var state = (value as ExpandCollapsePattern).Current.ExpandCollapseState;
        if (state == ExpandCollapseState.Collapsed) {
            var listElement = element.FindFirst(TreeScope.Children, 
                new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.List));
            if (listElement != null) {
                var selectedItem = (listElement.GetCurrentPattern(
                     SelectionPattern.Pattern) as SelectionPattern).Current.GetSelection().FirstOrDefault();
                string selectedValue = selectedItem?.Current.Name;
            }
        }
    }
}

void RemoveExpandCollapseEventHandler(AutomationElement element)
{
    Automation.RemoveAutomationPropertyChangedEventHandler(element, ExpandCollapsedHandler);
    ExpandCollapsedHandler = null;
}

► 订阅 SelectionItemPattern's ElementSelectedEvent:

如果您需要知道 ComboBox List 的任何元素何时为 changed/selected 而没有使用 GUI 进行实际用户选择,您可以订阅子元素引发的事件List 元素。
您只需要一个处理程序,附加到更广泛的范围:TreeScope.Children.

警告:由于这些事件需要异步处理,因此事件在线程池线程中引发。

这会产生后果:

  • 您不能直接访问 UI 线程中的控件(您需要 BeginInvoke()
  • 必须删除AutomationEventHandlerif/when ComboBox控件被释放(例如,当父Window关闭时),否则您的应用程序将变得不稳定,并且可能永远停滞不前,等待 return 来自某个元素的值的事件,该值在某个时刻不再响应。

您可能需要使用 WindowPattern.WindowClosedEvent handler to receive notifications when the parent Window is closed, then call the Automation.RemoveAutomationEventHandler (as shown here), but probably (to evaluate based on the context of the operations) also the Automation.RemoveAllEventHandlers() 方法。

称为:

bool success = SetListItemChangedEventHandler(elementComboBox);
// [...]
// Remove the handler when you're done with it
RemoveListItemChangedHandler();

private AutomationElement listElement = null;
private AutomationEventHandler ListItemChangedHandler = null;

public bool SetListItemChangedEventHandler(AutomationElement combobox)
{
    listElement = combobox.FindFirst(TreeScope.Children,
        new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.List));

    if ((bool)listElement.GetCurrentPropertyValue(AutomationElement.IsSelectionPatternAvailableProperty)) {
        Automation.AddAutomationEventHandler(SelectionItemPattern.ElementSelectedEvent, 
            listElement, TreeScope.Children, 
            ListItemChangedHandler = new AutomationEventHandler(OnListItemChanged));

        return true;
    }
    return false;
}
        
private void OnListItemChanged(object elm, AutomationEventArgs e)
{
    string selectedValue = (elm as AutomationElement)?.Current.Name;
}

private void RemoveListItemChangedHandler()
{
    Automation.RemoveAutomationEventHandler(
        SelectionItemPattern.ElementSelectedEvent, listElement, ListItemChangedHandler);
    ListItemChangedHandler = null;
}