Aligning Image with Entry in Xamarin Forms - xamarin

In the following Xamarin Forms code, I am trying to align Image with Entry to create visual appearance like a bootstrap input-group as explained in Bootstrap input group addon alignment problems
But it has following shortcomings:
The image takes more width and height than specified HeightRequest and WidthRequest
There is unwanted space between Image and Entry
How to fix this?
XAML
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MyHomeScreen2;assembly=MyHomeScreen2"
x:Class="MyHomeScreen2.InputFormTest"
NavigationPage.HasNavigationBar="False">
<ContentPage.Content>
<Grid x:Name="inputGrid" Grid.Row="1" ColumnSpacing="0" RowSpacing="0" Padding="0" BackgroundColor="#606060">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label x:Name="lblReading" TextColor="White" Text="READING" Grid.Row="0" Margin="15"></Label>
<StackLayout Grid.Row="1" Orientation="Horizontal">
<Image Source="homea.png" Aspect="AspectFit"
HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"
HeightRequest="10" WidthRequest="20"
BackgroundColor="Silver" ></Image>
<Entry x:Name="myEntry" TextColor="Black" Text="1" Keyboard="Numeric" BackgroundColor="White"
Opacity="0.9" HeightRequest="20">
</Entry>
</StackLayout>
</Grid>
</ContentPage.Content>
</ContentPage>

First things first:
XAML:
<Grid Grid.Row="1" ColumnSpacing="0"
RowSpacing="0"
Padding="0"
BackgroundColor="#606060">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0"
x:Name="lblReading"
TextColor="White"
Text="READING"
Margin="15"/>
<StackLayout Grid.Row="1"
Orientation="Horizontal"
Spacing="1"
Margin="5,0">
<Image Source="lan_connect_white_36dp"
Aspect="AspectFit"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand"
HeightRequest="40"
WidthRequest="40"
BackgroundColor="Silver"/>
<Entry x:Name="myEntry"
TextColor="Black"
Text="1"
Keyboard="Numeric"
BackgroundColor="White"
HorizontalOptions="FillAndExpand"
VerticalOptions="CenterAndExpand"
Opacity="0.9">
</Entry>
</StackLayout>
</Grid>
Result:
Explanation
The image takes more width and height than specified HeightRequest and WidthRequest
When you use a asterisk to set the Width of ColumnDefinition or the Height of RowDefinition you are saying that it should take all available space of it, and the others columns/rows will just use enough space to hold the inner view on it.
There is unwanted space between Image and Entry
Some layout containers have a default spacing between views. It is the case for GridLayout, that has a default ColumnSpacing and RowSpacing of 6.

