Partial text clickable in Xamarin Label - xamarin

I am looking for a way to make parts of the text of a Label in Xamarin Forms clickable, performing different functions, and ideally have different formatting. I understand that a clickable Label is straightforward and can be done something like this:
Label label = new Label { Text = "Click me" };
label.Click += () => { label.Text = "Clicked"; };
Essentially, I'm looking to mimic behavior in an app that I've done in native Android. My app must work for Android and iOS and ideally UWP. In Android, CharSequence objects can be added to a TextView (the control similar to Label). In Android, I might create a CharSequence object like this:
Runnable doSomething; //equivalent to C# Action; assuming initialized somewhere
String myString = "My Clickable String"; //the clickable string
Spannable span = Spannable.Factory.getInstance().newSpannable(myString); //this is the clickable text to add to TextView
span.setSpan(new ClickableSpan() {
#Override
public void onClick(View v) {
doSomething.run(); //perform the action
}
#Override
public void updateDrawState(TextPaint ds) {
ds.setUnderlineText(true); //make underlined
ds.setColor(Color.rgb(color.r, color.g, color.b)); //change the color
ds.setFakeBoldText(true); //make it bold
ds.setTextSize((float) largeFont); //change the text size
}
}, 0, a.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //style and function for the entire string
As you can see, it performs a function and has style attributes. I can then add this to TextView (Spannable implements Spanned which implements CharSequence). I can add multiple CharSequences in addition to non-clickable Strings as I desire.
I'm hoping for a way that I can do this in Xamarin but I haven't found a solution yet. I've considered the possibility of custom renderers though I feel that a cross-platform solution would be preferable, if possible. I'm far less familiar with iOS but it seems that it's possible to add NSMutableAttributedString objects to a UILabel to similar effect. I've also seen the FancyLabel object. I'm not quite sure how I would do this for UWP as I really didn't find much in my research.
Any assistance would be very appreciated!

Just to provide an updated answer to this using Xamarin Forms 3.4 (and over). You can use the FormattedText property of the Label. There is an in-depth overview on how to use it over in the Xamarin Docs.
Fundamentally, it works like so:
<Label
LineBreakMode="WordWrap">
<Label.FormattedText>
<FormattedString>
<Span
Text="Red Bold, "
TextColor="Red"
FontAttributes="Bold" />
<Span
Text="default, "
Style="{DynamicResource BodyStyle}">
<Span.GestureRecognizers>
<TapGestureRecognizer Command="{Binding TapCommand}" />
</Span.GestureRecognizers>
</Span>
<Span
Text="italic small."
FontAttributes="Italic"
FontSize="Small" />
</FormattedString>
</Label.FormattedText>
</Label>
In the above, tapping on the text default will execute TapCommand.

This answer may be extremely after-the-fact but may help others in the future.
What I've done and tested is to use a grid with your string split up appropriately, say you want to change the behaviour of a word:
In your ViewModel/View:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/> //for first few words
<ColumnDefinition Width="Auto"/> //the target word
<ColumnDefinition Width="Auto"/> //the rest of the string
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/> //one row for one line of text
</Grid.RowDefinitions>
<Label Grid.Column="0" Grid.ColumnSpan="1" Grid.Row="0" Text="First part of string"/>
<Label Grid.Column="1" Grid.ColumnSpan="1" Grid.Row="0" Text="TARGET-WORD">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding <YOUR_COMMAND>}"/>
</Label.GestureRecognizers>
</Label>
<Label Grid.Column="2" Grid.ColumnSpan="1" Grid.Row="0" Text="rest of the string"/>
</Grid>
This way you can isolate specific portions of grid and amend behaviour/aethetics (data triggers, styling, gestures). Remember that <Span></Span> tags do not support all the features of a <Label></Label>.
Clearly, in XAML you would need to know the position of the word, or if the string is dynamic, you could dynamically search for it and also construct your Grid in the code behind.
*Tested on Xamarin.Forms 2.3.4.247

