How to prevent Editor to go behind the keyboard in Xamarin.Forms? - xamarin

I have a Chat app. Currently, there is an Entry control for add chat text. Now I want to provide multiline Entry, same like Whatsapp.
If user type more than one line, it should automatic wrap the text to next line.
If user click on Nextline button in mobile keyboard, it should go to next line.
Height of Entry should be automatically increase upto 3 line and it should also decrease if user remove text.
To do that I have tried to replace Entry with Editor and implement following functionality.
1- Put an Editor in place of Entry.
2- Implement a functionality that keep keyboard open until user click on Message list screen or back button.
Now I am trying to implement auto height functioanlity but when user try to type, Editor goes behind the keyboard. Can anybody please suggest me how to keep Editor open and auto size?
Current code:
XAML:
<Grid x:Name="MessageControls" RowSpacing="1" ColumnSpacing="2" Padding="5"
Grid.Row="1" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<local:ChatEditorWithPlaceholder x:Name="txtMessage" Grid.Column="0" TextChanged="EnableSend" Text="{Binding OutGoingText}"/>
<Frame x:Name="SendButton" Grid.Column="1" Margin= "0" Padding="0" HasShadow="false" HeightRequest="25"
BackgroundColor="Transparent" HorizontalOptions="FillAndExpand">
<Frame.GestureRecognizers>
<TapGestureRecognizer Tapped="SendMessage_Click" NumberOfTapsRequired="1" />
</Frame.GestureRecognizers>
<Label Text="Send" x:Name="sendButton" TextColor="#1f88b7" HeightRequest="20"
HorizontalOptions="Center" VerticalOptions="Center"/>
</Frame>
</Grid>
Editor
public class ChatEditorWithPlaceholder : Editor
{
public ChatEditorWithPlaceholder()
{
this.TextChanged += (sender, e) => { this.InvalidateMeasure(); };
}
}
Editor Renderer:
public class ChatEditorRenderer : EditorRenderer
{
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
}
}
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
if(Control != null) {
Control.ScrollEnabled = false;
}
var element = this.Element as ChatEditorWithPlaceholder;
Control.InputAccessoryView = null;
Control.ShouldEndEditing += DisableHidingKeyboard;
MessagingCenter.Subscribe<ConversationPage>(this, "FocusKeyboardStatus", (sender) =>
{
if (Control != null)
{
Control.ShouldEndEditing += EnableHidingKeyboard;
}
MessagingCenter.Unsubscribe<ConversationPage>(this, "FocusKeyboardStatus");
});
}
private bool DisableHidingKeyboard(UITextView textView)
{
return false;
}
private bool EnableHidingKeyboard(UITextView textView)
{
return true;
}
Screenshots:

Try this renderer for ios.
using System;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly:ExportRenderer( typeof(CustomEditor), typeof(CustomEditorRenderer))]
namespace YourNameSpace.iOS
{
public class CustomEditorRenderer: EditorRenderer
{
public ChatEntryRenderer()
{
UIKeyboard.Notifications.ObserveWillShow ((sender, args) => {
if (Element != null)
{
Element.Margin = new Thickness(0,0,0, args.FrameEnd.Height); //push the entry up to keyboard height when keyboard is activated
}
});
UIKeyboard.Notifications.ObserveWillHide ((sender, args) => {
if (Element != null)
{
Element.Margin = new Thickness(0); //set the margins to zero when keyboard is dismissed
}
});
}
}
}
for android add this in MainActivity
App.Current.On<Xamarin.Forms.PlatformConfiguration.Android>().
UseWindowSoftInputModeAdjust(WindowSoftInputModeAdjust.Resize);

For Ios there is one plugin. You can use that. The link is Here.
For Andorid you have to just set below code in MainActivity after LoadApplication(new App()) method.
App.Current.On<Xamarin.Forms.PlatformConfiguration.Android().
UseWindowSoftInputModeAdjust(WindowSoftInputModeAdjust.Resize);
Updated answer for iOS :
For IOS you can use the following custom renderer to solve the keyboard overlapping issue. And please removed the keyboardoverlap nuget package from the project.
using System;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using HBIClientFacingApp;
using HBIClientFacingApp.iOS;
[assembly:ExportRenderer( typeof(CustomEditor), typeof(CustomEditorRenderer))]
namespace YourNameSpace.iOS
{
public class CustomEditorRenderer: EditorRenderer
{
public ChatEntryRenderer()
{
UIKeyboard.Notifications.ObserveWillShow ((sender, args) => {
if (Element != null)
{
Element.Margin = new Thickness(0,0,0, args.FrameEnd.Height); //push the entry up to keyboard height when keyboard is activated
}
});
UIKeyboard.Notifications.ObserveWillHide ((sender, args) => {
if (Element != null)
{
Element.Margin = new Thickness(0); //set the margins to zero when keyboard is dismissed
}
});
}
}
}

