How to deal with changes to Search Bar in Xamarin Forms - xamarin.forms

I have a search bar in Xamarin.Forms, and in version 3.4 it used to look like this:
But after I updated to Xamarin.Forms 4.4.0.991477, it looks like this:
I think what happened is the searchbar decided to create an outline around it, but didn't get rid of the underline it used to have so now the bottom lines are overlaying. I've seen suggestions of using a custom renderer but that seems overkill + I wouldn't know how to get rid of an element I don't know the keyword of..
Can someone help me either get rid of the border or the underline?? I'd like to know both if possible.
Thanks! (This is the android app, running Android 9)
edit: Forgot to add code
<SearchBar Placeholder="Search"
FontSize="Medium"
HeightRequest="50"
Text="{Binding SearchText}"/>

iOS and Android implementations for SearchBar are diferent, in iOS the SearchBar doesn't have the underline, and the underline can't be remove on Android unless you use a Custom Renderer, and it's not that overkill, is quite simple actualy.
1 - Create a folder for your custom controls and create a class and extend it from SearchBar like this:
MyApp.Mobile > Create The Folder Here > SearchBarNoUnderline.cs
public class SearchBarNoUnderline : SearchBar
{
}
After this, you only need the Custom Renderer for Android.
2 - Create a folder for your Android Custom Renderers in your Android Project and create a class that extends from Android SearchBarRenderer, like this:
MyApp.Android > Create The Folder Here > SearchBarNoUnderlineRenderer.cs
using MyApp.Mobile.Controls;
using MyApp.Mobile.Droid.CustomRenderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(SearchBarNoUnderline), typeof(SearchBarNoUnderlineRenderer))]
namespace MyApp.Mobile.Droid.CustomRenderers
{
public class SearchBarNoUnderlineRenderer : SearchBarRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
{
base.OnElementChanged(e);
if (Control != null)
{
var plateId = Resources.GetIdentifier("android:id/search_plate", null, null);
var plate = Control.FindViewById(plateId);
plate.SetBackgroundColor(Android.Graphics.Color.Transparent);
}
}
}
}
And that's it! now you can use you custom SearchBar in you XAML like this:
<controls:SearchBarNoUnderline Placeholder="Search"
FontSize="Medium"
HeightRequest="50"
Text="{Binding SearchText}"/>
And don't forget to add the reference for your custom controls folder:
xmlns:controls="clr-namespace:MyApp.Mobile.Controls"
And to remove the Border of the SearchBar, i use this little trick:
<StackLayout Spacing="0" BackgroundColor="Red" Padding="0">
<controls:SearchBarNoUnderline Placeholder="Search"
FontSize="Medium"
BackgroundColor="Transparent"
HeightRequest="50"
Text="{Binding SearchText}"/>
</StackLayout>
Now your border should disapear, and you can use the parent StackLayout to define the BackgroundColor for your SearchBar

Related

Xamarin Forms (iOS) - If you use FormattedText for label with LineBreakMode = TrailTruncation, it do not add ellipses

Give a try to add a label and add a long text against the property FormattedText - and set the LineBreakMode="TailTruncation". You will find that it's not adding the expected ellipses. But if you use the "Text" property it works fine. I still need to stick to this FormattedText property as we are achieving some formatting through this. Is there any alternative way to address this linebreak issue with FormattedText -
Example: "I need to show for long sample text with ellipses like in this example..."
<Grid Padding=" 0,50,0,0" HorizontalOptions="StartAndExpand">
<Grid.RowDefinitions>
<RowDefinition Height ="*"/>
</Grid.RowDefinitions>
<StackLayout>
<Label x:Name="label" FormattedText ="testing this big text as a sample which needs to get truncated at any cost" LineBreakMode="TailTruncation"/>
</StackLayout>
</Grid>
The ellipses will appear when the content of the Label beyond it's border . I set the UnderLine in FormattedText and it works fine in my project .
NSMutableAttributedString str = new NSMutableAttributedString("I need to show for long sample text with ellipses like in this example...");
UIStringAttributes attributes = new UIStringAttributes() { UnderlineStyle = NSUnderlineStyle.Double,UnderlineColor=UIColor.Red, };
str.AddAttributes(attributes, new NSRange(0, str.Length - 1));
label.AttributedText = str;
label.LineBreakMode = UILineBreakMode.TailTruncation;
Update
in Forms you could use Custom Renderer .
Add the following class in your iOS project
using UIKit;
using Xamarin.Forms.Platform.iOS;
using Xamarin.Forms;
using xxx.iOS;
[assembly:ExportRenderer(typeof(Label),typeof(MyLabelRenderer))]
namespace xxx.iOS
{
public class MyLabelRenderer: LabelRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
if(Control!=null)
{
Control.LineBreakMode = UILineBreakMode.TailTruncation;
}
}
}
}

