Xamarin.Forms Picker long text overflow - xamarin

In Xamarin.Forms Picker if there's a item with very large text it is overflowing in Android. iOS is able to truncate at the end but Android is not able to.
<Picker x:Name="picker" Title="Select a monkey" TitleColor="Red">
<Picker.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>Baboon</x:String>
<x:String>Capuchin Capuchin Capuchin Capuchin Capuchin</x:String>
<x:String>Blue Monkey</x:String>
<x:String>Squirrel Monkey</x:String>
<x:String>Golden Lion Tamarin</x:String>
<x:String>Howler Monkey</x:String>
<x:String>Japanese Macaque</x:String>
</x:Array>
</Picker.ItemsSource>
</Picker>
See Image for reference.
I Guess if there's even a way to left align the items instead of center align, that would still be enough. But if there's a way to add ellipsis , that'll be the best. I've looked through the custom renderer and was not able to find anything useful.

Create CustomPickerRenderer and inherits with Xamarin.Forms.Platform.Android.AppCompat.PickerRenderer. I didn't try this but this should create new picker dialogue without upgrading xamarin forms version.
public class CustomPickerRenderer : Xamarin.Forms.Platform.Android.AppCompat.PickerRenderer
{
public CustomPickerRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
}
}

As an alternative solution as well that you can change the alignment of the Picker Item(Default is Center), which can make it left.
Customizing the Text and Style in, Custom Rendering in Android & IOS look at the example here.
Also, I found a bug related to the mentioned problem, which could not find any answer. I hope the above solution will workaround.

I tested the code and I found that the long text will not overflow on my side. I tested on the different devices by using different Android versions.
Here is the view of the code on my side:

Related

Xamarin Forms Android Autosize Label TextCompat pre android 8 doesn't autosize text

