How to show label to appear off the page when using StackLayout - xamarin

I have a gesture to swipe left and I would like the label to appear like it is swiped off the screen.
How does one achieve this with xamarin forms?
For example in the picture below the item selected was "Check the trains schedule London - Paris"
When the item is selected the text appears off the screen. I would like to achieve the same

Create a lovely grid with 2 columns.
EDIT This grid is going to sit inside a horizontal scrollview. This is what makes the trick to go out of the screen possible.
Now if you want to prohibit user from manually scrolling your ScrollView use a custom renderer. But this is not needed in case you want the user to be able to swipe the control left-right, it then acts rather naturally. So in XAML code below you can just use ScrollView instead of controls:DisabledScrollView.
1st column is what you have on the screen.
2nd column is what is hidden out of the screen.
First col width = ScreenSize. Define in code as it can change dynamically on screen rotation.
Second column width - at will. In your case it's small for just this TrashCan icon.
I'm managing it in the OnSize changed event for the grid, the invoked OnSizeChanged_TitleBarMain from XAML:
cNavBarSlider.ColumnDefinitions.Clear();
cNavBarSlider.ColumnDefinitions.Add(
new ColumnDefinition { Width = new GridLength(ScreenWidth, GridUnitType.Absolute) }
);
cNavBarSlider.ColumnDefinitions.Add(
new ColumnDefinition { Width = new GridLength(ScreenWidth - popupSearchOffset, GridUnitType.Absolute) }
);
//reposition scroll if needed (on screen rotation)
if (IsPopupSearchVisible)
{
await cNavBarSlider.TranslateTo(-ScreenWidth + popupSearchOffset, 0, 0, null);
}
Slide this table left-right with TranslateTo, in my case it was:
await cNavBarSlider.TranslateTo(-cTitleBarMain.Width + popupSearchOffset, 0, PopupOptionsTimeIn, Easing.CubicInOut);
Both IconSearch and Cancel hotspots (using hotspots and i want larger area to respond to touch, instead of user trying to tap some small icon or small word) invoke same method:
private bool _tapped;
//-------------------------------------------------------------
private async void OnTapped_SearchIcon(object sender, EventArgs e)
//-------------------------------------------------------------
{
if (_tapped) return;
_tapped = true;
if (!IsPopupSearchVisible) await PopupSearchShow();
else await PopupSearchHide();
_tapped = false;
}
//-------------------------------------------------------------------
public async Task PopupSearchShow()
//-------------------------------------------------------------------
{
await PopupSearchInit();
await cNavBarSlider.TranslateTo(-cTitleBarMain.Width + popupSearchOffset, 0, PopupOptionsTimeIn, Easing.CubicInOut);
IsPopupSearchVisible = true;
ControlSearchEntry.Focus();
}
//-------------------------------------------------------------------
public async Task PopupSearchHide(bool animate = true)
//-------------------------------------------------------------------
{
uint d = PopupOptionsTimeOut;
if (!animate) d = 0;
await cNavBarSlider.TranslateTo(0, 0, d, Easing.CubicInOut);
IsPopupSearchVisible = false;
}
The XAML scheme to play with:
<!-- SWIPE CONTAINER -->
<controls:DisabledScrollView Orientation="Horizontal" VerticalOptions="FillAndExpand">
<Grid
x:Name="cNavBarSlider"
ColumnSpacing="0"
HorizontalOptions="Fill">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- this in on screen, going to swipe -->
<Grid
x:Name="cTitleBarMain"
Grid.Column="0"
SizeChanged="OnSizeChanged_TitleBarMain">
<!-- put ur stuff here -->
</Grid>
<StackLayout
x:Name="cTitleBarSearch"
Grid.Column="1"
Orientation="Horizontal">
<!-- your hidden content to appear put here --!>
<StackLayout>
</Grid>
</controls:DisabledScrollView>
EDIT
Here's your code in list (Android emulator) note you can implement swipes to limit the scrolling coordinates, to make it sticky to bounds:
Listview:
<ListView
x:Name="MainList"
ItemsSource="{Binding Items}"
BackgroundColor="{StaticResource ColorListView}"
HasUnevenRows="False"
RowHeight="40"
HorizontalOptions="FillAndExpand"
IsVisible="{Binding IsOffline, Converter={StaticResource not}}"
ItemSelected="MainList_OnItemSelected"
RefreshCommand="{Binding ForceRefreshCommand}">
<ListView.SeparatorColor>
<OnPlatform
x:TypeArguments="Color"
WinPhone="{StaticResource ListSeparator}"
iOS="{StaticResource ListSeparator}" />
</ListView.SeparatorColor>
<ListView.ItemTemplate>
<DataTemplate>
<appoMobi:CellJessica/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Cell XAML:
<?xml version="1.0" encoding="utf-8" ?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="AppoMobi.CellJessica"
x:Name="MyViewCell">
<!-- SWIPE CONTAINER -->
<StackLayout SizeChanged="OnSizeChanged_TitleBarMain" x:Name="cCell" HorizontalOptions="FillAndExpand">
<ScrollView x:Name="scrollView"
Orientation="Horizontal"
VerticalOptions="FillAndExpand">
<Grid x:Name="cNavBarSlider"
ColumnSpacing="0"
HorizontalOptions="Fill">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- this in on screen, going to swipe -->
<Grid
x:Name="cTitleBarMain"
Grid.Column="0">
<!-- put ur stuff here -->
<Label x:Name="txtLabel" TextColor="Black" />
</Grid>
<StackLayout
x:Name="cTitleBarSearch"
Grid.Column="1"
Orientation="Horizontal">
<Image Source="cake" HeightRequest="35" WidthRequest="35" VerticalOptions="Center" HorizontalOptions="Start" />
</StackLayout>
</Grid>
</ScrollView>
</StackLayout>
</ViewCell>
Cell Code:
public partial class CellJessica
{
public CellJessica()
{
InitializeComponent();
}
//-------------------------------------------------------------
protected override void OnBindingContextChanged()
//-------------------------------------------------------------
{
SetupCell();
base.OnBindingContextChanged();
}
private bool first_setup = true;
//-------------------------------------------------------------
public void SetupCell()
//-------------------------------------------------------------
{
var item = BindingContext as CSalonListItemEx;
if (item == null) return;
txtLabel.Text = item.Name;
}
private bool _titlebar_changingsize = false;
private double popupSearchOffset = 0;
//-------------------------------------------------------------------
private async void OnSizeChanged_TitleBarMain(object sender, EventArgs e)
//-------------------------------------------------------------------
{
if (_titlebar_changingsize) return;
_titlebar_changingsize = true;
cNavBarSlider.ColumnDefinitions.Clear();
cNavBarSlider.ColumnDefinitions.Add(
new ColumnDefinition { Width = new GridLength(cCell.Width, GridUnitType.Absolute) }
);
cNavBarSlider.ColumnDefinitions.Add(
//new ColumnDefinition { Width = new GridLength(cCell.Width - popupSearchOffset, GridUnitType.Absolute) }
new ColumnDefinition { Width = new GridLength(40, GridUnitType.Absolute) }
);
//todo
//reposition scroll if rotated when hidden barea is shown
//if (IsPopupSearchVisible)
//{
// await cNavBarSlider.TranslateTo(-cCell.Width + 40, 0, 0, null);
//}
_titlebar_changingsize = false;
}
}

