【问题标题】:How should I define Custom Controls to enable UI Automation and TestStack White?我应该如何定义自定义控件以启用 UI 自动化和 TestStack White?
【发布时间】:2016-11-07 15:38:36
【问题描述】:

我开始使用 TestStack White(UI 自动化)来自动化 WPF 现有应用程序中的测试。使用标准控件时,一切正常。但是,我在尝试与自定义控件交互时遇到了问题。

例如,我有一个 LabeledComboBox,它实际上是一个 TextBlock 加上一个 ComboBox。这被定义为从 Control 派生的类加上 XAML 中的 ControlTemplate:

public class LabeledComboBox : Control
{
    static LabeledComboBox()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(LabeledComboBox), new FrameworkPropertyMetadata(typeof(LabeledComboBox)));
    }
}

<local:LabeledComboBox>
    <local:LabeledComboBox.Template>
         <ControlTemplate TargetType="{x:Type local:LabeledComboBox}">
             <StackPanel>
                  <TextBlock Text="Text"/>
                  <ComboBox/>
             </StackPanel>
          </ControlTemplate>
    </local:LabeledComboBox.Template>
</local:LabeledComboBox>

此控件有效,但如果您运行 UI 自动化验证 UI 自动化可见的唯一部分是 ComboBox,并且无法访问 TextBlock。

但是,如果您使用 XAML 和代码隐藏将其创建为 UserControl,则 TextBox 和 ComboBox 对 UI 自动化都是正确可见的。

我已尝试为我的控件创建一个 AutomationPeer (FrameworkElementAutomationPeer),但到目前为止,我无法使 TextBlock 对 UI 自动化可见。一个有趣的结果是 FrameworkElementAutomationPeer::GetChildrenCore() 正确返回了 2 个自动化对等点的列表,一个用于 TextBlock,一个用于 ComboBox。

我应该如何更改我的自定义控件,以便使用 UI 自动化和 White 对其进行正确测试?

【问题讨论】:

    标签: c# wpf custom-controls microsoft-ui-automation white-framework


    【解决方案1】:

    如果出于某种原因,TextBlock (TextBlockAutomationPeer) 的默认自动化对等方会从 UI 树中删除相应的所有者,如果它是 ControlTemplate 的一部分。

    您可以在这里找到相关代码:https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Automation/Peers/TextBlockAutomationPeer.cs,16e7fab76ffcb40a

     override protected bool IsControlElementCore()
     {
        // Return true if TextBlock is not part of a ControlTemplate
        TextBlock tb = (TextBlock)Owner;
        DependencyObject templatedParent = tb.TemplatedParent;
        return templatedParent == null || templatedParent is ContentPresenter; // If the templatedParent is a ContentPresenter, this TextBlock is generated from a DataTemplate
     }
    

    因此,要解决此问题,您必须不在 ControlTemplate 中声明 TextBlock,或者使用这样的代码解决方法(很难推广到整个应用程序...):

    public class LabeledComboBox : Control
    {
        static LabeledComboBox()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(LabeledComboBox), new FrameworkPropertyMetadata(typeof(LabeledComboBox)));
        }
    
        // define our own peer
        protected override AutomationPeer OnCreateAutomationPeer()
        {
            return new LabeledComboBoxAutomationPeer(this);
        }
    
        protected class LabeledComboBoxAutomationPeer : FrameworkElementAutomationPeer
        {
            public LabeledComboBoxAutomationPeer(LabeledComboBox owner) : base(owner)
            {
            }
    
            // replace all TextBlockAutomationPeer by our custom peer for TextBlock
            protected override List<AutomationPeer> GetChildrenCore()
            {
                var list = base.GetChildrenCore();
                for (int i = 0; i < list.Count; i++)
                {
                    var tb = list[i] as TextBlockAutomationPeer;
                    if (tb != null)
                    {
                        list[i] = new InteractiveTextBlockAutomationPeer((TextBlock)tb.Owner);
                    }
                }
                return list;
            }
        }
    
        // just do the default stuff, instead of the strange TextBlockAutomationPeer implementation
        protected class InteractiveTextBlockAutomationPeer : FrameworkElementAutomationPeer
        {
            public InteractiveTextBlockAutomationPeer(TextBlock owner) : base(owner)
            {
            }
    
            protected override AutomationControlType GetAutomationControlTypeCore()
            {
                return AutomationControlType.Text;
            }
    
            protected override string GetClassNameCore()
            {
                return "TextBlock";
            }
        }
    }
    

    另一种解决方案是创建您自己的 TextBlock 控件类(从 TextBlock 派生)并覆盖 OnCreateAutomationPeer 以返回自定义的。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-08-26
    • 2017-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-27
    • 1970-01-01
    相关资源
    最近更新 更多