LCJ
LCJ

Reputation: 22661

Aligning Image with Entry in Xamarin Forms

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:

  1. The image takes more width and height than specified HeightRequest and WidthRequest
  2. There is unwanted space between Image and Entry

How to fix this?

enter image description here

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>

Upvotes: 0

Views: 5668

Answers (2)

Diego Rafael Souza
Diego Rafael Souza

Reputation: 5314

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:

Sample

Explanation

  1. 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.

  1. 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.

Upvotes: 3

Sreejith Sree
Sreejith Sree

Reputation: 3766

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 :)

Upvotes: 0

Related Questions