Related

Custom Renderer not Behaving on iOS as Expected

I have an Xamarin application. One of the pages I want to display an Image in a circle, rather than square. To do this I have created a custom rendered for each of the platforms following some online guidance. The classes are below; first in the (portable) project I have
public class CircleImage : Image
{
public static readonly BindableProperty BorderThicknessProperty =
BindableProperty.Create(propertyName: nameof(BorderThickness),
returnType: typeof(float),
declaringType: typeof(CircleImage),
defaultValue: 0F);
public float BorderThickness
{
get { return (float)GetValue(BorderThicknessProperty); }
set { SetValue(BorderThicknessProperty, value); }
}
public static readonly BindableProperty BorderColorProperty =
BindableProperty.Create(propertyName: nameof(BorderColor),
returnType: typeof(Color),
declaringType: typeof(CircleImage),
defaultValue: Color.White);
public Color BorderColor
{
get { return (Color)GetValue(BorderColorProperty); }
set { SetValue(BorderColorProperty, value); }
}
public static readonly BindableProperty FillColorProperty =
BindableProperty.Create(propertyName: nameof(FillColor),
returnType: typeof(Color),
declaringType: typeof(CircleImage),
defaultValue: Color.Transparent);
public Color FillColor
{
get { return (Color)GetValue(FillColorProperty); }
set { SetValue(FillColorProperty, value); }
}
}
Then for Android, I have the renderer
[assembly: ExportRenderer(typeof(CircleImage), typeof(CircleImageRenderer))]
namespace GL.Droid.Renderer
{
[Preserve(AllMembers = true)]
public class CircleImageRenderer : ImageRenderer
{
#pragma warning disable CS0618 // Type or member is obsolete.
public CircleImageRenderer() : base()
#pragma warning restore CS0618 // Type or member is obsolete.
{
}
public CircleImageRenderer(Context context) : base(context) { }
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously.
public async static void Init()
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
{
var temp = DateTime.Now;
}
protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
{
base.OnElementChanged(e);
if (e.OldElement == null)
{
// Only enable hardware accelleration on lollipop.
if ((int)Build.VERSION.SdkInt < 21)
{
SetLayerType(LayerType.Software, null);
}
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == CircleImage.BorderColorProperty.PropertyName ||
e.PropertyName == CircleImage.BorderThicknessProperty.PropertyName ||
e.PropertyName == CircleImage.FillColorProperty.PropertyName)
{
Invalidate();
}
}
protected override bool DrawChild(Canvas canvas, Android.Views.View child, long drawingTime)
{
try
{
var radius = (float)Math.Min(Width, Height) / 2f;
var borderThickness = ((CircleImage)Element).BorderThickness;
var strokeWidth = 0f;
if (borderThickness > 0)
{
var logicalDensity = Android.App.Application.Context.Resources.DisplayMetrics.Density;
strokeWidth = (float)Math.Ceiling(borderThickness * logicalDensity + .5f);
}
radius -= strokeWidth / 2f;
var path = new Path();
path.AddCircle(Width / 2.0f, Height / 2.0f, radius, Path.Direction.Ccw);
canvas.Save();
canvas.ClipPath(path);
var paint = new Paint
{
AntiAlias = true
};
paint.SetStyle(Paint.Style.Fill);
paint.Color = ((CircleImage)Element).FillColor.ToAndroid();
canvas.DrawPath(path, paint);
paint.Dispose();
var result = base.DrawChild(canvas, child, drawingTime);
path.Dispose();
canvas.Restore();
path = new Path();
path.AddCircle(Width / 2f, Height / 2f, radius, Path.Direction.Ccw);
if (strokeWidth > 0.0f)
{
paint = new Paint
{
AntiAlias = true,
StrokeWidth = strokeWidth
};
paint.SetStyle(Paint.Style.Stroke);
paint.Color = ((CircleImage)Element).BorderColor.ToAndroid();
canvas.DrawPath(path, paint);
paint.Dispose();
}
path.Dispose();
return result;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("Unable to create circle image: " + ex);
}
return base.DrawChild(canvas, child, drawingTime);
}
}
}
This works great and gives me the following looking layout
Now for the iOS and where the problem lies, we have the following, this (as far as I can tell), matches the Android implementation which is below
[assembly: ExportRenderer(typeof(CircleImage), typeof(CircleImageRenderer))]
namespace GL.iOS.Renderer
{
[Preserve(AllMembers = true)]
public class CircleImageRenderer : ImageRenderer
{
#pragma warning disable CS0108 // Member hides inherited member; missing new keyword
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
public async static void Init()
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
#pragma warning restore CS0108 // Member hides inherited member; missing new keyword
{
var temp = DateTime.Now;
}
protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
{
base.OnElementChanged(e);
if (Element == null)
return;
CreateCircle();
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == VisualElement.HeightProperty.PropertyName ||
e.PropertyName == VisualElement.WidthProperty.PropertyName ||
e.PropertyName == CircleImage.BorderColorProperty.PropertyName ||
e.PropertyName == CircleImage.BorderThicknessProperty.PropertyName ||
e.PropertyName == CircleImage.FillColorProperty.PropertyName)
{
CreateCircle();
}
}
private void CreateCircle()
{
try
{
var min = Math.Min(Element.Width, Element.Height);
Control.Layer.CornerRadius = (nfloat)(min / 2.0);
Control.Layer.MasksToBounds = false;
Control.BackgroundColor = ((CircleImage)Element).FillColor.ToUIColor();
Control.ClipsToBounds = true;
var borderThickness = ((CircleImage)Element).BorderThickness;
// Remove previously added layers.
var tempLayer = Control.Layer.Sublayers?
.Where(p => p.Name == borderName)
.FirstOrDefault();
tempLayer?.RemoveFromSuperLayer();
var externalBorder = new CALayer();
externalBorder.Name = borderName;
externalBorder.CornerRadius = Control.Layer.CornerRadius;
externalBorder.Frame = new CGRect(-.5, -.5, min + 1, min + 1);
externalBorder.BorderColor = ((CircleImage)Element).BorderColor.ToCGColor();
externalBorder.BorderWidth = ((CircleImage)Element).BorderThickness;
Control.Layer.AddSublayer(externalBorder);
}
catch (Exception ex)
{
Debug.WriteLine("Unable to create circle image: " + ex);
}
}
const string borderName = "borderLayerName";
}
}
But this gives me the rendered output of
My XAML is
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage x:Class="GL.ProfilePage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
Title="Generation London"
xmlns:local="clr-namespace:GL;assembly=GL"
xmlns:Controls="clr-namespace:GL.Controls"
xmlns:Converters="clr-namespace:GL.Converters"
BackgroundColor="White">
<ContentPage.Resources>
<ResourceDictionary>
<Converters:ResizingImageConverter x:Key="ResizingImageConverter"/>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<ScrollView>
<Grid ColumnSpacing="0" RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image Aspect="AspectFill"
Source="login_background.jpg" />
<Image Aspect="Fill"
Margin="0,-1,0,-1"
Source="curved_mask.png"
VerticalOptions="End" />
<Controls:CircleImage BorderThickness="2"
BorderColor="{x:Static local:Settings.LightPurple}"
WidthRequest="100"
HeightRequest="100"
TranslationY="50"
HorizontalOptions="FillAndExpand"
VerticalOptions="End"
Source="{Binding ProfilePicture, Converter={StaticResource ResizingImageConverter}}">
<!--<Image.Source>
<UriImageSource Uri="{Binding ProfilePicture}" CacheValidity="90"/>
</Image.Source>-->
</Controls:CircleImage>
<StackLayout Grid.Row="1" Padding="0,50,0,00" HorizontalOptions="Center">
<Label x:Name="fullName" Style="{StaticResource MainLabel}"/>
<Label Margin="0,-5" Style="{StaticResource SubLabel}" Text="{Binding Occupation}" />
</StackLayout>
<Grid Grid.Row="2" Margin="0,30" ColumnSpacing="0" RowSpacing="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackLayout>
<Label Style="{StaticResource ValueLabel}" Text="{Binding DateOfBirth, StringFormat='{0:dd/MM/yyyy}'}"/>
<Label Style="{StaticResource CaptionLabel}" Text="DOB"/>
</StackLayout>
<StackLayout Grid.Column="1">
<Label x:Name="workTubeStation" Style="{StaticResource ValueLabel}"/>
<Label Style="{StaticResource CaptionLabel}" Text="Nearest Tube"/>
</StackLayout>
<StackLayout Grid.Column="2">
<Label x:Name="gender" Style="{StaticResource ValueLabel}"/>
<Label Style="{StaticResource CaptionLabel}" Text="Gender"/>
</StackLayout>
</Grid>
<Grid Grid.Row="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="1"
Margin="0,-5"
Text="Interests"
Style="{StaticResource MainLabel}"/>
</Grid>
<ContentView Grid.Row="4" Padding="5">
<ListView x:Name="userInterests"
RowHeight="35"
ItemsSource="{Binding Interests}"
ItemTapped="NoOpInterestSelected"
HorizontalOptions="Center"
SeparatorVisibility="None">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Label Text="{Binding .}"
Style="{StaticResource ValueLabel}"
HorizontalTextAlignment="Center"
YAlign="Center" />
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentView>
<Button Grid.Row="5"
Margin="20"
Style="{StaticResource EditButton}"
Clicked="OnEditProfile"
Text="Edit"/>
</Grid>
</ScrollView>
</ContentPage.Content>
</ContentPage>
Q. Why is the circle container not being rendered correctly?
Thanks for your time.
You haven't shown your XAML, but based on your renderer and your output it seems that the Image is covering not just the photo part, but rather the whole screen width, which makes your code work (corner radius and drawing the ellipse) to appear on the unexpected parts and eventually results in what you have shown. The renderer code expects that the Image control has no transparent parts (e.g. that it uses AspectFill)
If you want to set the Rounded Corner for the control ,Refer the following code
...
Control.Layer.MasksToBounds = true;
Control.Layer.CornerRadius = (nfloat)(min / 2.0);
Control.Layer.BorderColor = ((CircleImage)Element).BorderColor.ToCGColor();
Control.Layer.BorderWidth = ((CircleImage)Element).BorderThickness;;
...
You don't need to add a new sublayer on layer.If you do want to do it.Refer to this similar issue.

