Xamarin Forms Automation Id for toolbaritems - xamarin.forms

We are building Test Automation with Appium and we need to add Unique Identifiers for elements we are interacting with. We have achieved this through adding AutomationId property to the XAML file.
This works fine except for Toolbaritems. The added AutomationId is not visible as content-desc for the elements.
<core:AppBaseContentPage.ToolbarItems>
<ToolbarItem AutomationId="Button1" BindingContext="{Binding BindingContext, Source={x:Reference this}}" Parent="{x:Reference this}"
Text="{extensions:Translate Button1}"
Command="{Binding Button1Command}" />
Is this expected behaviour or am I missing something?

The AutomationId for ToolbarItems doesn't work (at least on Android), but you can identify them using the text property.
<NavigationPage.ToolbarItems>
<controls:BindableToolbarItem
AutomationId="ToolbarButtonHelp"
Text="ToolbarButtonHelp"
Icon="help"
Priority="0"
Order="Primary"
IsVisible="{Binding ToolbarService.IsHelpVisible, Mode=TwoWay}"
Command="{Binding ToolbarService.HelpCommand}"/>
<controls:BindableToolbarItem
AutomationId="ToolbarButtonMenu"
Text="ToolbarButtonMenu"
Icon="quick_menu"
Priority="1"
Order="Primary"
IsVisible="{Binding ToolbarService.IsMenuVisible, Mode=TwoWay}"
Command="{Binding ToolbarService.MenuCommand}"/>
</NavigationPage.ToolbarItems>
Example Xamarin UI Test
this.app.WaitForNoElement("ToolbarButtonHelp");

Related

XLS0501 The property 'Content' is set more than once

There is nothing wrong with my code as I can see, but this error I have never seen before keeps coming up, and the Entry element (Input Field) doesn't show up in the UI when I run the app. How to get rid of this problem?
<StackLayout>
<Entry
x:Name="InputField"
Text=""
Placeholder="Enter Todo"
TextChanged="HandleTextChanged"
Completed="HandleCompleted"/>
</StackLayout>
<StackLayout x:Name="TodoList">
</StackLayout>
As Jason suggested in the comments, The Content property of the ContentPage can only accept one direct child element. To get around this, you need to use a Layout container, like a StackLayout or a Grid.
Here's the code sample below for your reference:
<ContentPage.Content>
<StackLayout>
<StackLayout>
<Entry
x:Name="InputField"
Text=""
Placeholder="Enter Todo"
TextChanged="HandleTextChanged"
Completed="HandleCompleted"/>
</StackLayout>
<StackLayout x:Name="TodoList">
</StackLayout>
</StackLayout>
</ContentPage.Content>

PRISM Navigation - Xamarin Forms - MasterDetail/FlyoutPage page setup, previous page nav links disabled when back button pressed?