Considering an example to answer this question which may help somebody in future,
Eg. i need to write "By signing up, you agree to the Terms of Services and Privacy Policy" with some formatting:
Terms of Services with different color and can be tapped
Privacy Policy with different color and can be tapped
Rest part of the string should be simple.
Code in XAML:
`<Label Text="By signing up, you agree to the " Grid.Row="0"/>
<Label Text="Terms of Services" TextColor="DarkOrange" WidthRequest="150"
HorizontalOptions="End" Grid.Row="0">
<Label.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped_Terms"/>
</Label.GestureRecognizers>
</Label>
<Label Text=" and " HorizontalOptions="End" Grid.Row="0" />
<Label Text="Privacy Policy" TextColor="DarkOrange" HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" Grid.Row="0">
<Label.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped_Policy"/>
</Label.GestureRecognizers>
</Label>
`
Code in C# for tapped callbacks:
private void TapGestureRecognizer_Tapped_Terms(object sender, EventArgs e)
{
Navigation.PushAsync(new TermsofServicesPage());
}
private void TapGestureRecognizer_Tapped_Policy(object sender, EventArgs e)
{
Navigation.PushAsync(new PrivacyPolicyPage());
}
Different formattings of the string can be performed and works fine for cross platforms.

Related

Xamarin Forms: Expand Button size in a Grid for larger Accessible Text

I am creating a search form with an Entry and a Button at the top. I have it all laid out well when the text is a normal size. I have implemented scaling fonts for accessibility, and now when the font is at the second to largest size the font is too big for the button and it cuts off the text.
Here is my current code. I don't require using a grid, it's just the only way I could get the entry control to fill out all of the extra space that the button isn't using. The width and height requests on the controls is the only way I could get them to look well on normal text sizing
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<controls:BorderedEntry Grid.Column="0" x:Name="queryEntry" Text="{Binding QueryString}" FontSize="{DynamicResource StandardLabelFontSize}"
BorderColor="{AppThemeBinding Light={StaticResource PrimaryColorLight}, Dark={StaticResource PrimaryColorDark}}" CompletedCommand="{Binding FindTextCommand}"
Placeholder="Search string" ReturnType="Search" ClearButtonVisibility="WhileEditing"
VerticalOptions="CenterAndExpand" HeightRequest="{OnPlatform Android=50, iOS=35}" Margin="10,15,5,5"
IsClippedToBounds="True" CornerRadius="5" AutomationId="TextSearchEntry" />
<Button Grid.Column="1" Text="Find" Command="{Binding FindTextCommand}" Margin="0,10,10,0" WidthRequest="80"
HeightRequest="35" IsEnabled="{Binding QueryString, Converter={StaticResource NonEmptyStringValue}}"
FontSize="{DynamicResource StandardLabelFontSize}" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"/>
</Grid>
Here is what it looks like on normal text sizing:
Here is what it looks like on the third to largest text size:
Here is what happens once I go to the second to, and largest text size:
And here is ideally what I would like it to look like at a minimum visual. I got this by setting the buttons width request to 100 instead of 80 (I ideally don't want this value as it doesn't look good on small text size):
The only solution I have came up with is keep track of what the text scaling is, and once it goes above 1.5, then I can increase the WidthRequest on the button control. Ideally I would just like the button to adjust to the width of the text.
EDIT 1:
I tried Jason's solution with this code here:
<StackLayout Orientation="Horizontal">
<controls:BorderedEntry Text="{Binding QueryString}" FontSize="{DynamicResource StandardLabelFontSize}"
BorderColor="{AppThemeBinding Light={StaticResource PrimaryColorLight}, Dark={StaticResource PrimaryColorDark}}" CompletedCommand="{Binding FindTextCommand}"
Placeholder="Search string" ReturnType="Search" ClearButtonVisibility="WhileEditing"
VerticalOptions="CenterAndExpand" HeightRequest="{OnPlatform Android=50, iOS=40}" Margin="10,15,5,5"
HorizontalOptions="FillAndExpand"
IsClippedToBounds="True" CornerRadius="5" AutomationId="TextSearchEntry" />
<Button Text="Find" Command="{Binding FindTextCommand}" Margin="0,10,10,0"
IsEnabled="{Binding QueryString, Converter={StaticResource NonEmptyStringValue}}"
FontSize="{DynamicResource StandardLabelFontSize}" VerticalOptions="Center"
Padding="20,0,20,0"/>
</StackLayout>
I had to leave the HeightRequest on the Entry else the entry would be a smaller height. Without the Padding on the Button it showed like so:
Adding the padding added some space to the button (I tried this instead of the WidthRequest):
So now it looks good on normal text, however once I start cranking up the text size in accessibility, it looks good on the 3rd to largest size (same as my original solution above):
But once I move it to the top two it starts pushing the button off the screen to the right:
Then I had the idea to go back to using the Grid and using Padding instead of the WidthRequest on the Button, and it displays correctly:
this works for me. As I change the FontSize of the button, the entry will adjust to accomodate
<StackLayout Padding="10,100,10,0" HeightRequest="50" Orientation="Horizontal" BackgroundColor="LightBlue">
<Entry HorizontalOptions="FillAndExpand" Placeholder="Search" />
<Button FontSize="80" Text="Find" />
</StackLayout>
After trying out Jason's suggested solution it still did not layout properly at the largest text accessibility settings (see my edits above in the original post). I tried putting a Padding="20,0,20,0" on the Button and removed the WidthRequest to add spacing around the Find text in the normal text size case and that seems to have allowed the Grid to auto-layout the width I also removed the HeightRequests on both controls. I don't like how the Find button looks as it is larger than the entry. Here's the final xaml:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<controls:BorderedEntry Grid.Column="0" Text="{Binding QueryString}" FontSize="{DynamicResource StandardLabelFontSize}"
BorderColor="{AppThemeBinding Light={StaticResource PrimaryColorLight}, Dark={StaticResource PrimaryColorDark}}" CompletedCommand="{Binding FindTextCommand}"
Placeholder="Search string" ReturnType="Search" ClearButtonVisibility="WhileEditing"
VerticalOptions="CenterAndExpand" Margin="10,15,5,5"
IsClippedToBounds="True" CornerRadius="5" AutomationId="TextSearchEntry" />
<Button Grid.Column="1" Text="Find" Command="{Binding FindTextCommand}" Margin="0,10,10,0"
IsEnabled="{Binding QueryString, Converter={StaticResource NonEmptyStringValue}}"
FontSize="{DynamicResource StandardLabelFontSize}" VerticalOptions="CenterAndExpand"
Padding="20,0,20,0"/>
</Grid>
With these visuals on normal text (iOS / Android):
And these visuals on the largest text size (iOS / Android):
To fix the issue where the Find button is taller than I really want it to be on the normal text, I added conditional Binding depending on what the font scaling is:
private void SetButtonBinding()
{
if (App.Current.CurrentFontScale < ScaleFontThreshold)
{
findButton.SetBinding(Button.HeightRequestProperty, nameof(FindViewModel.ButtonHeightRequest));
}
else
{
findButton.RemoveBinding(Button.HeightRequestProperty);
}
}
Which achieves these final visuals:
I wish there was a way to do this all through Xaml, but I can't seem to find the settings.