Xamarin | Custom control

I'm pretty new in Xamarin development.
I spent about a week building my custom Button control.
But unfortually I can do something good;
This is have look like:
I need a gradient backgound (I already have a simple button with a gradient background, so I know (more or less) how to achieve this)
I tried to use a frame - but cannot get a good ellipse
I trried to call "NeedToDisplay" on iOS custom renderer, but the background is not changed to the new gradient
I dont know how to add the circle inside the ellipse and how to set to circle image (And I need be change the color of the image - I have PNG icons)
I'd be very grateful if you could help me to build this control.
Instead of adding a custom button , you can achieve the same by building a button like component in xamarin.forms.
You can build it using stack layout and image and label.
<StackLayout>
<Frame CornerRadius="30" BackgroundColor="Coral" HorizontalOptions="CenterAndExpand" VerticalOptions="End" Margin="0,0,0,20" HeightRequest="50" WidthRequest="300" OutlineColor="Red" HasShadow="True" Padding="8" >
<Grid VerticalOptions="FillAndExpand" RowSpacing="0" BackgroundColor="Coral">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" BackgroundColor="Transparent">
<local:GradientColor StartColor="#97ef97" EndColor="#3e663e">
<StackLayout Orientation="Horizontal" Padding="0" BackgroundColor="Transparent">
<Image Source="Sound.png" Margin="5,0,0,0" WidthRequest="40" HeightRequest="40" HorizontalOptions="Center" VerticalOptions="Center"></Image>
<Label Text="Button" FontSize="20" TextColor="Black" HorizontalOptions="End"
VerticalOptions="CenterAndExpand" />
<Frame.GestureRecognizers>
<TapGestureRecognizer Tapped="OnButtonClicked"/>
</Frame.GestureRecognizers>
</StackLayout>
</local:GradientColor>
</Grid>
</Grid>
</Frame>
</StackLayout>
and for the gradient color you can use custom renderer
Custom Renderer within core
public class GradientColor : StackLayout
{
public Color StartColor
{
get;
set;
}
public Color EndColor
{
get;
set;
}
}
Custom Renderer in Android where you can paint the gradient color and give it a rounded corner
public class GradientColorRenderer : VisualElementRenderer<StackLayout>
{
private Color StartColor
{
get;
set;
}
private Color EndColor
{
get;
set;
}
protected override void DispatchDraw(global::Android.Graphics.Canvas canvas)
{
#region for Vertical Gradient
//var gradient = new Android.Graphics.LinearGradient(0, 0, 0, Height,
#endregion
#region for Horizontal Gradient
var gradient = new LinearGradient(0, 0, Width, 0,
#endregion
this.StartColor.ToAndroid(),
this.EndColor.ToAndroid(),
Android.Graphics.Shader.TileMode.Mirror);
var paint = new Android.Graphics.Paint()
{
Dither = true,
};
paint.SetShader(gradient);
RectF rect = new RectF(0, 0, 450,68);
canvas.DrawRoundRect(rect, 30, 30, paint);
base.DispatchDraw(canvas);
}
protected override void OnElementChanged(ElementChangedEventArgs<StackLayout> e)
{
base.OnElementChanged(e);
if (e.OldElement != null || Element == null)
{
return;
}
try
{
var frame = e.NewElement as GradientColor;
this.StartColor = frame.StartColor;
this.EndColor = frame.EndColor;
}
catch (Exception ex)
{
}
}
}