Xamarin forms: Top stacklayout is not visible in UI when keyboard is on

My Xaml:
<StackLayout
Orientation="Vertical">
<StackLayout
Orientation="Horizontal">
//Group labels and back arrow
</StackLayout>
<ListView>
//Message list
</ListView>
<Grid>
//Plus symbol,Editor and send icon
</Grid>
</StackLayout>
Screenshot:
Issue:
In normal screen there is a bar on the top(red circled). When click the editor on the bottom top bar is hiding. I need the top bar always on top. This issue is only in IOS part, in android this feature is working fine. How can I fix this issue?
Its an iOS feature that the UI is adjusted upwards. You can shrink (or keep) the view via a custom renderer.
Check this great explanation.
And also this peace of code for a keyboard custom renderer on iOS.
Like the picture in the question my soft keyboard always touching the Editor. So I added a custom renderer for my Editor to solve that issue. After adding that custom renderer the top bar is always sticking on the top of the page.
I used the following custom renderer to solve the keyboard overlapping issue:
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
}
});
}
}
}

How to allow for iOS status bar and iPhone X notch in Xamarin.Forms

I'm fairly new to this, so sorry if this is a dumb question. How do I get my Xamarin.Forms app to start below the status bar or the notch when applicable? I've tried using a NavigationPage, but then it started wear below the top of the screen. I've also seen a few other solutions, but I can't make it work.
Can anyone help?
Thanks!
use UseSafeArea
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
using Xamarin.Forms;
namespace iPhoneX
{
public partial class ItemsPage : ContentPage
{
public ItemsPage()
{
InitializeComponent();
On<Xamarin.Forms.PlatformConfiguration.iOS>().SetUseSafeArea(true);
}
}
}
You'll need to consider the safe area but have the background colors expand to take the full screen. So you shouldn't use
On<Xamarin.Forms.PlatformConfiguration.iOS>().SetUseSafeArea(true);
this will box your page with large empty spaces on the bottom and top edges.
Instead, you should measure the safe areas and apply it as padding to your root view.
[assembly: ResolutionGroupName("Enterprise")]
[assembly: ExportEffect(typeof(SafeAreaPaddingEffect), nameof(SafeAreaPaddingEffect))]
namespace Enterprise.iOS.Effects
{
class SafeAreaPaddingEffect : PlatformEffect
{
Thickness _padding;
protected override void OnAttached()
{
if (Element is Layout element)
{
if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
{
_padding = element.Padding;
var insets = UIApplication.SharedApplication.Windows[0].SafeAreaInsets; // Can't use KeyWindow this early
if (insets.Top > 0) // We have a notch
{
element.Padding = new Thickness(_padding.Left + insets.Left, _padding.Top + insets.Top, _padding.Right + insets.Right, _padding.Bottom);
return;
}
}
// Uses a default Padding of 20. Could use an property to modify if you wanted.
element.Padding = new Thickness(_padding.Left, _padding.Top + 20, _padding.Right, _padding.Bottom);
}
}
protected override void OnDetached()
{
if (Element is Layout element)
{
element.Padding = _padding;
}
}
}
}
then in xaml:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Enterprise.View.Features.Authentication.LoginView"
xmlns:effect="clr-namespace:Enterprise.View.Effects">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="100"/>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ContentView BackgroundColor="Green">
<ContentView.Effects>
<effect:SafeAreaPaddingEffect />
</ContentView.Effects>
<Label Text="Hello, from XamarinHelp.com" />
</ContentView>
</Grid>
</ContentPage>
ref: https://xamarinhelp.com/safeareainsets-xamarin-forms-ios/ Thank Adam, not me!
is it possible to make one thing on the screen expand all the way to the edges?
(from this answer)
Stretching elements out of the bounds of the safe area is arguably a use case in the case you provided. The bar is a mere background element and not content, as the navigation bar is, which also stretches to fill the whole screen.
Having said that, you unfortunately don't get this for free, but have to implement this by yourself. Assume you have the following XAML
<ContentPage ...>
<StackLayout>
<ContentView BackgroundColor="LightSkyBlue" HorizontalOptions="Fill" x:Name="Header">
<!-- Header -->
</ContentView>
<ContentView x:Name="Content">
<!-- Content -->
</ContentView>
</StackLayout>
</ContentPage>
In your code-behind (I would not use it like this, but to make the point it suffices. For a real application I have written a utility class, which is attached to the view and manages the insets.) you can now check for the property SafeAreaInsets being changed
class SafeAreaPage : ContentPage
{
// elided constructor
protected override void OnPropertyChanged(string propertyName)
{
if(propertyName = "SafeAreaInsets")
{
var insets = On<Xamarin.Forms.PlatformConfiguration.iOS>.GetSafeAreaInsets();
var headerInsets = insets; // Thickness is a value type
headerInsets.Bottom = 0;
var contentInsets = insets;
contentInsets.Top = 0;
Header.Padding = headerInsets;
Content.Padding = contentInsets;
}
}
}
How you set the Paddings of your views depends on your layouts, but this way you have a bit more control on how the safe area insets are used, although it is a bit fiddly.