I have done this using this blog.
In PCL: Create a class named ImageEntry
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;
namespace ImageEntry
{
public class ImageEntry1 : Entry
{
public ImageEntry1()
{
this.HeightRequest = 50;
}
public static readonly BindableProperty ImageProperty =
BindableProperty.Create(nameof(Image), typeof(string), typeof(ImageEntry1), string.Empty);
public static readonly BindableProperty LineColorProperty =
BindableProperty.Create(nameof(LineColor), typeof(Xamarin.Forms.Color), typeof(ImageEntry1), Color.White);
public static readonly BindableProperty ImageHeightProperty =
BindableProperty.Create(nameof(ImageHeight), typeof(int), typeof(ImageEntry1), 40);
public static readonly BindableProperty ImageWidthProperty =
BindableProperty.Create(nameof(ImageWidth), typeof(int), typeof(ImageEntry1), 40);
public static readonly BindableProperty ImageAlignmentProperty =
BindableProperty.Create(nameof(ImageAlignment), typeof(ImageAlignment), typeof(ImageEntry1), ImageAlignment.Left);
public Color LineColor
{
get { return (Color)GetValue(LineColorProperty); }
set { SetValue(LineColorProperty, value); }
}
public int ImageWidth
{
get { return (int)GetValue(ImageWidthProperty); }
set { SetValue(ImageWidthProperty, value); }
}
public int ImageHeight
{
get { return (int)GetValue(ImageHeightProperty); }
set { SetValue(ImageHeightProperty, value); }
}
public string Image
{
get { return (string)GetValue(ImageProperty); }
set { SetValue(ImageProperty, value); }
}
public ImageAlignment ImageAlignment
{
get { return (ImageAlignment)GetValue(ImageAlignmentProperty); }
set { SetValue(ImageAlignmentProperty, value); }
}
}
public enum ImageAlignment
{
Left,
Right
}
}
In android create a class named ImageEntryRenderer
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.Graphics;
using Android.Graphics.Drawables;
using Android.OS;
using Android.Runtime;
using Android.Support.V4.Content;
using Android.Views;
using Android.Widget;
using ImageEntry;
using ImageEntry.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(ImageEntry1), typeof(ImageEntryRenderer))]
namespace ImageEntry.Droid
{
public class ImageEntryRenderer : EntryRenderer
{
ImageEntry1 element;
public ImageEntryRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (e.OldElement != null || e.NewElement == null)
return;
element = (ImageEntry1)this.Element;
var editText = this.Control;
if (!string.IsNullOrEmpty(element.Image))
{
switch (element.ImageAlignment)
{
case ImageAlignment.Left:
editText.SetCompoundDrawablesWithIntrinsicBounds(GetDrawable(element.Image), null, null, null);
break;
case ImageAlignment.Right:
editText.SetCompoundDrawablesWithIntrinsicBounds(null, null, GetDrawable(element.Image), null);
break;
}
}
editText.CompoundDrawablePadding = 25;
Control.Background.SetColorFilter(element.LineColor.ToAndroid(), PorterDuff.Mode.SrcAtop);
}
private BitmapDrawable GetDrawable(string imageEntryImage)
{
int resID = Resources.GetIdentifier(imageEntryImage, "drawable", this.Context.PackageName);
var drawable = ContextCompat.GetDrawable(this.Context, resID);
var bitmap = ((BitmapDrawable)drawable).Bitmap;
return new BitmapDrawable(Resources, Bitmap.CreateScaledBitmap(bitmap, element.ImageWidth * 2, element.ImageHeight * 2, true));
}
}
}
In IOS create a class named ImageEntryRenderer
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using CoreAnimation;
using CoreGraphics;
using Foundation;
using ImageEntry;
using ImageEntry.iOS;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(ImageEntry1), typeof(ImageEntryRenderer))]
namespace ImageEntry.iOS
{
public class ImageEntryRenderer : EntryRenderer
{
ImageEntry1 element;
public ImageEntryRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (e.OldElement != null || e.NewElement == null)
return;
var element = (ImageEntry1)this.Element;
var textField = this.Control;
if (!string.IsNullOrEmpty(element.Image))
{
switch (element.ImageAlignment)
{
case ImageAlignment.Left:
textField.LeftViewMode = UITextFieldViewMode.Always;
textField.LeftView = GetImageView(element.Image, element.ImageHeight, element.ImageWidth);
break;
case ImageAlignment.Right:
textField.RightViewMode = UITextFieldViewMode.Always;
textField.RightView = GetImageView(element.Image, element.ImageHeight, element.ImageWidth);
break;
}
}
textField.BorderStyle = UITextBorderStyle.None;
CALayer bottomBorder = new CALayer
{
Frame = new CGRect(0.0f, element.HeightRequest - 1, this.Frame.Width, 1.0f),
BorderWidth = 2.0f,
BorderColor = element.LineColor.ToCGColor()
};
textField.Layer.AddSublayer(bottomBorder);
textField.Layer.MasksToBounds = true;
}
private UIView GetImageView(string imagePath, int height, int width)
{
var uiImageView = new UIImageView(UIImage.FromBundle(imagePath))
{
Frame = new RectangleF(0, 0, width, height)
};
UIView objLeftView = new UIView(new System.Drawing.Rectangle(0, 0, width + 10, height));
objLeftView.AddSubview(uiImageView);
return objLeftView;
}
}
}
In XAML added the entry code: MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:ImageEntry"
BackgroundColor="#2f4259"
x:Class="ImageEntry.MainPage">
<ContentPage.Content>
<ScrollView>
<StackLayout Padding="40" Spacing="10">
<local:ImageEntry1 TextColor="White"
Image="user"
Placeholder="Emil"
HorizontalOptions="FillAndExpand"/>
</StackLayout>
</ScrollView>
</ContentPage.Content>
</ContentPage>
Attached a sample project on here, happy coding :)

Related

