I have a WPF app and I am trying to Automate it using FlaUI. I am facing a problem with the DxTabControl
. I have provided Automation IDs
to the DxTabControl
. I am using DXTabControl.ItemHeaderTemplate
to generate TabItems
dynamically.
According to DevExpress Team, The DXTabControl.ItemHeaderTemplate
doesnt support AutoamtionPeer
so a custom implementation has been added to override its default behaviour.
Now, I am able to see the TabControl
and the TabItems
in the Inspect.exe
.
Now , my requirement is to Access the currently selected Tabitem and find the CloseButton using the AutoamtionID mentioned in the XAML below and close it. Pasting below the line again. As there would be multiple TabItems generated, I am unable to get the Currently active/Selected TabItem
.
The XAML
is below
<dx:DXTabControl AutomationProperties.AutomationId="ViewsParentTabControl"
MaxWidth="4000"
MaxHeight="4000"
Margin="1,0,-1,0"
Focusable="False"
ItemsSource="{Binding OpenViews}"
SelectedIndex="{Binding SelectedTabIndex}"
TabContentCacheMode="CacheTabsOnSelecting">
<dx:DXTabControl.ItemHeaderTemplate>
<DataTemplate DataType="viewModels1:OpenViewDefinitionViewModel">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Name="CreatedViewName"
MaxWidth="100"
Text="{Binding Data.ViewDefinition.Name}"
TextTrimming="CharacterEllipsis"
ToolTip="{Binding DisplayName}" />
<TextBlock Grid.Row="0" Grid.Column="1"><Run Text=" [" /><Run Text="{Binding ItemsCount, FallbackValue=0, Mode=OneWay}" /><Run Text="]" /></TextBlock>
<controls2:ProgressIndicator AutomationProperties.AutomationId="ProgressCurrentView"
Grid.Row="0"
Grid.Column="3"
Width="14"
Margin="4,0,0,0"
VerticalAlignment="Center"
CircleBorder="{StaticResource ListBoxItem.Foreground}"
CircleFill="{StaticResource ListBoxItem.Foreground}"
IndicatorEnabled="{Binding IsDataLoading}" />
<Button AutomationProperties.AutomationId="CloseCurrentViewButton"
Grid.Row="0"
Grid.Column="2"
Width="10"
Height="10"
Margin="10,1,0,0"
Padding="0"
HorizontalAlignment="Right"
BorderThickness="0"
Command="{Binding DataContext.CloseItemCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=dx:DXTabControl}}"
CommandParameter="{Binding}"
Focusable="False"
Style="{StaticResource MwButtonStyle}"
ToolTip="Close">
<Path
Data="F1 M 26.9166,22.1667L 37.9999,33.25L 49.0832,22.1668L 53.8332,26.9168L 42.7499,38L 53.8332,49.0834L 49.0833,53.8334L 37.9999,42.75L 26.9166,53.8334L 22.1666,49.0833L 33.25,38L 22.1667,26.9167L 26.9166,22.1667 Z"
Fill="White"
Stretch="Fill" />
</Button>
</Grid>
</DataTemplate>
</dx:DXTabControl.ItemHeaderTemplate>
<dx:DXTabControl.ItemTemplate>
<DataTemplate DataType="viewModels1:OpenViewDefinitionViewModel">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<local:VoyagesGridControl />
<local:VoyageValidationUserControl
Grid.Row="1"
Grid.Column="0"
MinHeight="100"
MaxHeight="300"
Visibility="{Binding Path=IsVoyageValidationShowing, FallbackValue=Collapsed, Converter={StaticResource BooleanToVisibilityConverter}}" />
<local:VoyageHistoryUserControl
Grid.Row="2"
Grid.Column="0"
MinHeight="300"
MaxHeight="300"
Visibility="{Binding Path=IsVoyageHistoryShowing, FallbackValue=Collapsed, Converter={StaticResource BooleanToVisibilityConverter}}" />
<local:VesselHistoryUserControl
Grid.Row="3"
Grid.Column="0"
MinHeight="300"
MaxHeight="300"
Visibility="{Binding Path=IsVesselHistoryShowing, FallbackValue=Collapsed, Converter={StaticResource BooleanToVisibilityConverter}}" />
<local:VoyageEvents
Grid.Row="0"
Grid.RowSpan="3"
Grid.Column="1"
VerticalAlignment="Top"
Visibility="{Binding Path=IsVoyageEventsShowing, FallbackValue=Collapsed, Converter={StaticResource BooleanToVisibilityConverter}}" />
<controls2:ProgressIndicator AutomationProperties.AutomationId="showProgressForLoadingViews"
Grid.Row="0"
Grid.RowSpan="3"
Grid.Column="0"
Width="80"
HorizontalAlignment="Center"
VerticalAlignment="Center"
CircleBorder="{StaticResource ListBox.BorderBrush}"
CircleFill="{StaticResource ListBox.BorderBrush}"
IndicatorEnabled="{Binding IsDataLoading}" />
<!-- Buttons -->
<Grid Grid.Row="4" Grid.Column="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel
Grid.Column="0"
VerticalAlignment="Center"
Orientation="Horizontal">
<TextBlock Style="{StaticResource MwTextBlockLabelStyle}" Text="Last Refresh:" />
<TextBlock
Margin="2,0,4,0"
VerticalAlignment="Center"
Text="{Binding LoadDate, StringFormat=G}" />
</StackPanel>
<Button AutomationProperties.AutomationId="AddNewVoyageButton"
Grid.Row="0"
Grid.Column="1"
Padding="0"
Command="{Binding AddVoyagesCommand}"
Focusable="False"
Style="{StaticResource MwButtonStyle}"
ToolTip="Add a new voyage to this View (ALT + A)">
<StackPanel Orientation="Horizontal">
<ContentControl Height="26" Content="{StaticResource Add}" />
<Label Style="{StaticResource MwLabelStyle}">_Add</Label>
</StackPanel>
</Button>
<Button AutomationProperties.AutomationId="refreshVoyageButton"
Grid.Row="0"
Grid.Column="2"
Padding="0"
Command="{Binding RefreshVoyagesCommand}"
Focusable="False"
Style="{StaticResource MwButtonStyle}"
ToolTip="Refresh the this View (modified entries are left unchanged)">
<StackPanel Orientation="Horizontal">
<ContentControl Height="26" Content="{StaticResource Refresh}" />
<TextBlock Style="{StaticResource MwTextBlockLabelStyle}" Text="Refresh" />
</StackPanel>
</Button>
<Button AutomationProperties.AutomationId="showVoyageHistroyButton"
Grid.Column="4"
Margin="2,2,2,2"
Padding="0"
VerticalAlignment="Center"
Command="{Binding ShowVoyageHistoryCommand}"
Focusable="False"
ToolTip="Show the selected voyage's change history"
Visibility="{Binding Data.ViewDefinition.IsInternalView, Converter={StaticResource MwBoolToVisibilityConverterReverse}}">
<StackPanel Orientation="Horizontal">
<ContentControl Height="26" Content="{StaticResource ShowVoyageHistory}" />
<TextBlock
Style="{StaticResource MwTextBlockLabelStyle}"
Text="Hide Voyage History"
Visibility="{Binding IsVoyageHistoryShowing, Converter={StaticResource BooleanToVisibilityConverter}}" />
<TextBlock
Style="{StaticResource MwTextBlockLabelStyle}"
Text="Show Voyage History"
Visibility="{Binding IsVoyageHistoryShowing, Converter={StaticResource MwBoolToVisibilityConverterReverse}}" />
</StackPanel>
</Button>
<Button AutomationProperties.AutomationId="showVesselHistroyButton"
Grid.Column="5"
Margin="2,2,2,2"
Padding="0"
VerticalAlignment="Center"
Command="{Binding ShowVesselHistoryCommand}"
Focusable="False"
ToolTip="Show the selected vessel's voyage history"
Visibility="{Binding Data.ViewDefinition.IsInternalView, Converter={StaticResource MwBoolToVisibilityConverterReverse}}">
<StackPanel Orientation="Horizontal">
<ContentControl Height="26" Content="{StaticResource ShowVesselHistory}" />
<TextBlock
Style="{StaticResource MwTextBlockLabelStyle}"
Text="Hide Vessel History"
Visibility="{Binding IsVesselHistoryShowing, Converter={StaticResource BooleanToVisibilityConverter}}" />
<TextBlock
Style="{StaticResource MwTextBlockLabelStyle}"
Text="Show Vessel History"
Visibility="{Binding IsVesselHistoryShowing, Converter={StaticResource MwBoolToVisibilityConverterReverse}}" />
</StackPanel>
</Button>
<Button AutomationProperties.AutomationId="showVoyageButton"
Grid.Column="6"
Margin="2,2,2,2"
Padding="0"
VerticalAlignment="Center"
Command="{Binding ShowVesselVisitsCommand}"
Focusable="False"
ToolTip="Show the selected voyage's events"
Visibility="{Binding Data.ViewDefinition.IsInternalView, Converter={StaticResource MwBoolToVisibilityConverterReverse}}">
<StackPanel Orientation="Horizontal">
<ContentControl Height="26" Content="{StaticResource Anchor}" />
<TextBlock
Style="{StaticResource MwTextBlockLabelStyle}"
Text="Hide Voyage Events"
Visibility="{Binding IsVoyageEventsShowing, Converter={StaticResource BooleanToVisibilityConverter}}" />
<TextBlock
Style="{StaticResource MwTextBlockLabelStyle}"
Text="Show Voyage Events"
Visibility="{Binding IsVoyageEventsShowing, Converter={StaticResource MwBoolToVisibilityConverterReverse}}" />
</StackPanel>
</Button>
<Border Grid.Row="0" Grid.Column="8">
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<DataTrigger Binding="{Binding IsDuplicateView, Mode=TwoWay}" Value="true">
<Setter Property="Background" Value="LightGreen" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Button AutomationProperties.AutomationId="DuplicateCheckButton"
Padding="0"
Command="{Binding DuplicateVoyagesCommand}"
Focusable="False"
Style="{StaticResource MwButtonStyle}"
ToolTip="Switch to duplicate Voyages (ALT + D)"
Visibility="{Binding Data.ViewDefinition.IsInternalView, Converter={StaticResource MwBoolToVisibilityConverterReverse}}">
<StackPanel Orientation="Horizontal">
<ContentControl Height="26" Content="{StaticResource Duplicate}" />
<AccessText Style="{StaticResource MwAccessTextLabelStyle}" Text="{Binding VoyageDuplicateText}" />
</StackPanel>
</Button>
</Border>
<Button AutomationProperties.AutomationId="PublishVoyagesButton"
Grid.Row="0"
Grid.Column="9"
Padding="0"
Command="{Binding PublishVoyagesCommand}"
Focusable="False"
Style="{StaticResource MwButtonStyle}"
ToolTip="Publish any modified Voyages (ALT + P)"
Visibility="{Binding Data.ViewDefinition.IsInternalView, Converter={StaticResource MwBoolToVisibilityConverterReverse}}">
<StackPanel Orientation="Horizontal">
<ContentControl Height="26" Content="{StaticResource Publish}" />
<AccessText Style="{StaticResource MwAccessTextLabelStyle}" Text="{Binding VoyagePublishText}" />
</StackPanel>
</Button>
<Button AutomationProperties.AutomationId="UndoSingleVoyageButton"
Grid.Row="0"
Grid.Column="10"
Padding="0"
Command="{Binding UndoSingleChangedVoyagesCommand}"
Focusable="False"
Style="{StaticResource MwButtonStyle}"
ToolTip="Locally Undo unpublished changes to the selected voyage"
Visibility="{Binding Data.ViewDefinition.IsInternalView, Converter={StaticResource MwBoolToVisibilityConverterReverse}}">
<StackPanel Orientation="Horizontal">
<ContentControl Height="26" Content="{StaticResource Undo}" />
<TextBlock Style="{StaticResource MwTextBlockLabelStyle}" Text="Undo Selected" />
</StackPanel>
</Button>
<Button AutomationProperties.AutomationId="UndoandUnpublishVoyageButton"
Grid.Row="0"
Grid.Column="11"
Padding="0"
Command="{Binding UndoChangedVoyagesCommand}"
Focusable="False"
Style="{StaticResource MwButtonStyle}"
ToolTip="Locally Undo any changed and unpublished voyages"
Visibility="{Binding Data.ViewDefinition.IsInternalView, Converter={StaticResource MwBoolToVisibilityConverterReverse}}">
<StackPanel Orientation="Horizontal">
<ContentControl Height="26" Content="{StaticResource Undo}" />
<TextBlock Style="{StaticResource MwTextBlockLabelStyle}" Text="Undo All" />
</StackPanel>
</Button>
</Grid>
</Grid>
</DataTemplate>
</dx:DXTabControl.ItemTemplate>
</dx:DXTabControl>
My FlaUI
approach of locating the Controls is below
public IMainWindow ConfirmCreatedView()
{
_logger.Info("Checking the newly created View on the screen");
//Apoorv: Need to find TabItem here
_controlAction.Highlight(ViewsTabControl); // This highlights the TabControl- Works
int NumberOfActiveTabs = ViewsTabControl.TabItems.Length; // This gives me no of TabItems
TabItem SelectedTab= ViewsTabControl.SelectedTabItem as TabItem; // Gives me Null here
var newTab = ViewsTabControl.SelectedTabItemIndex ; // Give me -1 here
_controlAction.Highlight(ViewsTabControl.TabItems[2]); // Works. It highlights the TabItem at position 2
_controlAction.ClickWait<TabItem>(ViewsTabControl.TabItems[2]); // This goes and clicks the tab item
TabItem SelectedTabs = ViewsTabControl.SelectedTabItem as TabItem;
var check = ViewsTabControl.TabItems[2].FindAllChildren();
// TabItem ti = ViewsTabControl.SelectedItem as TabItem;
//_controlAction.Highlight()
_controlAction.Highlight(CloseCurrentView); // highlights the close button atTabItem[0]
_controlAction.Click<Button>(CloseCurrentView); // closes it
return this;
}
I am using FlaUI
to Find the TabControl
using AutomationID
as shown below
private Tab ViewsTabControl => _uiAutomation.FindElement("ViewsParentTabControl", Automation.FindBy.Id).AsTab();
private TabItem ViewsTabItem => _uiAutomation.FindElement("DXTabItem", Automation.FindBy.Id).AsTabItem();
I would like to find the curently active TabItem
based on the Index and then go and click the close button by automating it.
TabItem SelectedTab= ViewsTabControl.SelectedTabItem as TabItem; // Gives me Null here
var newTab = ViewsTabControl.SelectedTabItemIndex ; // Give me -1 here
DevEXpress
don't create automation peers
for controls within ItemHeaderTemplate
. It will be necessary to use a custom automation peer
to provide this functionality. For example, I used the following class for test purposes:
public class DXTabItemAutomationPeerEx : DXTabItemAutomationPeer, ISelectionItemProvider {
private DXTabItem TabItem => base.Owner as DXTabItem;
private DXTabControl TabControl => TabItem.With((DXTabItem x) => x.Owner);
public DXTabItemAutomationPeerEx(DXTabItem ownerCore) : base(ownerCore) { }
protected override List<AutomationPeer> GetChildrenCore() {
List<AutomationPeer> children = base.GetChildrenCore();
foreach (var button in LayoutTreeHelper.GetVisualChildren(Owner).OfType<Button>())
children.Add(new ButtonAutomationPeer(button));
return children;
}
bool ISelectionItemProvider.IsSelected { get {
if (TabControl != null) {
return TabControl.SelectedContainer == TabItem;
}
return false;
}
}
}
Post adding this code inside my MainPage.Xaml.cs
, I added a static constructor
to register
it
static MainWindow() {
NavigationAutomationPeersCreator.Default.RegisterObject(typeof(DXTabItem), typeof(DXTabItemAutomationPeerEx), owner => new DXTabItemAutomationPeerEx((DXTabItem)owner));
}
post this , these lines work as charm
TabItem CurrentTab = ViewsTabControl.SelectedTabItem as TabItem;
var Children = ViewsTabControl.FindAllChildren();
foreach (var child in Children) {
var subChildren = child.FindAllChildren();
}