On long press on entry goes behind the keyboard in Xamarin.Forms in iOS?

I have chat page. Page uses StackLayout as wrapper of ListView, Entry and Button.
ChatPage.xaml
<ContentPage.Resources>
<ResourceDictionary>
<local:MessageTemplateSelector x:Key="MessageTemplateSelector" />
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout>
<ListView x:Name="MessagesListView"
ItemTemplate="{StaticResource MessageTemplateSelector}"
ItemsSource="{Binding Messages}"
HasUnevenRows="True"
IsPullToRefreshEnabled="true"
IsRefreshing="{Binding IsRefreshing}"
RefreshCommand="{Binding RefreshCommand}"
SeparatorVisibility="None"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent,Property=Height,Factor=1,Constant=0}"
/>
<Grid x:Name="MessageControls" RowSpacing="1" ColumnSpacing="2" Padding="5"
BackgroundColor="#EFEFF4"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Entry x:Name="txtMessage" Grid.Column="0" HeightRequest="40" Placeholder="Message" Text="{Binding OutGoingText}" TextChanged="EnableSend"/>
<Button x:Name="sendButton" Grid.Column="1" Text="Send" Command="{Binding SendCommand}"/>
</Grid>
</StackLayout>
</ContentPage.Content>
Issue: When I click on Entry, keyboard covers Entry. So, I have handled keyboard appear/disappear event to manage entry visibility as following code. It is working fine except this case. Whenever user long press on entry(most probable to cop/paste), entry goes down/behind the keyboard.
Can anybody please suggest me?
[assembly: ExportRenderer (typeof (ChatPage), typeof (KeyboardAdaptedPageRenderer))]
namespace Project.iOS
{
public class KeyboardAdaptedPageRenderer : ContentPageWithCustomBackButtonRenderer
{
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
Keyboard.WillShow += OnKeyboardWillShow;
Keyboard.WillHide += OnKeyboardWillHide;
}
public override void ViewDidDisappear(bool animated)
{
base.ViewDidDisappear(animated);
Keyboard.WillShow -= OnKeyboardWillShow;
Keyboard.WillHide -= OnKeyboardWillHide;
OnKeyboardWillHide(new KeyboardInfo()
{
AnimationDuration = 0,
AnimatonOptions = UIViewAnimationOptions.TransitionNone
});
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
Keyboard.WillShow -= OnKeyboardWillShow;
Keyboard.WillHide -= OnKeyboardWillHide;
}
private void OnKeyboardWillShow(KeyboardInfo info)
{
if (info.SoftwareKeyboardIsVisible)
{
UIView.Animate(info.AnimationDuration, 0, info.AnimatonOptions, () =>
{
var bounds = View.Bounds;
bounds.Y = info.BeginRect.Top - info.EndRect.Top; // iphone 4 and others
View.Bounds = bounds;
}, null);
}
}
private void OnKeyboardWillHide(KeyboardInfo info)
{
UIView.Animate(info.AnimationDuration, 0, info.AnimatonOptions, () =>
{
var bounds = View.Bounds;
bounds.Y = 0;
View.Bounds = bounds;
}, null);
}
}
}

