我开始使用TestStack White (UI自动化)来自动化WPF现有应用程序中的测试。当使用标准控件时,一切都很好。但是,在尝试交互自定义控件时,我遇到了一些问题。
例如,我有一个LabeledComboBox,它实际上是一个TextBlock加上一个ComboBox。这被定义为从XAML中的Control +一个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,则UI自动化可以正确地看到TextBox和ComboBox。
我试图为我的控件创建一个AutomationPeer (FrameworkElementAutomationPeer),但是到目前为止,我还没有能够使UI自动化中的TextBlock可见。一个有趣的结果是,FrameworkElementAutomationPeer::GetChildrenCore()正确地返回了两个自动化对等点的列表,一个用于TextBlock,一个用于ComboBox。
我应该如何更改我的自定义控件,以便它能够正确地使用UI自动化和白色测试?
发布于 2016-11-07 07:32:38
TextBlock的默认自动化对等程序(TextBlockAutomationPeer)出于某种原因从UI树中移除相应的所有者(如果它是ControlTemplate的一部分)。
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
}因此,要解决这个问题,您必须声明TextBlock不是在ControlTemplate中,或者使用这样的代码来解决(很难将其概括为整个应用程序.):
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以返回自定义的OnCreateAutomationPeer。
https://stackoverflow.com/questions/40454625
复制相似问题