Xamarin.Forms: GroupHeaderTemplate {Binding Key } is not working, But ItemTemplate {Binding Key} works well

I'm building a mobileApp with Xamarin forms. My problem is that I cannot get GroupHeader. I checked all platforms and websites. I couldn't find any solution for my problem. My fist listview works well and at second listview I want to show my values with groupheaders. My .xaml and .cs codes and scroonshots are below. I successfully got my all values from web service. And My values are shown at second listview except group headers.
<ListView x:Name="listview" HasUnevenRows="True" SeparatorVisibility="Default"
GroupDisplayBinding="{Binding Key}"
IsGroupingEnabled="True" ItemsSource="{Binding GroupedList}"
CachingStrategy="RecycleElement" Margin="0,0,0,15">
<ListView.GroupHeaderTemplate>
<DataTemplate>
<ViewCell Height="50" >
<Label Text ="{Binding Key}" Margin="0,20,0,0" FontSize="Medium" TextColor="Gray" />
</ViewCell>
</DataTemplate>
</ListView.GroupHeaderTemplate>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid BackgroundColor="White" Margin="0,0,0,1" >
<Grid.RowDefinitions>
<RowDefinition Height="0.4*"/>
<RowDefinition Height="0.6*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<Label Text="{Binding PayDeskName}" Grid.Row="0" HorizontalOptions="Start" VerticalTextAlignment="Center" FontAttributes="Bold" TextColor="Gray"/>
<Label Grid.Column="0" Text="{Binding CollectionType}" Grid.Row="1" HorizontalOptions="Start" VerticalTextAlignment="Center" FontAttributes="Bold" TextColor="Blue"/>
<Label Grid.Column="1" Text="{Binding CollectionAmount, StringFormat='{0:N}'}" Grid.Row="1" HorizontalOptions="End" VerticalOptions="Center" FontSize="Large" TextColor="Blue"></Label>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
--
using MunIS.Parameters;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using Xamarin.Forms.PlatformConfiguration.AndroidSpecific;
namespace MunIS.AccrumentCollection
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class CollectionPage : ContentPage
{
public CollectionPage()
{
InitializeComponent();
}
public PayDeskBaseCollectionParameters[] CollectionList { get; set; }
public ObservableCollection<Grouping<string, PayDeskBaseCollectionParameters>> GroupedList { get; set; }
public IEnumerable<IGrouping<string, PayDeskBaseCollectionParameters>> CollectionTypeBasedGroupedList { get; set; }
async void GetCollectionList(object sender, EventArgs e)
{
activityStackLayout.IsVisible = true;
activityIndicator.IsRunning = true;
loadingTxt.IsVisible = true;
string queryIntervalStartDate = dtIntervalStart.Date.ToString("MM.dd.yyyy");
string queryIntervalFinishDate = dtIntervalFinish.Date.ToString("MM.dd.yyyy");
CollectionDetailQueryCriteriaParameters criteria = new CollectionDetailQueryCriteriaParameters();
criteria.CollectionDateStart = dtIntervalStart.Date;
criteria.CollectionDateFinish = dtIntervalFinish.Date;
ServiceCaller serviceCaller = new ServiceCaller();
CollectionList = await serviceCaller.ListPayDeskBasedCollections(criteria);
var groupedCollectionList = CollectionList.GroupBy(c => c.CollectionType).Select(
g => new
{
CollectionType = g.Key,
CollectionAmount = g.Sum(s => s.CollectionAmount)
});
GroupedList = new ObservableCollection<Grouping<string, PayDeskBaseCollectionParameters>>(CollectionList.GroupBy(c => c.PayDeskName)
.Select(k => new Grouping<string, PayDeskBaseCollectionParameters>(k.Key + "\t\t" + CollectionList
.Where(c => c.PayDeskName == k.Key)
.Sum(x => x.CollectionAmount).ToString("N"), k)));
listview1.ItemsSource = groupedCollectionList;
listview.ItemsSource = GroupedList;
activityIndicator.IsRunning = false;
loadingTxt.IsVisible = false;
activityStackLayout.IsVisible = false;
}
}
}
And this is my Grouping.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
namespace MunIS
{
public class Grouping<K, T> : ObservableCollection<T>
{
public K Key { get; set; }
public Grouping(K key, IEnumerable<T> items)
{
Key = key;
foreach (var item in items)
{
this.Items.Add(item);
}
}
}
}
See my Page I want to get groupheaders
I think you need to remove GroupDisplayBinding from your listview element.
<ListView x:Name="listview" HasUnevenRows="True" SeparatorVisibility="Default"
GroupDisplayBinding="{Binding Key}"
IsGroupingEnabled="True" ItemsSource="{Binding GroupedList}"
CachingStrategy="RecycleElement" Margin="0,0,0,15">
This property is mutually exclusive with GroupHeaderTemplate property.
Setting it will set GroupHeaderTemplate to null.
From the docs: https://learn.microsoft.com/en-us/dotnet/api/xamarin.forms.listview.groupdisplaybinding?view=xamarin-forms#remarks
This worked for me
remove the x:DataType attribute from the XAML. For some reason doing this for the overall page creates problems with templated controls that have their own context.
And as Jeroen Corteville said, you need to remove the GroupDisplayBinding aswell. The two do the same, but somehow works differently.