I want to utilise the auto-sizing feature of android textviews in my xamarin forms solution so that as the text length grows, the font sizes shrinks to never overflow the bounds of the label, and doesn't get truncated. I've created a custom Label control to do so and added an android custom renderer. It's not working in Android 7 and below. It is working in Android 8 and above.
According to the docs autosize support was introduced in android 8, but can be supported back to Android 4 with AppCompat.v4. However, my custom rendered label just renders the default font size in Android pre 8. It works fine in 8+ devices, the label text resizes as needed to not overflow the bounds. The accepted answer to this question with a similar issue on native android says it can be to do with not setting a width and height, I've tried setting widthrequest and heightrequest explicitly and it doesn't change anything. Also setting maxlines=1 doesn't change anything. An alternative thread suggests that custom fonts are the culprit. I created a vanilla forms solution using the default device font, and get the same effect.
My code:
internal class AutosizeLabelRenderer : LabelRenderer
{
#region constructor
public AutosizeLabelRenderer(Context context) : base(context)
{
}
#endregion
#region overridable
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
if (e.NewElement == null || !(e.NewElement is AutoSizeLabel autoLabel) || Control == null) { return; }
TextViewCompat.SetAutoSizeTextTypeUniformWithConfiguration(Control, autoLabel.AutoSizeMinTextSize,
autoLabel.AutoSizeMaxTextSize, autoLabel.AutoSizeStepGranularity, (int)ComplexUnitType.Sp);
}
#endregion
}
public class AutoSizeLabel : Label
{
public int AutoSizeMaxTextSize
{
get => (int)GetValue(AutoSizeMaxTextSizeProperty);
set => SetValue(AutoSizeMaxTextSizeProperty, value);
}
public static readonly BindableProperty AutoSizeMaxTextSizeProperty = BindableProperty.Create(
nameof(AutoSizeMaxTextSize), // the name of the bindable property
typeof(int), // the bindable property type
typeof(AutoSizeLabel)); // the default value for the property
public int AutoSizeMinTextSize
{
get => (int)GetValue(AutoSizeMinTextSizeProperty);
set => SetValue(AutoSizeMinTextSizeProperty, value);
}
public static readonly BindableProperty AutoSizeMinTextSizeProperty = BindableProperty.Create(
nameof(AutoSizeMinTextSize), // the name of the bindable property
typeof(int), // the bindable property type
typeof(AutoSizeLabel)); // the default value for the property
public int AutoSizeStepGranularity
{
get => (int)GetValue(AutoSizeStepGranularityProperty);
set => SetValue(AutoSizeStepGranularityProperty, value);
}
public static readonly BindableProperty AutoSizeStepGranularityProperty = BindableProperty.Create(
nameof(AutoSizeStepGranularity), // the name of the bindable property
typeof(int), // the bindable property type
typeof(AutoSizeLabel)); // the default value for the property
//
}
Not working: Android 7 - text does not shrink
Working as expected: Android 8 and above
Xaml for above images:
<StackLayout HeightRequest="200" WidthRequest="100">
<Label Text="Fixed width and height, sentences get longer, text should shrink" />
<controls:AutoSizeLabel
AutoSizeMaxTextSize="50"
AutoSizeMinTextSize="8"
AutoSizeStepGranularity="1"
BackgroundColor="{StaticResource Shamrock}"
HeightRequest="40"
HorizontalOptions="Start"
MaxLines="1"
Text="A small sentence"
WidthRequest="200" />
<controls:AutoSizeLabel
AutoSizeMaxTextSize="50"
AutoSizeMinTextSize="8"
AutoSizeStepGranularity="1"
BackgroundColor="{StaticResource Shamrock}"
HeightRequest="40"
HorizontalOptions="Start"
MaxLines="1"
Text="A larger sentence that shrinks"
WidthRequest="200" />
<controls:AutoSizeLabel
AutoSizeMaxTextSize="50"
AutoSizeMinTextSize="8"
AutoSizeStepGranularity="1"
BackgroundColor="{StaticResource Shamrock}"
HeightRequest="40"
HorizontalOptions="Start"
MaxLines="1"
Text="An even larger sentence that shrinks more."
WidthRequest="200" />
</StackLayout>
TextView font size changes with the size of the control, which is new in Android 8.0 (API26),therefore, compatibility issues need to be considered when using the previous version.You could change the TextView to AppCompatTextView.
Change your
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
if (e.NewElement == null || !(e.NewElement is AutoSizeLabel autoLabel) || Control == null) { return; }
AppCompatTextView appCompatTextView = new AppCompatTextView(_context);
appCompatTextView.Text = Element.Text;
appCompatTextView.SetMaxLines(1);
SetNativeControl(appCompatTextView);
TextViewCompat.SetAutoSizeTextTypeUniformWithConfiguration(Control,autoLabel.AutoSizeMinTextSize,autoLabel.AutoSizeMaxTextSize, autoLabel.AutoSizeStepGranularity, (int)ComplexUnitType.Sp);
}
Leo Zhu's answer got me most of the way there. There were a couple of extra steps I needed to take to get it fully working, so I'm posting the code as a separate answer here.
Differences between mine and Leo's answer:
Creating a new native control in scope like Leo suggested meant that it worked for a while but got disposed by the garbage collector and caused an exception when returning to the page after navigating away. To fix this I needed to override a property called ManageNativeControlLifetime to return false, and then manually manage disposing the object by overriding the dispose method and calling Control.RemoveFromParent();. This advice comes from a xamarin staff member in this thread.
Formatting and binding context are not automatically inherited when creating the new native control and need to be set manually. I needed to add those based on my needs using the android specific binding syntax. You may need to add other formatting and binding code based on your needs, I'm just doing font colour, gravity and binding context here.
I set the binding context with
appCompatTextView.SetBindingContext(autoLabel.BindingContext);
Once the binding context was set, I needed to add a new string property to my XF AutoSizeLabel class to pass in through XAML, then use it to set the binding path for the relevant property (In my case the text property). If more than one binding is required, you would need to add multiple new binding path properties for each required property. I set a specific binding like this:
appCompatTextView.SetBinding("Text", new Binding(autoLabel.TextBindingPath));
To facilitate this in my Xamarin Forms Xaml, my Xaml went from <Label Text="{Binding MyViewModelPropertyName}" /> to <controls:AutoSizeLabel TextBindingPath="MyViewModelPropertyName" />
Here's the full code of the renderer:
protected override bool ManageNativeControlLifetime => false;
protected override void Dispose(bool disposing)
{
Control.RemoveFromParent();
base.Dispose(disposing);
}
private AppCompatTextView appCompatTextView;
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
if (e.NewElement == null || !(e.NewElement is AutoSizeLabel autoLabel) || Control == null) { return; }
//v8 and above supported natively, no need for the extra stuff below.
if (DeviceInfo.Version.Major >= 8)
{
Control?.SetAutoSizeTextTypeUniformWithConfiguration(
autoLabel.AutoSizeMinTextSize,
autoLabel.AutoSizeMaxTextSize, autoLabel.AutoSizeStepGranularity,
(int)ComplexUnitType.Sp);
return;
}
appCompatTextView = new AppCompatTextView(Context);
appCompatTextView.SetTextColor(Element.TextColor.ToAndroid());
appCompatTextView.SetMaxLines(1);
appCompatTextView.Gravity = GravityFlags.Center;
appCompatTextView.SetBindingContext(autoLabel.BindingContext);
appCompatTextView.SetBinding("Text", new Binding(autoLabel.TextBindingPath));
SetNativeControl(appCompatTextView);
TextViewCompat.SetAutoSizeTextTypeUniformWithConfiguration(Control, autoLabel.AutoSizeMinTextSize, autoLabel.AutoSizeMaxTextSize, autoLabel.AutoSizeStepGranularity, (int)ComplexUnitType.Sp);
}

Complte event for Editor behaving like Unfocussed event