Xamarin Forms XAML Label Rotation

I've got a problem with Xamarin.Forms and Label.
I'm trying to set a label on a grid column.
The first image here shows the expected result, which is written in AXML on Android.
The second image here is written in XAML in Xamarin.Forms.
The code in the XAML file is as follows:
<Grid
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="400*"/>
<ColumnDefinition Width="75*"/>
</Grid.ColumnDefinitions>
<WebView Source="{Binding ContentSource}" />
<!--<ProgressBar IsVisible="{Binding IsLoading}"
Progress="{Binding Progress}"/>-->
<Grid Grid.Column="1"
BackgroundColor="#EE7F00"
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand">
<Label
Text="{Binding DocumentIndex}"
LineBreakMode="NoWrap"
HorizontalOptions="Center"
Rotation="-90"
VerticalOptions="Center" />
</Grid>
</Grid>
How can I expand the height or width of the label to equal to the text length?
Thank you so far
Remove the Grid container for label and place a Box view instead, and set the Grid Row and Column same for both the box view and label. like this
<Grid
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80*"/>
<ColumnDefinition Width="20*"/>
</Grid.ColumnDefinitions>
<WebView Grid.Row="0" Grid.Column="0" Source="{Binding ContentSource}" />
<!--<ProgressBar IsVisible="{Binding IsLoading}"
Progress="{Binding Progress}"/>-->
<BoxView Grid.Row="0" Grid.Column="1" BackgroundColor="#EE7F00" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"/>
<Label Grid.Row="0" Grid.Column="1" Text="{Binding DocumentIndex}"
LineBreakMode="NoWrap"
HorizontalOptions="Center"
Rotation="-90"
VerticalOptions="Center" />
</Grid>
I hope this will solve your label length problem after rotation.
I solved this with a custom renderer. In your Forms project:
public class RotatedText : View
{
public static BindableProperty TitleValueProperty = BindableProperty.Create(nameof(TitleValue), typeof(string), typeof(string), null, BindingMode.TwoWay, null,
(bindable, oldValue, newValue) =>
{
});
public string TitleValue
{
get => (string)GetValue(TitleValueProperty);
set => SetValue(TitleValueProperty, value);
}
}
And in your Android project:
[assembly: ExportRenderer(typeof(RotatedText), typeof(RotatedTextRenderer))]
namespace MyNamespace
{
public class RotatedTextRenderer : ViewRenderer
{
private Context _context;
public RotatedTextRenderer(Context c) : base(c)
{
_context = c;
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.View> e)
{
base.OnElementChanged(e);
if (e.NewElement is RotatedText)
{
string title = ((RotatedText)e.NewElement).TitleValue;
SetNativeControl(new RotatedTextView(_context, title));
}
}
}
public class RotatedTextView : Android.Views.View
{
private int DEFAULT_TEXT_SIZE = 30;
private string _text;
private TextPaint _textPaint;
public RotatedTextView(Context c, string title) : base(c)
{
_text = title;
initLabelView();
}
private void initLabelView()
{
this._textPaint = new TextPaint();
this._textPaint.AntiAlias = true;
this._textPaint.TextAlign = Paint.Align.Center;
this._textPaint.TextSize = DEFAULT_TEXT_SIZE;
this._textPaint.Color = new Android.Graphics.Color(0, 0, 0);
}
public override void Draw(Canvas canvas)
{
base.Draw(canvas);
if (!string.IsNullOrEmpty(this._text))
{
float x = (Width / 2) - DEFAULT_TEXT_SIZE/3;
float y = (Height / 2);
canvas.Rotate(90);
canvas.DrawText(this._text, y, -x, this._textPaint);
}
}
}
}
Then just set TitleValue where ever you use RotatedText. It's a little ugly but I couldn't find a better way.