Xamarin.Forms: ListView inside StackLayout: How to set height?

In a ContentPage I have a ListView inside a StackLayout inside a ScrollView. The ListView is populated (ItemSource is set) in the ContentPage when OnAppearing gets called and I can see that the list is populated in the emulator. The StackLayouts orientation is Vertical and below the ListView I have a Button.
My problem is that no matter how many elements the ListView has, it gets the height of 53.33. I would like the height of the ListView to match the total height of the items in it. By setting HeightRequest I can set the height of the ListView to anything I want, but since I do not know the height of the items inside the ListView the result is most often that the distance to the button below it is incorrect and therefore looks ugly. I have tried to set VerticalOptions on both the ListView and the StackLayout to Startand other settings, but this does not change the height from 53.33 (and if I try to combine using HeightRequest and Start it turns out that HeightRequest wins out).
How can I solve this?
(please excuse the cross posting from Xamarin forum)
With the new BindableLayout feature in Xamarin Forms 3.5 you can easily use the ItemsSource on StackPanel.
So, basically you can write something like this:
<StackLayout BindableLayout.ItemsSource="{Binding list}">
<BindableLayout.ItemTemplate>
<DataTemplate>
...
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
You can read more about it here: https://blog.xamarin.com/xamarin-forms-3-5-a-little-bindable-love/
The solution in my case was to put the ListView inside a StackLayout and then put that StackLayout inside the main StackLayout. Then I could set the VerticalOptions = LayoutOptions.FillAndExpand on the inner StackLayout (the one containing the ListView) with the result that the ListView got the space it needed (which of course varies depending on the data).
Here is the main code:
listView.ItemsSource = alternativeCells;
listView.ItemSelected += ListViewOnItemSelected;
var listStackLayout = new StackLayout
{
VerticalOptions = LayoutOptions.FillAndExpand,
Orientation = StackOrientation.Vertical
};
listStackLayout.Children.Add(listView);
_stackLayout.Children.Add(listStackLayout);
As you see, I added a new StackLayout with the only purpose of putting the ListView inside it. Then I put that listStackLayout inside the main _stackLayout.
See the post on this Xamarin forum post for more information
I ran into the same problem, and for me this worked like a charm:
listView.HasUnevenRows = true;
(http://developer.xamarin.com/guides/cross-platform/xamarin-forms/working-with/listview/#Display_Rows_with_Variable_Heights)
I had the same problem, and this was the only thing I did that solved for me (in XAML):
<StackLayout Orientation="Vertical"
VerticalOptions="Fill"
HorizontalOptions="StartAndExpand">
<ListView VerticalOptions="FillAndExpand"
RowHeight="<some row height>">
</ListView>
</StackLayout>
Hope it works!
Below code worked for me,
protected override void OnAppearing()
{
base.OnAppearing();
listViewOrderCloths.HeightRequest = model.ListOrderedCloths.Count*100)/2 ;
}
I had a similar struggle, with a slightly different solution. First, setting a RowHeight on the ListView seemed to be pivotal. Second, I was running into a binding + timing issue. If the ListView was displayed and had no contents, it was set to a default height (showing the empty space). If I moved away from this page and came back, the size was fine.
So my approach was to bind the ListView's visibility to the presence (or lack of) something being bound to. Then when data came back, the ListView became visible and had the proper size.
<ListView x:Name="lvSettlements" ItemsSource="{Binding RecentSettlements}" VerticalOptions="FillAndExpand" RowHeight="25" IsVisible="{Binding RecentSettlements, Converter={StaticResource nullConverter}}">
...SNIP...
</ListView>
On Android, my table created in code was leaving gaps above and below it.
This fixed it...
HeightRequest="1000000"
I think I have the hackiest solution.
The accepted answer wasn't applicable to my situation, where my ListView might be longer then the length of the display, hence it needs be placed within a ScrollView, which brings back the empty space.
They way I solved this, was to have top level StackLayout and then place the ListView in an immediate ScrollView, like the following XAML:
<?xml version="1.0" encoding="utf-8" ?>
<local:ContentPage>
<StackLayout x:Name="entirePage">
<ScrollView VerticalOptions="FillAndExpand"
Orientation="Vertical">
<ListView x:Name="listView" ItemsSource="{Binding Items}"
Margin="0">
<!-- ListView Stuff -->
</ListView>
</ScrollView>
</StackLayout><!--entirePage-->
</local:ContentPage >