You can try to change this line:
<local:ChatEditorWithPlaceholder x:Name="txtMessage" Grid.Column="0" TextChanged="EnableSend" Text="{Binding OutGoingText}"/>
For this one:
<Editor x:Name="txtMessage" Grid.Column="0" AutoSize="TextChanges" TextChanged="EnableSend" Text="{Binding OutGoingText}"/>

Related

Frame CornerRadius just bottom corners from code xamarin [duplicate]

Simple question. I need a frame with only one rounded corner, instead of all four. How can I only round one of the corners of a frame (top right in my case)?
Another way to phrase it: How can I set the cornerradius of only one corner of a frame?
The easy way is to use the Nuget PancakeView.
You can specify the CornerRadius in each vertice, achieving the desired effect:
Example:
<yummy:PancakeView BackgroundColor="Orange"CornerRadius="60,0,0,60"/>
You can read more in the official page.
Another way it to use custom render for frame.
1.Create class name CustomFrame, inherit Frame class, add BindableProperty CornerRadiusProperty in PCL.
public class CustomFrame: Frame
{
public static new readonly BindableProperty CornerRadiusProperty = BindableProperty.Create(nameof(CustomFrame), typeof(CornerRadius), typeof(CustomFrame));
public CustomFrame()
{
// MK Clearing default values (e.g. on iOS it's 5)
base.CornerRadius = 0;
}
public new CornerRadius CornerRadius
{
get => (CornerRadius)GetValue(CornerRadiusProperty);
set => SetValue(CornerRadiusProperty, value);
}
}
create CustomFrameRender in Android.
using FrameRenderer = Xamarin.Forms.Platform.Android.AppCompat.FrameRenderer;
[assembly: ExportRenderer(typeof(CustomFrame), typeof(CustomFrameRenderer))]
namespace Demo1.Droid
{
class CustomFrameRenderer : FrameRenderer
{
public CustomFrameRenderer(Context context)
: base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
{
base.OnElementChanged(e);
if (e.NewElement != null && Control != null)
{
UpdateCornerRadius();
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == nameof(CustomFrame.CornerRadius) ||
e.PropertyName == nameof(CustomFrame))
{
UpdateCornerRadius();
}
}
private void UpdateCornerRadius()
{
if (Control.Background is GradientDrawable backgroundGradient)
{
var cornerRadius = (Element as CustomFrame)?.CornerRadius;
if (!cornerRadius.HasValue)
{
return;
}
var topLeftCorner = Context.ToPixels(cornerRadius.Value.TopLeft);
var topRightCorner = Context.ToPixels(cornerRadius.Value.TopRight);
var bottomLeftCorner = Context.ToPixels(cornerRadius.Value.BottomLeft);
var bottomRightCorner = Context.ToPixels(cornerRadius.Value.BottomRight);
var cornerRadii = new[]
{
topLeftCorner,
topLeftCorner,
topRightCorner,
topRightCorner,
bottomRightCorner,
bottomRightCorner,
bottomLeftCorner,
bottomLeftCorner,
};
backgroundGradient.SetCornerRadii(cornerRadii);
}
}
}
}
3.using custonframe in forms.
<StackLayout>
<controls:CustomFrame
BackgroundColor="Red"
CornerRadius="0,30,0,0"
HeightRequest="100"
HorizontalOptions="Center"
VerticalOptions="Center"
WidthRequest="100" />
</StackLayout>
More detailed info about this, please refer to:
https://progrunning.net/customizing-corner-radius/
Use the nuget package Xamarin.Forms.PancakeView.
Look at this answer for a similar question:
https://stackoverflow.com/a/59650125/5869384
This is for UWP renderer
I've used the solutions from Cherry Bu - MSFT and changed it for UWP. In my project im using it in Android, iOS and UWP and it is working fine.
using System.ComponentModel;
using Windows.UI.Xaml.Media;
using Xamarin.Forms;
using Xamarin.Forms.Platform.UWP;
[assembly: ExportRenderer(typeof(CustomFrame), typeof(yourNamespace.UWP.CustomFrameRenderer))]
namespace yourNamespace.UWP
{
public class CustomFrameRenderer : FrameRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
{
base.OnElementChanged(e);
if (e.NewElement != null && Control != null)
{
UpdateCornerRadius();
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == nameof(CustomFrame.CornerRadius) ||
e.PropertyName == nameof(CustomFrame))
{
UpdateCornerRadius();
}
}
private void UpdateCornerRadius()
{
var radius = ((CustomFrame)this.Element).CornerRadius;
Control.CornerRadius = new Windows.UI.Xaml.CornerRadius(radius.TopLeft, radius.TopRight, radius.BottomRight, radius.BottomLeft);
}
}
}
You can use BoxView instead of Frame
<Grid Margin="10,10,80,10">
<BoxView Color="#CCE4FF"
CornerRadius="10,10,10,0"
HorizontalOptions="Fill"
VerticalOptions="Fill" />
<Grid Padding="10">
<Label Text="This is my message"
FontSize="14"
TextColor="#434343"/>
</Grid>
</Grid>
result view
simple solution i have used is to set another frame behind the rounded frame something like this
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="0.05*"/>
<RowDefinition Height="0.05*"/>
<RowDefinition Height="0.8*"/>
<RowDefinition Height="0.05*"/>
<RowDefinition Height="0.05*"/>
</Grid.RowDefinitions>
<Frame
Grid.Row="4"
Padding="0"
BackgroundColor="Green"
CornerRadius="0"/>
<Frame
Grid.Row="3"
Grid.RowSpan="2"
Padding="0"
BackgroundColor="Green"
HasShadow="True"
CornerRadius="20">
</Frame>
</Grid>