Xamarin custom renderer take photo

I want to take photo using Xamarin form custom renderer.
I use Custom Renderer Sample and I add a Button named 'btnTakePicture' in Xaml , but I don't have any idea how to take photo on Button click event.
I want to show camera in part of screen.
Also I checked Xam.Media.Plugin and I couldn't take photo.
Xaml Code
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer"
x:Class="CustomRenderer.frmCamera"
Title="Main Page">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="7*"/>
<RowDefinition Height="2*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Label Text="Camera Preview"/>
</Grid>
<Grid Grid.Row="1">
<local:CameraPreview Camera="Rear" x:Name="cmrPreview"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand" Background="black"/>
</Grid>
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Button x:Name="btnChangeCamera" Text="Switch Camera" Clicked="btnChangeCamera_Clicked"/>
<Button x:Name="btnTakePicture" Text="Take" Grid.Column="1" Clicked="btnTakePicture_Clicked"/>
</Grid>
<Frame></Frame>
</Grid>
</ContentPage>
C# Code
using System.IO;
using Xamarin.Essentials;
using Xamarin.Forms;
namespace CustomRenderer
{
public partial class frmCamera : ContentPage
{
public frmCamera()
{
InitializeComponent();
}
private void btnChangeCamera_Clicked(object sender, System.EventArgs e)
{
if (cmrPreview.Camera == CameraOptions.Rear)
cmrPreview.Camera = CameraOptions.Front;
else
cmrPreview.Camera = CameraOptions.Rear;
}
bool blnIsFlashLight = false;
private void btnFlashLight_Clicked(object sender, System.EventArgs e)
{
if (blnIsFlashLight)
{
Flashlight.TurnOffAsync();
blnIsFlashLight = false;
}
else
{
Flashlight.TurnOnAsync();
blnIsFlashLight = true;
}
}
private void btnTakePhoto_Clicked(object sender, System.EventArgs e)
{
}
}
}
here is my project Screenshot

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 Forms: System.NullReferenceException in AppDelegate