I am trying for saving some value in the editor after clicking on done on the keyboard for that I used the Completed event for the editor but this is calling while tapping on everywhere in the view like the Unfocused event. How to avoid that this?
<controls:CustomEditor Keyboard="Default"
ReturnKeyType="Next"
TextChanged="Comment_Changed"
Completed="OnDoneClicked"
VerticalOptions="StartAndExpand"
HorizontalOptions="FillAndExpand"
Text="{Binding QuestionComment}">
<controls:CustomEditor.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>
<OnPlatform x:TypeArguments="x:Double"
iOS="12"
Android="12"
WinPhone="30" />
</OnIdiom.Phone>
<OnIdiom.Tablet>
<OnPlatform x:TypeArguments="x:Double"
iOS="13"
Android="13"
WinPhone="40" />
</OnIdiom.Tablet>
</OnIdiom>
</controls:CustomEditor.FontSize>
</controls:CustomEditor>
CustomEditorClass is as following:
public const string ReturnKeyPropertyName = "ReturnKeyType";
public CustomEditor() { }
public static readonly BindableProperty ReturnKeyTypeProperty = BindableProperty.Create(
propertyName: ReturnKeyPropertyName,
returnType: typeof(ReturnKeyTypes),
declaringType: typeof(CustomEditor),
defaultValue: ReturnKeyTypes.Done );
public ReturnKeyTypes ReturnKeyType
{
get { return (ReturnKeyTypes)GetValue(ReturnKeyTypeProperty); }
set { SetValue(ReturnKeyTypeProperty, value); }
}
public enum ReturnKeyTypes : int
{
Default,
Go,
Google,
Join,
Next,
Route,
Search,
Send,
Yahoo,
Done,
EmergencyCall,
Continue
}
And the event is as following:
private async void OnDoneClicked(object sender, EventArgs e)
{
//some logic
}
Please see the documentation, this is the intended behavior
iOS (Unfocusing the editor or pressing "Done" triggers the event). Android / Windows Phone (Unfocusing the Editor triggers the event)
I've checked some messenger apps (iOS) and they don't have a done button on the keyboard at all, they provide a send button besides the entry control.
Depending on what you try to achieve, I'd ditch the done button on the keyboard altogether and rely on some sort of view on the UI, that is used for that purpose. This is platform dependent behavior anyway, Android does not support it at all, AFAIK. In order to get rid of the done button, you'll have to implement a custom renderer and set the InputAccessoryView of the native control to null (see here)
[assembly: ExportRenderer(typeof(CustomEditor), typeof(CustomEditorRenderer))]
namespace ProjectName.iOS
{
public class CustomEditorRenderer : EditorRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs e)
{
base.OnElementChanged(e);
this.Control.InputAccessoryView = null;
}
}
}
If you really want a button to send, you could create a derivative of Editor with a custom renderer, that creates a InputAccessoryView and raises an SendPressed event on your custom Editor when the InputAccessoryView is pressed. But remember, this is not possible for Android.

Frame goes transparent on tap

I am working on a Xamarin.Forms project with a ListView and each item has a Frame with a white background. My problem is I recently noticed when I tap the Item it makes the Frame's background white. It still shows all the other objects but the frame goes transparent.
The frame is simply
<Frame CornerRadius="10" Padding="0" Margin="10, 10, 10, 5" BackgroundColor="White">...</Frame>
Promoting above comments-answer to real-answer for readability sake:
The behavior you are seeing is specific to iOS. You can solve it by overriding the default behavior using an effect, like so:
[assembly: ResolutionGroupName("MyEffects")]
[assembly: ExportEffect(typeof(ListViewHighlightEffect), nameof(ListViewHighlightEffect))]
namespace MyProject.iOS.Effects
{
public class ListViewHighlightEffect : PlatformEffect
{
protected override void OnAttached()
{
var listView = (UIKit.UITableView)Control;
listView.AllowsSelection = false;
}
protected override void OnDetached()
{
}
}
}
Then, you can apply it in your view code-behind:
MyListView.Effects.Add(Effect.Resolve($"MyEffects.ListViewHighlightEffect"));
I did a short write-up on the full solution here
Alternatively, if you want to maintain the ability to select an item, one possible fix would be to add an ItemTapped handler in your code behind and null out
MyListView.SelectedItem = null;

Editing a editor with text and after clicking Done button the editor covers the whole page in iOS xamarin app

I have a text editor in iOS and with Done button so when i tap it after typing something for edit the editor covers the whole area of the app and unable to edit.
I want to fix the height of the editor so that will be able to edit and save it.
I have used custom renderer for this and tried setting the height and width but not working as expected
my xaml code
<controls:XEditor x:Name="Editor" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" />
my custom renderer
public class XEditorRenderer:EditorRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
Control.SelectAll(Self);
//this.Control.InputAccessoryView = null;
}
}
This is and Editor issue so i have tweaked it to creating a custom renderer and fix it

Why Augment Reality does not work well in Portrait orientation

As above, I need some help on AR for my windows phone. The below code is what I used:
Orientation : Portrait
XAML :
<Rectangle Margin="0,44,0,178">
<Rectangle.Fill>
<videoBrush x:Name="videoBrush" />
</Rectangle.Fill>
</Rectangle>
Code being XAML :
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
cam = new PhotoCamera();
videoBrush.SetSource(cam);
}
Problems:
1) I am holding the Phone Upright but the VideoBrush does not show image in portrait position. The image is like in Landscape. What do I need to do ?
Problem solved. I need to use Motion API. Thanks

Resources