Xamarin.Forms Listview with 2 line label?

I'm converting a MonoTouch.Dialog app to Xamarin.Forms.
I have a Cell in a ListView and that has a "Detail" line that should be 2 lines long, and should truncate the tail after that.
var lblDetail = new Label
{
LineBreakMode = LineBreakMode.TailTruncation,
Text = "a really long string that should show 2 lines then ..."
};
How do I set something like, "Lines = 2"
I solved this with a custom renderer
This is my xaml (in my pcl project)
<ListView ItemsSource="{Binding Cards}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Source="{Binding Icon}"/>
<customRenderers:MultiLineLabel Text="{Binding Summary}"
Grid.Column="1"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This is my MultiLineLabel class
public class MultiLineLabel : Label
{
}
This is the renderer for iOS:
[assembly: ExportRenderer(typeof(MultiLineLabel), typeof(MultiLineLabelRenderer))]
namespace NameSpace.iOS.Renderers
{
public class MultiLineLabelRenderer : LabelRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.LineBreakMode = UILineBreakMode.TailTruncation;
Control.Lines = 3;
}
}
}
}
the following code working fine for me :)
UserNotesListView = new ListView() {
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand,
HasUnevenRows=true,
ItemsSource=//upon your bussiess
};
DataTemplate dt=new DataTemplate(()=>
{
var LabelText = new Label();
LabelText.SetBinding(Label.TextProperty, new Binding("note"));
return new ViewCell { View = LabelText };
});
UserNotesListView.ItemTemplate = dt;
Content = new StackLayout
{
HorizontalOptions=LayoutOptions.FillAndExpand,
Children=
{
UserNotesListView
}
};
You might want to customize ListView:
<ListView x:Name="ListMenu" ItemsSource="{Binding _YourItems_}" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding _YourText_}" TextColor="Black" />
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
even if text not fit one line, it will wrap automatically.

Resources