How should I define Custom Controls to enable UI Automation and TestStack White?
See the question and my original answer on StackOverflowThe default automation peer for TextBlock (TextBlockAutomationPeer) removes the corresponding owner from the UI tree if it's part of a ControlTemplate, for some reason.
You can find the relevant code here: 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
}
So, to fix this, you'll have to declare the TextBlock not in a ControlTemplate, or workaround with a code like this (difficult to generalize to a whole app...):
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";
}
}
}
One other solution is to create your own TextBlock control class (deriving from TextBlock) and override OnCreateAutomationPeer to return the custom one.