Skip to content

Instantly share code, notes, and snippets.

@rudyryk

rudyryk/Badge.cs

Last active Feb 25, 2020
Embed
What would you like to do?
C# — Xamarin.Forms custom simple badge view + rounded box view via custom renderer
//
// Badge.cs
// Created by Alexey Kinev on 19 Jan 2015.
//
// Licensed under The MIT License (MIT)
// http://opensource.org/licenses/MIT
//
// Copyright (c) 2015 Alexey Kinev <alexey.rudy@gmail.com>
//
using System;
using Xamarin.Forms;
namespace ZeroFiveBit.Forms.Basic
{
/// <summary>
/// Badge.
/// </summary>
public class Badge : AbsoluteLayout
{
/// <summary>
/// The text property.
/// </summary>
public static readonly BindableProperty TextProperty =
BindableProperty.Create("Text", typeof(String), typeof(Badge), "");
/// <summary>
/// The box color property.
/// </summary>
public static readonly BindableProperty BoxColorProperty =
BindableProperty.Create("BoxColor", typeof(Color), typeof(Badge), Color.Default);
/// <summary>
/// The text.
/// </summary>
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
/// <summary>
/// Gets or sets the color of the box.
/// </summary>
public Color BoxColor
{
get { return (Color)GetValue(BoxColorProperty); }
set { SetValue(BoxColorProperty, value); }
}
/// <summary>
/// The box.
/// </summary>
protected RoundedBox Box;
/// <summary>
/// The label.
/// </summary>
protected Label Label;
/// <summary>
/// Initializes a new instance of the <see cref="ZeroFiveBit.Forms.Basic.Badge"/> class.
/// </summary>
public Badge(double size, double fontSize)
{
HeightRequest = size;
WidthRequest = HeightRequest;
// Box
Box = new RoundedBox {
CornerRadius = HeightRequest / 2
};
Box.SetBinding(BackgroundColorProperty, new Binding("BoxColor", source: this));
Children.Add(Box, new Rectangle(0, 0, 1.0, 1.0), AbsoluteLayoutFlags.All);
// Label
Label = new Label {
TextColor = Color.White,
FontSize = fontSize,
XAlign = TextAlignment.Center,
YAlign = TextAlignment.Center
};
Label.SetBinding(Label.TextProperty, new Binding("Text",
BindingMode.OneWay, source: this));
Children.Add(Label, new Rectangle(0, 0, 1.0, 1.0), AbsoluteLayoutFlags.All);
// Auto-width
SetBinding(WidthRequestProperty, new Binding("Text", BindingMode.OneWay,
new BadgeWidthConverter(WidthRequest), source: this));
// Hide if no value
SetBinding(IsVisibleProperty, new Binding("Text", BindingMode.OneWay,
new BadgeVisibleValueConverter(), source: this));
// Default color
BoxColor = Color.Red;
}
}
/// <summary>
/// Badge visible value converter.
/// </summary>
class BadgeVisibleValueConverter : IValueConverter
{
#region IValueConverter implementation
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var text = value as string;
return !String.IsNullOrEmpty(text);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
/// <summary>
/// Badge width converter.
/// </summary>
class BadgeWidthConverter : IValueConverter
{
/// <summary>
/// The width of the base.
/// </summary>
readonly double baseWidth;
/// <summary>
/// The width ratio.
/// </summary>
const double widthRatio = 0.33;
public BadgeWidthConverter(double baseWidth)
{
this.baseWidth = baseWidth;
}
#region IValueConverter implementation
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var text = value as string;
if ((text != null) && (text.Length > 1))
{
// We won't measure text length exactly here!
// May be we should, but it's too tricky. So,
// we'll just approximate new badge width as
// linear function from text legth.
return baseWidth * (1 + widthRatio * (text.Length - 1));
}
return baseWidth;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
}
//
// RoundedBox.cs
// Created by Alexey Kinev on 19 Jan 2015.
//
// Licensed under The MIT License (MIT)
// http://opensource.org/licenses/MIT
//
// Copyright (c) 2015 Alexey Kinev <alexey.rudy@gmail.com>
//
using System;
using Xamarin.Forms;
namespace ZeroFiveBit.Forms.Basic
{
public class RoundedBox : BoxView
{
/// <summary>
/// The corner radius property.
/// </summary>
public static readonly BindableProperty CornerRadiusProperty =
BindableProperty.Create("CornerRadius", typeof(double), typeof(RoundedBox), 0.0);
/// <summary>
/// Gets or sets the corner radius.
/// </summary>
public double CornerRadius
{
get { return (double)GetValue(CornerRadiusProperty); }
set { SetValue(CornerRadiusProperty, value); }
}
}
}
//
// RoundedBoxRenderer_Droid.cs
// Created by Alexey Kinev on 26 Apr 2015.
//
// Licensed under The MIT License (MIT)
// http://opensource.org/licenses/MIT
//
// Copyright (c) 2015 Alexey Kinev <alexey.rudy@gmail.com>
//
using System;
using System.ComponentModel;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Android.Graphics;
using ZeroFiveBit.Forms.Basic;
using ZeroFiveBit.Forms.Basic.Droid;
[assembly: ExportRenderer(typeof(RoundedBox), typeof(RoundedBoxRenderer))]
namespace ZeroFiveBit.Forms.Basic.Droid
{
public class RoundedBoxRenderer : BoxRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
{
base.OnElementChanged(e);
SetWillNotDraw(false);
Invalidate();
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == RoundedBox.CornerRadiusProperty.PropertyName)
{
Invalidate();
}
}
public override void Draw(Canvas canvas)
{
var box = Element as RoundedBox;
var rect = new Rect();
var paint = new Paint() {
Color = box.BackgroundColor.ToAndroid(),
AntiAlias = true,
};
GetDrawingRect(rect);
var radius = (float)(rect.Width() / box.Width * box.CornerRadius);
canvas.DrawRoundRect(new RectF(rect), radius, radius, paint);
}
}
}
//
// RoundedBoxRenderer_iOS.cs
// Created by Alexey Kinev on 19 Jan 2015.
//
// Licensed under The MIT License (MIT)
// http://opensource.org/licenses/MIT
//
// Copyright (c) 2015 Alexey Kinev <alexey.rudy@gmail.com>
//
using System;
using System.ComponentModel;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using ZeroFiveBit.Forms.Basic;
using ZeroFiveBit.Forms.Basic.iOS;
[assembly: ExportRenderer(typeof(RoundedBox), typeof(RoundedBoxRenderer))]
namespace ZeroFiveBit.Forms.Basic.iOS
{
public class RoundedBoxRenderer : BoxRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
{
base.OnElementChanged(e);
if (Element != null)
{
Layer.MasksToBounds = true;
UpdateCornerRadius(Element as RoundedBox);
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == RoundedBox.CornerRadiusProperty.PropertyName)
{
UpdateCornerRadius(Element as RoundedBox);
}
}
void UpdateCornerRadius(RoundedBox box)
{
Layer.CornerRadius = (float)box.CornerRadius;
}
}
}
@loanburger