Best approach for show/hide password toggle functionality in Xamarin traditional approach

We are working on show/ hide password toggle functionality in Xamarin traditional approach. What is the best place to implement it? Is it in Xamarin.iOS &. Droid or in Xamarin.Core?
If it is in Xamarin.Core, can you let us know the process. Is it by value convertors?
Thanks in advance.
Recently, Microsoft MVP Charlin, wrote an article showing how to do this using Event Triggers in the Xamarin Forms code:
She was able to do it simply using a new ShowPasswordTriggerAction of type TriggerAction that implemented INotifyPropertyChanged.
Therein, she created a HidePassword bool property that Invoke a PropertyChanged event which changes the Source of the Icon image:
protected override void Invoke(ImageButton sender)
{
sender.Source = HidePassword ? ShowIcon : HideIcon;
HidePassword = !HidePassword;
}
Then place the Entry and ImageButton inside a layout (like a Frame or horizontally oriented LinearLayout) as shown:
<Entry Placeholder="Password"
IsPassword="{Binding Source={x:Reference ShowPasswordActualTrigger}, Path=HidePassword}"/>
<ImageButton VerticalOptions="Center"
HeightRequest="20"
HorizontalOptions="End"
Source="ic_eye_hide">
<ImageButton.Triggers>
<EventTrigger Event="Clicked">
<local:ShowPasswordTriggerAction ShowIcon="ic_eye"
HideIcon="ic_eye_hide"
x:Name="ShowPasswordActualTrigger"/>
</EventTrigger>
</ImageButton.Triggers>
</ImageButton>
We always use custom controls to show/hide password while entering the password using effects.
Android:
Create the control manually in ‘OnDrawableTouchListener’ method where, we are adding the ShowPass and HidePass icons to the entry control, changing them on the basis of user touch action and attaching it on effect invocation which will be fired when the effect is added to the control.
public class OnDrawableTouchListener : Java.Lang.Object, Android.Views.View.IOnTouchListener
{
public bool OnTouch(Android.Views.View v, MotionEvent e)
{
if (v is EditText && e.Action == MotionEventActions.Up)
{
EditText editText = (EditText)v;
if (e.RawX >= (editText.Right - editText.GetCompoundDrawables()[2].Bounds.Width()))
{
if (editText.TransformationMethod == null)
{
editText.TransformationMethod = PasswordTransformationMethod.Instance;
editText.SetCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, Resource.Drawable.ShowPass, 0);
}
else
{
editText.TransformationMethod = null;
editText.SetCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, Resource.Drawable.HidePass, 0);
}
return true;
}
}
return false;
}
}
Result:
IOS:
Create the control manually in 'ConfigureControl' method where we are adding the ShowPass and HidePassicons to the entry control, changing them on the basis of user touch action; and attaching it on effect invocation which will be fired when the effect will be added to the control.
private void ConfigureControl()
{
if (Control != null)
{
UITextField vUpdatedEntry = (UITextField)Control;
var buttonRect = UIButton.FromType(UIButtonType.Custom);
buttonRect.SetImage(new UIImage("ShowPass"), UIControlState.Normal);
buttonRect.TouchUpInside += (object sender, EventArgs e1) =>
{
if (vUpdatedEntry.SecureTextEntry)
{
vUpdatedEntry.SecureTextEntry = false;
buttonRect.SetImage(new UIImage("HidePass"), UIControlState.Normal);
}
else
{
vUpdatedEntry.SecureTextEntry = true;
buttonRect.SetImage(new UIImage("ShowPass"), UIControlState.Normal);
}
};
vUpdatedEntry.ShouldChangeCharacters += (textField, range, replacementString) =>
{
string text = vUpdatedEntry.Text;
var result = text.Substring(0, (int)range.Location) + replacementString + text.Substring((int)range.Location + (int)range.Length);
vUpdatedEntry.Text = result;
return false;
};
buttonRect.Frame = new CoreGraphics.CGRect(10.0f, 0.0f, 15.0f, 15.0f);
buttonRect.ContentMode = UIViewContentMode.Right;
UIView paddingViewRight = new UIView(new System.Drawing.RectangleF(5.0f, -5.0f, 30.0f, 18.0f));
paddingViewRight.Add(buttonRect);
paddingViewRight.ContentMode = UIViewContentMode.BottomRight;
vUpdatedEntry.LeftView = paddingViewRight;
vUpdatedEntry.LeftViewMode = UITextFieldViewMode.Always;
Control.Layer.CornerRadius = 4;
Control.Layer.BorderColor = new CoreGraphics.CGColor(255, 255, 255);
Control.Layer.MasksToBounds = true;
vUpdatedEntry.TextAlignment = UITextAlignment.Left;
}
}
Result:
For more details, please refer to the article below.
https://www.c-sharpcorner.com/article/xamarin-forms-tip-implement-show-hide-password-using-effects/
You could download the source file from GitHub for reference.
https://github.com/techierathore/ShowHidePassEx.git
You can use the PhantomLib library to do this. It has a control which allows you to have a show/hide icon for the password with examples. Just install the nuget. https://github.com/OSTUSA/PhantomLib
Your UI codes like this having a entry and image button
source to named accroding to your ui
<Frame CornerRadius="30" Background="white" Padding="0" HeightRequest="43" Margin="0,17,0,0">
<StackLayout Orientation="Horizontal">
<Entry x:Name="eLoginPassword"
Margin="15,-10,0,-15"
HorizontalOptions="FillAndExpand"
IsPassword="True"
Placeholder="Password"/>
<ImageButton
x:Name="ibToggleLoginPass"
Clicked="IbToggleLoginPass"
Source="eyeclosed"
Margin="0,0,13,0"
BackgroundColor="White"
HorizontalOptions="End"
/>
</StackLayout>
</Frame>
in C# code
// IbToggleLoginPass your defined method in xaml
//"eye" is drawable name for open eye and "eyeclosed" is drawable name for closed eye
private void IbToggleLoginPass(object sender, EventArgs e)
{
bool isPass = eLoginPassword.IsPassword;
ibToggleLoginPa`enter code here`ss.Source = isPass ? "eye" : "eyeclosed";
eLoginPassword.IsPassword = !isPass;
}
Trigger and a command
The trigger changes the icon, and the command changes the entry.
View xaml
<Grid>
<Entry Placeholder="Password" Text="{Binding Password, Mode=TwoWay}" IsPassword="{Binding IsPassword}" />
<ImageButton BackgroundColor="Transparent" WidthRequest="24" VerticalOptions="Center" TranslationY="-5" TranslationX="-10" HorizontalOptions="End"
Command="{Binding ToggleIsPassword}"
Source="eye" >
<ImageButton.Triggers>
<DataTrigger TargetType="ImageButton" Binding="{Binding IsPassword}" Value="True" >
<Setter Property="Source" Value="eye-slash" />
</DataTrigger>
</ImageButton.Triggers>
</ImageButton>
</Grid>
And in my ViewModel
private bool _IsPassword = true;
public bool IsPassword
{
get
{
return _IsPassword;
}
set
{
_IsPassword = value;
RaisePropertyChanged(() => IsPassword);
}
}
public ICommand ToggleIsPassword => new Command(() => IsPassword = !IsPassword);