So I built an app using Prism Library and Xamarin Forms and the Navigation flow starts with a MasterDetail page, like so.
NavigationService.NavigateAsync($"/{nameof(MainMenuPage)}/{nameof(NavigationPage)}/{nameof(DashboardPage)}");
MainMenuPage
My MainMenuPage contains the Flyout page with a ListView of the MenuItems
<FlyoutPage.Flyout>
<NavigationPage
NavigationPage.HasNavigationBar="false"
Icon="{ StaticResource HamburgerIcon }"
Style="{ StaticResource MainMenuStyle }"
Title="{ grial:Translate PageTitleMainMenu }">
<x:Arguments>
<ContentPage>
<Grid>
<BoxView
Style="{ StaticResource MainMenuOrModalBackgroundStyle }"
Opacity="1" />
<Image
Style="{ StaticResource MainMenuBackgroundImageStyle }" />
<Grid
grial:Effects.ApplyIOSSafeAreaAsPadding="Left,Right"
RowSpacing="0"
VerticalOptions="FillAndExpand">
<Grid.RowDefinitions>
<RowDefinition
Height="Auto" />
<RowDefinition
Height="*" />
<RowDefinition
Height="Auto"/>
</Grid.RowDefinitions>
<local:BrandBlock
Grid.Row="0"
Grid.Column="0"
Margin="20,60,20,30"
VerticalOptions="Start"
HorizontalOptions="Start" />
<ListView
Margin="0,0,0,10"
Grid.Row="1"
SelectedItem="{ Binding SelectedMainMenuItem, Mode=TwoWay}"
ItemsSource="{ Binding MainMenuItems }"
VerticalOptions="FillAndExpand"
Style="{ StaticResource MainMenuListViewStyle }">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<views:MainMenuItemTemplate />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.Behaviors>
<behavior:EventToCommandBehavior EventName="ItemTapped"
Command="{Binding NavigateCommand}"
EventArgsParameterPath="Item">
</behavior:EventToCommandBehavior>
</ListView.Behaviors>
</ListView>
<views:ApplicationVersionTemplate Grid.Row="2" Margin="35,0,0,20" HorizontalOptions="Start" VerticalOptions="Start" />
</Grid>
</Grid>
</ContentPage>
</x:Arguments>
</NavigationPage>
</FlyoutPage.Flyout>
<FlyoutPage.Detail>
<NavigationPage>
<x:Arguments>
<ContentPage>
<Grid></Grid>
</ContentPage>
</x:Arguments>
</NavigationPage>
</FlyoutPage.Detail>
I'm navigating to the detail pages using the following format:
await NavigationService.NavigateAsync($"{nameof(NavigationPage)}/{SelectedMainMenuItem.PageName}", null, SelectedMainMenuItem.IsModal, SelectedMainMenuItem.IsAnimated);
I noticed in a lot of the samples, the MasterDetailPage.Master node is in a ContentPage not a NavigationPage node like I'm doing. Is that what I'm doing wrong? It looks to me like NavigationPage is a child of Master here. Is that correct? Just trying to figure out why when I go back to a previous page (by pressing the back button in the navbar) the page is disabled, meaning the navigation links no longer work. My initial root page navigation is absolute, all the rest of the Navigate commands are relative.
Note - I changed the MasterDetailPage to FlyoutPage since the naming changed in Xamarin.Forms v5. This bug was happening before I changed the name to FlyoutPage.
I don't know about FlyoutPage.Detail, but MasterDetailPage.Content should be left empty in xaml, it's filled automatically when navigating.
Also, with MasterDetailPage, I navigate to ../SomeOtherPage to add it to the stack (enable back button). Navigate to /MainPage/NavigationPage/AnotherPage to replace the stack (no back button).
So, I rolled back my code to a previous version and then, one at a time, added back in the packages and code changes, smoke testing each iteration along the way. My best guess is there was a conflict between Telerik UI for Xamarin package and the latest Xamarin Forms version but I can't be sure. I updated Xamarin Forms and Prism to latest update and it's all working fine.

Disable iOS autocomplete in Xamarin.Forms SearchBar

I have a Xamarin.Forms SearchBar to search for information on a remote server. This data is displayed in a ListView below the searchbar like the following:
Pseudocode:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage ... >
<ContentPage.Content>
<StackLayout>
<SearchBar
x:Name="SearchBar"
Placeholder="Search for a business..."
HorizontalTextAlignment="Start"
SearchCommand="{Binding PerformSearch}"
SearchCommandParameter="{Binding Text, Source={x:Reference SearchBar}}">
</SearchBar>
<ListView
x:Name="SearchResults"
ItemsSource="{Binding Path=SearchResults}"
...
>
</ListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Is it possible to disable the autocomplete/autocorrect in the keyboard? I know for a Text.Entry it is possible to set IsTextPredictionEnabled to false but I don't see a way to configure a SearchBar in a similar way.
Yes, according to the SearchBar Properties, the Keyboard for the InputView is exposed. Therefore, similar to this answer, it can be achieved in the XAML by using the x:FactoryMethod attribute:
<SearchBar
x:Name="SearchBar"
Placeholder="Search for a business..."
HorizontalTextAlignment="Start"
SearchCommand="{Binding PerformSearch}"
SearchCommandParameter="{Binding Text, Source={x:Reference SearchBar}}">
<SearchBar.Keyboard>
<Keyboard x:FactoryMethod="Create">
<x:Arguments>
<KeyboardFlags>None</KeyboardFlags>
</x:Arguments>
</Keyboard>
</SearchBar.Keyboard>
</SearchBar>