Yet Another Xamarin Forms Pinch Zoom

I know nothing about Xamarin, but I inherited a project. The request is to pinch and zoom on one of the forms. I followed a tutorial on pinch and zoom on an image, and that worked out fine. However I do not seem to be able to apply to a whole page. I am using the default PinchToZoomContainer from the Xamarin dev site.
Here is how I am implementing it.
Here is where the PinchZoomContainer lives
I think this exposing the name space
xmlns:utilites="clr-namespace:MSTCEvents.Views.Utilities"
And this is how I think it needs to be applied.
<ContentPage.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<controls:Header Grid.Row="0"/>
<controls:CustomScrollView Grid.Row="1">
<utilites:PinchZoomContainer>
<utilites:PinchZoomContainer.Content>
a bunch of StackLayouts follow then
</utilites:PinchZoomContainer.Content>
</utilites:PinchZoomContainer>
</controls:CustomScrollView>
Just to see if I could get something simple to work I tried to add a final stacklayout at the end and just apply the container there, with no luck.
<StackLayout>
<utilites:PinchZoomContainer>
<utilites:PinchZoomContainer.Content>
<Image Source="Images/mstcLogo.jpg" />
</utilites:PinchZoomContainer.Content>
</utilites:PinchZoomContainer>
</StackLayout>
I am using Ctrl Left Mouse Click to simulate the gestures on an emulator.
cheers
I created a simple test about it: wrap the stacklayout inside the PinchToZoomContainer,and add the pinchGesture on page's GestureRecognizers,code as following:
<ContentPage.Content>
<local:PinchToZoomContainer>
<StackLayout>
<Label Text="This is a label" ></Label>
<Grid Padding="20">
<Image Source="pic.PNG" />
</Grid>
<Label Text="This is a label" ></Label>
</StackLayout>
</local:PinchToZoomContainer>
</ContentPage.Content>
codebehind:
var pinchGesture = new PinchGestureRecognizer();
pinchGesture.PinchUpdated += (s, e) => { // Handle the pinch };
GestureRecognizers.Add(pinchGesture);

How to create a table with vertically sticky header and horizontally sticky first column using Xamarin Forms?

When displaying tabular data, I think that in some cases having an always visible header row and an always visible first column can really improve the readability and the overall usability of a table, especially if there is a lot of data in the table. The problem occurs when the table has to support both horizontal and vertical scrolling. A good example of such a table can be found from the NBA application when viewing box score of a past game. Here's an example image from the NBA Android application:
Example table from NBA mobile application
As you can clearly see from the image the header row is horizontally aligned with the actual table data and the first column is vertically aligned with the table data. I don't know whether or not it's an involuntary or a voluntary decision to prevent scrolling both horizontally and vertically with the same touch motion but that's a minor detail I don't care about.
I don't know how to implement this using Xamarin Forms. I am not interested in a closed source / paid solution since I would like to actually learn how to accomplish this by myself. I do realize that I most likely have to use custom renderers for both Android and IOS. My current idea is that I have an absolute layout where I have the following elements:
The first cell (it's stationary and the only stationary thing)
Rest of the header row inside a horizontal scrollview
First column inside a listview/stacklayout + vertical scrollview
The actual table data inside a listview + horizontal scrollview / stacklayout + horizontal and vertical scrollview
With this setup I would capture the touch event and send it to the other listviews/scrollviews, thus synchronizing the scrolling. In fact I can easily achieve the synchronized scrolling with the first column and the actual table data by setting the table data inside the same vertical scrollview as the first column. But I don't know how to synchronize the horizontal scrolling to the header row and I do believe that this can't be accomplished by clever component structure. I have tested only on Android so far that I can capture the touch event in a scrollview custom renderer's OnTouchEvent -method but I don't know how I could send this to the header row scrollview from the custom renderer.
Here is a draft XAML illustrating my approach.
<AbsoluteLayout xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
HorizontalOptions="FillAndExpand">
<ScrollView
Orientation="Horizontal"
x:Name="HeaderScrollView"
AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<!-- Skip first column, leave it empty for stationary cell -->
<Label Text="Column 1" Grid.Row="0" Grid.Column="1" />
<Label Text="Column 2" Grid.Row="0" Grid.Column="2" />
<Label Text="Column 3" Grid.Row="0" Grid.Column="3" />
<Label Text="Column 4" Grid.Row="0" Grid.Column="4" />
</Grid>
</ScrollView>
<ScrollView
x:Name="FirstColumnScrollView"
Orientation="Vertical"
AbsoluteLayout.LayoutBounds="0,50,1,1"
AbsoluteLayout.LayoutFlags="SizeProportional"
BackgroundColor="Aqua">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackLayout
Grid.Column="0"
Grid.Row="0"
BindableLayout.ItemsSource="{Binding DataSource}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
</Grid.ColumnDefinitions>
<Label Text="{Binding Column1}" Grid.Row="0" Grid.Column="0" />
</Grid>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
<ScrollView
x:Name="TableDataScrollView"
Grid.Column="1"
Grid.Row="0"
Orientation="Horizontal">
<StackLayout
BindableLayout.ItemsSource="{Binding DataSource}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<Label Text="{Binding Column2}" Grid.Row="0" Grid.Column="0" />
<Label Text="{Binding Column3}" Grid.Row="0" Grid.Column="1" />
<Label Text="{Binding Column4}" Grid.Row="0" Grid.Column="2" />
<Label Text="{Binding Column5}" Grid.Row="0" Grid.Column="3" />
</Grid>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</ScrollView>
</Grid>
</ScrollView>
<Label Text="First Column" BackgroundColor="White" AbsoluteLayout.LayoutBounds="0,0,200,50" />
</AbsoluteLayout>
As you can see the problem is that horizontal scrolling events between HeaderScrollView and TableDataScrollView are not shared and I don't know how to accomplish this in the best way possible or at all.
I do appreciate all the help and feedback with this!
What you are looking for is a DataGrid component with Frozen row and Frozen column feature. There are some third party components that would meet your requirements.
Syncfusion, Telerik and Infragistics DataGrids have the features you are looking for. Refer below links.
https://www.syncfusion.com/xamarin-ui-controls/xamarin-datagrid
https://www.telerik.com/xamarin-ui/datagrid
https://www.infragistics.com/products/xamarin/grids-and-lists/data-grid
There are few open-source DataGrid available as well. But not sure whether they have the row and column pinning features. Check the below links.
https://github.com/akgulebubekir/Xamarin.Forms.DataGrid
https://www.nuget.org/packages/Xamarin.Forms.DataGrid/
For open source, you could use Zumero DataGrid for Xamarin.Forms. It supports scrolling, both horizontal and vertical, optional top frozen header row, optional left frozen column and so on. You could download the sample code form the link below.
Zumero DataGrid for Xamarin.Forms: https://github.com/zumero/DataGrid/tree/8caf4895e2cc4362da3dbdd4735b5c6eb1d2dec4
For the sample code, if you get the error below, run as admin would be okay.
Build action 'EmbeddedResource' is not supported by one or more of the project's targets
Thanks for the help with this #Harikrishnan and #Wendy Zang - MSFT ! The Zumero DataGrid inspired me to do the motion event handling differently from the usual motion event handling flow. I basically created the following custom renderer for the AbsoluteLayout
using Android.Content;
using Android.Views;
using Test.Droid;
using Test.Views;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using View = Android.Views.View;
[assembly: ExportRenderer(typeof(StatisticsTable), typeof(StatisticsTableRenderer))]
namespace Test.Droid
{
public class StatisticsTableRenderer : ViewRenderer
{
private View _headerScrollView;
private View _tableScrollView;
private float _startX;
public StatisticsTableRenderer(Context context) : base(context)
{
}
public override bool OnInterceptTouchEvent(MotionEvent ev)
{
if (_headerScrollView == null || _tableScrollView == null)
{
// Completely dependant on the structure of XAML
_headerScrollView = GetChildAt(0);
_tableScrollView = GetChildAt(1);
}
return true;
}
public override bool OnTouchEvent(MotionEvent ev)
{
if (ev.Action == MotionEventActions.Down)
{
_startX = ev.GetX();
}
var headerScroll = false;
if (_startX > _headerScrollView.GetX())
{
headerScroll = _headerScrollView.DispatchTouchEvent(ev);
}
var tableScroll = _tableScrollView.DispatchTouchEvent(ev);
return headerScroll || tableScroll;
}
}
}
As you can see I always intercept the motion event and then manually dispatch it to the children. However that was not enough. I had to prevent HeaderScrollView from scrolling when the motion event didn't start inside of it because the TableDataScrollView wouldn't scroll if the motion event wasn't started inside of it. I also had to create custom renderers for all scrollviews in this table. TableDataScrollView and HeaderScrollView were using the same custom renderer. The only thing that custom renderer implemented was OnInterceptTouchEvent like this:
public override bool OnInterceptTouchEvent(MotionEvent ev)
{
return false;
}
I am not quite sure why this is necessary but it seems to have done the trick for me. I suppose that sometimes the HeaderScrollView would intercept the motion event and this caused the header to scroll without scrolling of the table data.
The vertical scrollview aka FirstColumnScrollView in the question's XAML had to implement motion event handling differently because it is the parent of the TableDataScrollView and we are now handling motion events in a top-to-bottom manner instead of the default Android way of bottom-to-top. This caused issues where FirstColumnScrollView would simply handle the motion event and not pass it to TableDataScrollView which would then lead to the header and actual table data to be out of sync with each other. This is why I added the following custom renderer for it
using Android.Content;
using Android.Views;
using Test.Droid;
using Test.Views;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using View = Android.Views.View;
[assembly: ExportRenderer(typeof(ChildFirstScrollView), typeof(ChildFirstScrollViewRenderer))]
namespace Test.Droid
{
public class ChildFirstScrollViewRenderer : ScrollViewRenderer
{
private View _childView;
public ChildFirstScrollViewRenderer(Context context) : base(context)
{
}
public override bool DispatchTouchEvent(MotionEvent e)
{
if (_childView == null)
{
_childView = GetChildAt(0);
}
_childView.DispatchTouchEvent(e);
return base.DispatchTouchEvent(e);
}
public override bool OnInterceptTouchEvent(MotionEvent ev)
{
return true;
}
}
}
In this ScrollView we always intercept/handle the motion event and we always send it to the child ScrollView first before handling the motion event.
I also had to do some minor adjustments to the XAML shown in the question. I set the starting X of HeaderScrollView to the width of the first column so it doesn't actually go under the static header of the first column. However this caused issues because I was unable to use width of the AbsoluteLayout (Why is it so hard in XAML?) to calculate the correct width for the HeaderScrollView. Now the width was set in a way that a part of the HeaderScrollView will always be outside of the viewport causing the last header to be never shown. So I added a "PaddingColumn" to the header grid with a width equal to the first column. I also had to add a "PaddingRow" to the FirstColumnScrollView grid for the same reason.
One other thing I had to do was to set the spacing of the grid inside FirstColumnScrollView to 0. Without that, there was this small gap from where you could start motion events that would only scroll the header and not the table data.
This is only the Android solution at the moment but I'll come back with the iOS one if I can accomplish it.

How to hide a Grid row and ListView column so that no empty space is visible?

I have two issues with Xamarin Grid and ListView.
(1) In ListView, I have seven columns. Based on a condition, the fifth and sixth columns need to be hidden in such a way that there is no blank space visible after forth column. I tried to set IsVisble=false but it shows blank space in between.
(2) Similar issue is with Grid. Inside a ContentView, I have Grid with ten rows. Based on certain condition, I want to hide rows seven and eight in such a way that the empty portion should get collapsed. User should not be able to view the empty row.
If from code-behind I try removing rows using the below code, I suspect the .XAML may crash as row numbers need to be reordered.
GridView gv = listview.View as GridView;
GridViewColumn cd = gv.Columns[0];
gv.Columns.Remove(cd);
gv.Columns.Add(cd);
For the grid problem, just be sure to use Binding to set the RowHeight dynamically.
So as soon as you want to hide those rows, you set the height to 0.
Code would look like this:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Test"
x:Class="Test.MainPage">
<StackLayout Margin="0,20,0,0">
<Grid RowSpacing="0" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="{Binding RowHeight}" />
<RowDefinition Height="{Binding RowHeight}" />
<RowDefinition Height="50" />
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<Label Text="Row 1" Grid.Row="0" HorizontalTextAlignment="Center" />
<Label Text="Row 2" Grid.Row="1" HorizontalTextAlignment="Center" />
<Label Text="Row 3" Grid.Row="2" HorizontalTextAlignment="Center" />
<Label Text="Row 4" Grid.Row="3" HorizontalTextAlignment="Center" />
<Label Text="Row 5" Grid.Row="4" HorizontalTextAlignment="Center" />
</Grid>
<Button Text="Hide rows" Clicked="OnClicked" />
</StackLayout>
</ContentPage>
public partial class MainPage : ContentPage, INotifyPropertyChanged
{
private int _rowHeight = 50;
public int RowHeight
{
get => _rowHeight;
set
{
_rowHeight = value;
OnPropertyChanged();
}
}
public MainPage()
{
InitializeComponent();
BindingContext = this;
}
private void OnClicked(object sender, System.EventArgs e)
{
RowHeight = 0;
}
}
You can set the row Height to Auto and then bind the IsVisible property of the content you want to hide.

Xamarin Forms Add Image with Text into Navigation Page Title

Does anyone know if there is a way to add an image into the title bar? So im wanting it to show the image right next to the normal title bar text (it's a logo). I can only seem to find a way to show text and i was hoping to steer clear of custom renderers unless it was worst case scenario.
This is currently what I have:
Im wanting to put a little logo directly to the left of the "News Feed" text.
I use either of these two ways in order to achieve that.
1.- Using NavigationPage.SetTitleIconImageSource as Isuru answer.
2.- Using NavigationPage.TitleView(see sample below)
<NavigationPage.TitleView>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Margin="10, 0, 0, 0" Source="{Binding TitleImage}" HorizontalOptions="CenterAndExpand" HeightRequest="25" WidthRequest="25" MinimumHeightRequest="25" MinimumWidthRequest="25" Grid.Row="0" Grid.Column="0" />
<Label Text="{Binding Title}" FontSize="Title" Grid.Row="0" Grid.Column="1" TextColor="White"/>
</Grid>
</NavigationPage.TitleView>
TitleImage and Title are BindingContext model properties.
Use 'SetTitleIcon' method in Xamarin.Forms.NavigationPage class.
public class MyPage : NavigationPage
{
public MyPage ()
{
var myContentPage = new MyContentPage (Color.White);
this.Push (myContentPage);
var s = "icon-45.png";
NavigationPage.SetTitleIcon (myContentPage, s);
}
}
for more detail go to this

Resources