This comment has been minimized.

Copy link

@loanburger loanburger commented Apr 14, 2015

Hi rudyryk I am trying to create a rounded box view - I'm following your example above, but a, lost at the renderer - Where does your Box renderer come from as I only have a BoxViewRenderer whcih does not have the CornerRadius and MasksToBounds properties.

@rudyryk

This comment has been minimized.

Copy link
Owner Author

@rudyryk rudyryk commented Apr 25, 2015

Hi Loan! That's strange, BoxRenderer should be available via Xamarin.Forms.Platform.Android.BoxRenderer or Xamarin.Forms.Platform.iOS.BoxRenderer.

@kolchy

This comment has been minimized.

Copy link

@kolchy kolchy commented Apr 29, 2015

Hey @rudyryk - I get an error for this line:

HeightRequest = Metrics.Pixels(size);

Says Metrics not found :(

@android-bee

This comment has been minimized.

Copy link

@android-bee android-bee commented May 13, 2015

HeightRequest = Metrics.Pixels(size);

Says Metrics not found . what should i do.. if i just give some random value, nothing is working, pls reply.

@stammi922

This comment has been minimized.

Copy link

@stammi922 stammi922 commented Jun 26, 2015

In case people are still having issues getting the roundedBox to work. I made the following changes which fixed the issue.

// Android MainActivity.cs
protected override void OnCreate (Bundle bundle)
{
    //Add in the beginning
App.ScreenHeight = (int)(Resources.DisplayMetrics.HeightPixels / Resources.DisplayMetrics.Density);
    ....
}
//iOS AppDelegate.cs
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
App.ScreenHeight = (int)UIScreen.MainScreen.Bounds.Height;
    ...
}
//Shared Code App.cs
    public class App : Application
    {
        static public int ScreenHeight;
        ...
//HeightRequest replacement Badge.cs
public Badge(double size, double fontSize)
{
    HeightRequest = App.ScreenHeight;
    WidthRequest = HeightRequest;
@rudyryk

This comment has been minimized.

Copy link
Owner Author

@rudyryk rudyryk commented Jul 27, 2015

Oh, sorry I didn't see comments earlier.

Metrics.Pixels(size) is actually not necessary and could be replaced with size, so I did it. That was my helper to scale elements automatically for iPhone / iPad development.

@rudyryk

This comment has been minimized.

Copy link
Owner Author

@rudyryk rudyryk commented Jul 27, 2015

Hi @stammi922! Hmm, seems like you're trying to set badge height to screen height. That's not an idea :) Badge just should have size (in points) specified as constructor parameter.

@michaeldimoudis

This comment has been minimized.

Copy link

@michaeldimoudis michaeldimoudis commented Oct 1, 2015

Hi @rudyryk, thank's for this! I implemented the same thing with a complete different approach in iOS but needed it for Android too so tried using your code. Works great on iOS, however if I put badges in a ListView, initially they show up good, but when I start scrolling, there are some weird artifacts that sometimes appear. Have you noticed this? Is their a way to fix that?

@prajaktashinde

This comment has been minimized.

Copy link

@prajaktashinde prajaktashinde commented Mar 10, 2016

@rudyryk will you please give me screen shots how this code's output is coming? It will be helpful.
Thanks in advance :)