Get start position of a listview scroll to end in xamarin forms wpf

I have worked on getting listview scroll position scroll to end in xamarin forms WPF application. I have tried below solution, it works in ios and android but unfortunately, it doesn't work in wpf application. Please suggest any idea to get scroll position of a listview end in xamarinforms WPF application.
Sample code you can find in below link
https://stackoverflow.com/questions/40373761/how-to-set-listview-to-start-showing-the-last-item-instead-in-xamarin-forms
If you're working with Xamarin Forms, you can create a control that extend from ListView and add methods for scrolling to top or bottom.
namespace YourAppName.Controls
{
public class CustomListView : ListView
{
public CustomListView() : this(ListViewCachingStrategy.RecycleElement)
{
}
public CustomListView(ListViewCachingStrategy cachingStrategy)
: base(cachingStrategy)
{
}
public void ScrollToFirst()
{
Device.BeginInvokeOnMainThread(() =>
{
try
{
if (ItemsSource != null && ItemsSource.Cast<object>().Count() > 0)
{
var firstItem = ItemsSource.Cast<object>().FirstOrDefault();
if (firstItem != null)
{
ScrollTo(firstItem, ScrollToPosition.Start, false);
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
});
}
public void ScrollToLast()
{
try
{
if (ItemsSource != null && ItemsSource.Cast<object>().Count() > 0)
{
var lastItem = ItemsSource.Cast<object>().LastOrDefault();
if (lastItem != null)
{
ScrollTo(lastItem, ScrollToPosition.End, false);
}
}
}
catch(Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
}
}
}
And on your xaml:
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:YourAppName.Controls"
x:Class="YourAppName.Views.CustomListViewPage">
<controls:CustomListView
x:Name="customListView"
ItemsSource="{Binding Items}"
SeparatorVisibility="None"
SelectionMode="None"
HasUnevenRows="true">
<controls:CustomListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label
FontSize="Medium"
Text="{Binding TestText}" />
</ViewCell>
</DataTemplate>
</controls:CustomListView.ItemTemplate>
</controls:CustomListView>
</ContentPage>
And on the code behind you can do something like this:
namespace YourAppName.Views
public partial class CustomListViewPage : ContentPage
{
public CustomListViewPage()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
this.customListView.ScrollToLast();
}
}

Xamarin: Binding a ContentView content not working, wrong implementation

I need help understanding how I can bind a ContentView's Content's to my Xamarin page. I have tried maybe 20 different methods and I can only get the binded contentview to render when I don't use bindings and set the property directly.
<ContentPage.Content>
<Grid VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" x:Name="GridLayout" RowSpacing="0" ColumnSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="60" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<nav:NavView Grid.Row="0" Grid.Column="0" Padding="0,0,0,0" Margin="0,0,0,0" HorizontalOptions="FillAndExpand" VerticalOptions="StartAndExpand"
HeightRequest="60" x:Name="nav"/>
<ContentView BindingContext="{Binding MainView}" Grid.Row="1" Grid.Column="0" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" />
</Grid>
</ContentPage.Content>
and then below is my code behind
public partial class DetailLayoutView : ContentPage, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(name));
}
private ContentView mainView;
public ContentView MainView
{
get { return mainView; }
set
{
mainView = value;
OnPropertyChanged("MainView");
}
}
public DetailLayoutView()
{
InitializeComponent ();
MainView = new PyxusChatView();
BindingContext = MainView;
}
}
Can someone help guide me to the right direction? I dont have the bandwidth to implement an entire MVVM refactor at this time I would just like to know how I can achieve this w/ minimal code.
DETECT WHEN A UI IS RENDERED IOS & ANDROID BELOW...
public DetailLayoutView()
{
InitializeComponent ();
MainContentArea.LayoutChanged += MainContentArea_LayoutChanged;
}
private void MainContentArea_LayoutChanged(object sender, EventArgs e)
{
Device.BeginInvokeOnMainThread(() => {
//UI Is now visible, send a msg to let everyone subscribed to "UpdateDetailView" event now that the
//detail page was changed and it is now okay to make API calls!
MessagingCenter.Send<ContentView, string>(MainView, "UpdateDetailView", "From BlePairingViewModel");
});
}
YOU HAVE SENT MSG NOW YOU CAN MAKE API CALLS & YOUR UI IS VISIBLE W/O MVVM... ALTHOUGH MVVM WOULD BE A BETTER PRACTICE TO INDIVIDUALS W/ MORE TIME TO CODE..
MessagingCenter.Subscribe<ContentView, string>("Pyx", "UpdateDetailView", (sender, arg) =>
{
if (sender == this)
{
Task.Run(async () =>
{
await OnAppearing();
});
}
});
I AM NOT ADVISING THIS AS BEST PRACTICE BUT IT DOES ANSWER THE QUESTION