Hi tried to compile my app for the first time for IOS but I got this:
System.NullReferenceException: Object reference not set to an instance of an object
in the AppDelegate.
It stops in this line :return base.FinishedLaunching(app, options);
I'm relative new into xamarin so I am sorry for my unknowingness.
Here is my complete AppDelegate:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
using System.Globalization;
using System.IO;
using Flex;
using Foundation;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using System.Xml.Linq;
namespace dpsApp.iOS
{
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
Rg.Plugins.Popup.Popup.Init();
FlexButton.Init();
LoadApplication(new App());
return base.FinishedLaunching(app, options);
}
}
}
So here is my MainPage XAML:
`
<StackLayout WidthRequest="10000">
<StackLayout x:Name="LogoStack" BackgroundColor="White">
<Image x:Name="Image"
Source="a.png"
HeightRequest="120"
WidthRequest="120"
HorizontalOptions="Center">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="EasterCount"/>
</Image.GestureRecognizers>
</Image>
</StackLayout>
<StackLayout x:Name="StackList" IsVisible="True" HeightRequest="3000">
<ListView x:Name="PageList"
HasUnevenRows="True"
ItemTapped="Link_ItemTapped"
HeightRequest="25"
BackgroundColor="White">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal" Margin="0,0,0,0" BackgroundColor="#e7e7e7" HeightRequest="65" Padding="0">
<Grid x:Name="DeleteStack" VerticalOptions="CenterAndExpand" BackgroundColor="White" HorizontalOptions="FillAndExpand" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="50" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="28" />
<RowDefinition Height="22" />
<RowDefinition Height="1" />
</Grid.RowDefinitions>
<Label Grid.Column="0" Grid.Row="0" Text="{Binding Title}" LineBreakMode="TailTruncation" FontSize="25" Margin="20,0,0,0"/>
<Label Grid.Column="0" Grid.Row="1" Text="{Binding Link}" LineBreakMode="TailTruncation" FontSize="17" Margin="20,0,0,0"/>
<Image Margin="0,0,20,0"
IsVisible="{Binding IsVisible}"
Grid.Column="1"
Grid.Row="0"
Grid.RowSpan="2"
x:Name="DeleteButton"
Source="delete.png"
VerticalOptions="Center"
HeightRequest="20"
HorizontalOptions="Center"/>
</Grid>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
<StackLayout x:Name="FirstTimeOpenStack" HorizontalOptions="Center">
<Label Text="Please tap on the plus icon in the top right corner to add a website" />
</StackLayout>
</StackLayout>
Your App Delegate looks ok. You have an unhandled exception somewhere in the App() you are launching.
Here is one way to capture unhandled exceptions:
namespace WiFiVisualPager.iOS
{
// The UIApplicationDelegate for the application. This class is responsible for launching the
// User Interface of the application, as well as listening (and optionally responding) to
// application events from iOS.
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
//
// This method is invoked when the application has loaded and is ready to run. In this
// method you should instantiate the window, load the UI into it and then make the window
// visible.
//
// You have 17 seconds to return from this method, or iOS will terminate your application.
//
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;
TaskScheduler.UnobservedTaskException += TaskSchedulerOnUnobservedTaskException;
global::Xamarin.Forms.Forms.Init();
DisplayCrashReport();
LoadApplication(new App());
return base.FinishedLaunching(app, options);
}
#region [Error handling]
//Credit: Peter Norman.
//https://peterno.wordpress.com/2015/04/15/unhandled-exception-handling-in-ios-and-android-with-xamarin/
//Minor compile fixes by David McCurley.
private static void TaskSchedulerOnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs unobservedTaskExceptionEventArgs)
{
var newExc = new Exception("TaskSchedulerOnUnobservedTaskException", unobservedTaskExceptionEventArgs.Exception);
LogUnhandledException(newExc);
}
private static void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs unhandledExceptionEventArgs)
{
var newExc = new Exception("CurrentDomainOnUnhandledException", unhandledExceptionEventArgs.ExceptionObject as Exception);
LogUnhandledException(newExc);
}
internal static void LogUnhandledException(Exception exception)
{
try
{
const string errorFileName = "Fatal.log";
var libraryPath = System.Environment.GetFolderPath(Environment.SpecialFolder.Resources); // iOS: Environment.SpecialFolder.Resources
var errorFilePath = Path.Combine(libraryPath, errorFileName);
var errorMessage = String.Format("Time: {0}\r\nError: Unhandled Exception\r\n{1}",
DateTime.Now, exception.ToString());
File.WriteAllText(errorFilePath, errorMessage);
// Log to Android Device Logging.
//Android.Util.Log.Error("Crash Report", errorMessage);
}
catch
{
// just suppress any error logging exceptions
}
}
/// <summary>
// If there is an unhandled exception, the exception information is diplayed
// on screen the next time the app is started (only in debug configuration)
/// </summary>
[Conditional("DEBUG")]
private static void DisplayCrashReport()
{
const string errorFilename = "Fatal.log";
var libraryPath = Environment.GetFolderPath(Environment.SpecialFolder.Resources);
var errorFilePath = Path.Combine(libraryPath, errorFilename);
if (!File.Exists(errorFilePath))
{
return;
}
var errorText = File.ReadAllText(errorFilePath);
var alertView = new UIAlertView("Crash Report", errorText, null, "Close", "Clear") { UserInteractionEnabled = true };
alertView.Clicked += (sender, args) =>
{
if (args.ButtonIndex != 0)
{
File.Delete(errorFilePath);
}
};
alertView.Show();
}
#endregion
}
}

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.

Resources