@repper

This comment has been minimized.

Copy link

@repper repper commented Jun 6, 2016

Hey @rudyryk the example was very helpful. But I have a problem, I am using ViewRender for RelativeLayout to convert that it into rounded corner layout. And I am able to do it with your codes help. But the problem is that whatever children I have added, does not shows up inside that. Is it because of the Draw method, because it is called after I add the children in the extended RelativeLayout. Thanks

@sacredgeometry

This comment has been minimized.

Copy link

@sacredgeometry sacredgeometry commented Jun 9, 2016

I am having the same issue as @repper, anyone got any ideas please?

@rudyryk

This comment has been minimized.

Copy link
Owner Author

@rudyryk rudyryk commented Jun 15, 2016

Hi @repper

I am using ViewRender for RelativeLayout to convert that it into rounded corner layout.

Hm, I think that won't work simply like that. Probably I'm missing something, but Xamarin.Forms didn't provide valid hooks to replace layout renderers. There are predefined renderers for simple views in Forms like BoxRenderer etc. and we can tweak them as I usually did.

@bemnet4u

This comment has been minimized.

Copy link

@bemnet4u bemnet4u commented Jul 9, 2016

I noticed that i had to add horizontal and vertical option. Otherwise i was getting rectangles.

<controls:Badge Grid.Row="0" Grid.Column="2" Text="3" WidthRequest="25" HeightRequest="25"VerticalOptions="Center"HorizontalOptions="Center"/>

@padhisandeepkumar

This comment has been minimized.

Copy link

@padhisandeepkumar padhisandeepkumar commented Aug 1, 2016

@rudyryk, I'm not able to add to toolbaritem is there any way add on that at run time?

Waiting for your response,.....

@sportzbee

This comment has been minimized.

Copy link

@sportzbee sportzbee commented Aug 9, 2016

Hi Sandeep,

Have you able to added the badge to toolbar item?

@rudyryk

This comment has been minimized.

Copy link
Owner Author

@rudyryk rudyryk commented Aug 28, 2016

@padhisandeepkumar @sportzbee Adding a badge to toolbar item can't be simply done with Xamarin.Forms. It has to be implemented per-platform and this component isn't really helpful for that case.

Actually, it's probably even easier :) Because iOS8+ natively supports UIBarButton.badgeValue! Not sure about Android, though.

@rubgithub

This comment has been minimized.

Copy link

@rubgithub rubgithub commented Dec 16, 2016

Xaml sample:

<badge:Badge Text="3" WidthRequest="25" HeightRequest="25" VerticalOptions="Center" HorizontalOptions="Center" BoxColor="Blue">
            <x:Arguments>
                <x:Double>30</x:Double>
                <x:Double>12</x:Double>
            </x:Arguments>
        </badge:Badge>
@jaisont07

This comment has been minimized.

Copy link

@jaisont07 jaisont07 commented Mar 21, 2017

Hi, the text on the badge gets misaligned at times. ie. it gets misaligned when an App is kept in the background and then resumed. Any idea on it?
badgeissue

@johanvandijke

This comment has been minimized.

Copy link

@johanvandijke johanvandijke commented Jun 29, 2017

@jaisont07: Do you have a fix already for the misalignment? I'm facing the same issue

@yoel30

This comment has been minimized.

Copy link

@yoel30 yoel30 commented Nov 15, 2017

How to use it? can someone show an example of implementation? I just learning xamarin forms.

@rraallvv

This comment has been minimized.

Copy link

@rraallvv rraallvv commented Feb 6, 2018

@sportzbee I added the badge to the ToolbarItem for iOS using a custom renderer as explained in this blog post.

By the way, if anyone has been able to do the same for Android, I'm interested, I've googled for a solution without any luck.

@pjavax

This comment has been minimized.

Copy link

@pjavax pjavax commented Feb 23, 2018

How can i use it? I have grid and inside it i have 7 image(Grid.Row="0" Grid.Column="0" and so on) how can i do to show badge each cell? The values that will be shown comes from a method like this(the value to be show is resultado):

decimal realizado = 0;
                        decimal previsto = 0;

                        foreach (var item in indItem)
                        {
                            var val = item.Nome == "Previsto" ? previsto = item.Valor : realizado = item.Valor;
                        }

                        decimal desvio = Math.Abs(previsto - realizado);

                        if (desvio < 1000)
                            resultado = Math.Truncate(desvio).ToString();
                        else
                            while (desvio >= 1000 && siglas.Count > 0)
                            {
                                desvio /= 1000;
                                resultado = Math.Truncate(desvio) + siglas[0];
                                siglas.RemoveAt(0);
                            }

                        if (CorIndicador == "VERDE")
                            image.Source = ImageSource.FromResource("Estapar.AppOperacional.Images.faturamento caixa-28.png");
                        else
                        {
                            var pinta = CorIndicador == "VERMELHO" ? image.Source = ImageSource.FromResource("Estapar.AppOperacional.Images.faturamento caixa-26.png") : image.Source = ImageSource.FromResource("Estapar.AppOperacional.Images.faturamento caixa-27.png");
                        }
                        label.Text = resultado;


and my xaml is like this


<ContentPage.Content>
        <StackLayout Padding="0" Spacing="0" Margin="0">
            <Grid x:Name="grd" RowSpacing="1" ColumnSpacing="1" Padding="0" Margin="0" >
            <Grid.RowDefinitions>
                <RowDefinition Height="1" />
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
                <RowDefinition Height="*" />
                </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
                <Image x:Name="imgDesvioFat" Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Source="{local:ImageResource Operacional.Images.faturamento caixa-28.png}" Aspect="AspectFill">
                        <Image.GestureRecognizers>
                            <TapGestureRecognizer Tapped="OnDesvioFaturamentoTapReconizerTapped" NumberOfTapsRequired="1"></TapGestureRecognizer>
                        </Image.GestureRecognizers>
                    </Image>
                    
                    <!--<Label x:Name="lblFaturamento" Text="" Grid.Row="0" Grid.Column="0" TextColor="White" FontSize="9" FontAttributes="Bold" />-->    
                  
                <!--</Grid>-->
                <Image x:Name="imgTckCancelados" Grid.Row="1" Grid.Column="1" Source="{local:ImageResource Operacinal.Images.tickets cancelados-05.png}" Aspect="AspectFill">
                    <Image.GestureRecognizers>
                        <TapGestureRecognizer Tapped="OnTckCanceladosTapGestureReconizerTapped" NumberOfTapsRequired="1"></TapGestureRecognizer>
                    </Image.GestureRecognizers>
                </Image>

i shown just two images, but i have 7 images and the code is such. How can i show the badge and the value inside with my code?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.