xamarin.Forms DataGrid with editor

I'm trying to embed an editor in a Xamarin.Forms.DataGrid cell using following code (inspired from this sample)
<ContentPage.Content>
<StackLayout>
<dg:DataGrid ItemsSource="{Binding Items}" SelectionEnabled="True" SelectedItem="{Binding SelectedItem}"
RowHeight="70" HeaderHeight="50" PullToRefreshCommand="{Binding RefreshCommand}" IsRefreshing="{Binding IsRefreshing}">
<dg:DataGrid.Columns>
<dg:DataGridColumn Title="Col. 1" PropertyName="Prop. 1" Width="0.7*">
<dg:DataGridColumn.CellTemplate>
<DataTemplate>
<Entry Text="{Binding}" />
</DataTemplate>
</dg:DataGridColumn.CellTemplate>
</dg:DataGridColumn>
</dg:DataGrid.Columns>
</dg:DataGrid>
</StackLayout>
</ContentPage.Content>
Now that works ok, but the edited text is aligned top-left of the cell. So I go trying to use one of the following alignment parameters:
HorizontalOptions="Center" VerticalOptions="Center"
<!-- or >
HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"
But in both cases, I end up with text centered within the cell, but now, the editor doesn't expand (it seems as if I had padding)... And the result with either Centeror CenterAndExpand is actually the same:
Could anyone help me get the desired result ?
EDIT: updated with full grid's definition
BONUS question: how to get rid of the editor's border?
You can try this:
<Entry Text="{Binding}" HorizontalTextAlignment="Center" />
Bonus answer: To get rid of border you need custom renderers.

binding a Custom control in DataTemplate

I've been tackling a problem regarding databinding in XAML for a little while now.
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Pedagouge.Views;assembly=Pedagouge"
x:Class="Pedagouge.Views.StudentGroupView">
<StackLayout>
<ListView x:Name="Students">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<local:PhotoView x:Name="Photo" Persona="{Binding}" />
<StackLayout Orientation="Vertical" HorizontalOptions="StartAndExpand" VerticalOptions="StartAndExpand">
<Label Text="{Binding FullName}" />
<Label Text="{Binding StudentIsAt.Group.Name}" />
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
I thought I had binding figured, but the problem I'm having clearly shows that I'm not.
Anyways, the problem is that for the custom control PhotoView it seems that the binding context used by {Binding} to the property Persona is the PhotoView's BindingContext, not the DataTemplate's context, as it is for the Labels a couple of rows down.
Had this been WPF, I would have just changed the binding to
Persona="{Binding DataContext,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ListBoxItem}}"
and that would probably have fixed it, but that won't work here (RelativeSource is apparently not defined).
How come? How can I make Persona bound to the DataTemplates context, as is the case with the Labels, instead?
Xamarin.Forms (up to 1.2.x) bindings always use the BindableObject.BindingContext as the context binding.
In the case of templates items, the item context is set to the template element, in this case the ViewCell, and recursively to the ViewCell.View, the StackLayout, your PhotoView, the inner StackLayout and the 2 Labels.
The fact that FullName and StudentIsAt.Group.Name works is a strong hint.
So, when you say
[...] for the custom control PhotoView it seems that the binding context used by
{Binding} to the property Persona is the PhotoView's BindingContext, not the
DataTemplate's context, [...]
it's both true and wrong.
The binding context used by {Binding} is the PhotoView's Bindingcontext, which is also the DataTemplate's context.
If the Binding does not work as you expect, it's probably because PhotoView somehow set the BindingContext to this, and prevent the {Binding} to work as expected, but I can't say without seeing PhotoView's code.

Resources