Scroll horizontally in Xamarin.Forms ScrollView

I am using Xamarin.Forms and have created a ScrollView, which contains a horizontal StackLayout. I want to be able to scroll horizontally, so I set:
Orientation = ScrollOrientation.Horizontal;
But I don't get horizontal scroll. The content of the StackLayout is wider than the screen, and I see the content is being clipped at the edge.
How do I achieve horizontal scroll with Xamarin.Forms ?
This is how I got it to work
var scrollView = ScrollView
{
HorizontalOptions = LayoutOptions.Fill,
Orientation = ScrollOrientation.Horizontal,
Content = new StackLayout{
Orientation = StackOrientation.Horizontal,
Children = {}
}
};
This nuget package will work:
https://github.com/SuavePirate/DynamicStackLayout
The property Words is a list of strings:
<ScrollView Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<dynamicStackLayout:DynamicStackLayout ItemsSource="{Binding Words}" HorizontalOptions="Fill" Orientation="Horizontal" Padding="10, -0, 50, 10">
<dynamicStackLayout:DynamicStackLayout.ItemTemplate>
<DataTemplate>
<StackLayout BackgroundColor="Gray" WidthRequest="80" HeightRequest="80">
<Label Text="{Binding .}" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" VerticalTextAlignment="Center" HorizontalTextAlignment="Center" />
</StackLayout>
</DataTemplate>
</dynamicStackLayout:DynamicStackLayout.ItemTemplate>
</dynamicStackLayout:DynamicStackLayout>
</ScrollView>
I hope it helps :)
If you're using the templates in Visual Studio 2013 for Xamarin apps, the version of Xamarin.Forms is a bit outdated and does not support scrolling. To fix this, just nuget 'update-package' and this code
public class MainPage : ContentPage
{
public MainPage()
{
Label label = new Label {
Text = "This is a very long label which I expect to scroll horizontally because it's in a ScrollView.",
Font = Font.SystemFontOfSize(24),
};
this.Content = new ScrollView {
Content = label,
Orientation = ScrollOrientation.Horizontal,
};
}
}
code will work fine on android.
For iOS, the code will work as expected.
Unfortunately, at date, for WP8 there's a bug and the hack is to add a custom renderer.
using System.Windows.Controls;
using App2.WinPhone;
using Xamarin.Forms;
using Xamarin.Forms.Platform.WinPhone;
[assembly: ExportRenderer(typeof(ScrollView), typeof(FixedSVRenderer))]
namespace App2.WinPhone
{
public sealed class FixedSVRenderer : ScrollViewRenderer
{
protected override void OnModelSet()
{
base.OnModelSet();
if (Model.Orientation == ScrollOrientation.Horizontal)
{
// Enable horiz-scrolling
Control.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto;
}
}
}
}

Resources