HorizontalTextAlignment is ignored after navigation

I have created a simple app, where users can see more information in section, if they tab a Label, which is seen on the pictures below.
The problem is, that after navigating to AnotherPage and back, the text moves to left, even though I have set the HorizontalTextAlignment to End.
This only happens, when I have shown the section and hiding it again (by tapping on the Label twice).
The problem is illustrated on the picture below, where y is located at the left side.
My source code for showing a simple app with this problem, can be downloaded from this Dropbox link.
EDIT
Added code example
The ContentPage is as simple as
<StackLayout>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Text="Test" VerticalOptions="Start" />
<Label Text="{Binding Text}" HorizontalTextAlignment="End" TextColor="Red" HorizontalOptions="FillAndExpand" BackgroundColor="Yellow">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding ChangeVisibilityCommand}" />
</Label.GestureRecognizers>
</Label>
</Grid>
<StackLayout IsVisible="{Binding IsVisible}">
<Label Text="Text 1" />
<Label Text="Text 2" />
</StackLayout>
<Button Command="{Binding OpenAnotherPageCommand}" Text="Open Another Page" />
</StackLayout>
And the ViewModel is shown below
public class MainPageViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private ICommand _changeVisibilityCommand;
public ICommand ChangeVisibilityCommand
{
get => _changeVisibilityCommand;
set
{
if (value != _changeVisibilityCommand)
{
_changeVisibilityCommand = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ChangeVisibilityCommand)));
}
}
}
private bool _isVisible;
public bool IsVisible
{
get => _isVisible;
set
{
if (value != _isVisible)
{
_isVisible = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsVisible)));
}
}
}
private ICommand _openAnotherPageCommand;
public ICommand OpenAnotherPageCommand
{
get => _openAnotherPageCommand;
set
{
if (value != _openAnotherPageCommand)
{
_openAnotherPageCommand = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(OpenAnotherPageCommand)));
}
}
}
private string _text = "u";
public string Text
{
get => _text;
set
{
if (!string.Equals(_text, value, StringComparison.InvariantCultureIgnoreCase))
{
_text = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Text)));
}
}
}
public MainPageViewModel()
{
ChangeVisibilityCommand = new Command(() =>
{
IsVisible = !IsVisible;
Text = IsVisible ? "x" : "y";
});
OpenAnotherPageCommand = new Command(() =>
{
(Application.Current.MainPage as NavigationPage)?.PushAsync(new AnotherPage());
});
}
}
And the AnotherPage is simply showing a text for example
First of all, i wasn't able to reproduce your issue using the latest version of Xamarin. Maybe simply upgrading Xamarin (Forms) will remove the unwanted behavior in your case as well.
If not, you have two options:
1 - Use HorizontalOptions instead
HorizontalOptions="EndAndExpand"
since HorizontalOptions has a higher priority in the layout engine.
Not the most elegant way but surely the most reliable.
2 - Use DataBinding
In your model just add the following property:
public TextAlignment LabelAlignment
{
get => labelAlignment;
set
{
labelAlignment = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(LabelAlignment)));
}
}
private TextAlignment labelAlignment = TextAlignment.End;
And in your Xaml you change HorizontalAlignment to
HorizontalTextAlignment="{Binding LabelAlignment}"
That will work because after your screen has been created, the values in the model will be loaded and updated afterwards.
Change HorizontalOptions="FillAndExpand"
To HorizontalOptions="CenterAndExpand"
This will force the text to expand from the center and be placed at the end.
To force the text to the right, you can add a another column definition to your grid:
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
Place and emtpy object into the first cell, the one to the left:
number from the top (0)
number from the left (0)
and your label into the second cell, the one to the right:
number from the top (0)
number from the left (1)
The text area will adjust to the text length and will always be to the right.
I hope this information helps!
You can read more about grid in the documentation:
https://learn.microsoft.com/en-us/xamarin/android/user-interface/layouts/grid-layout

Resources