52 Weeks of Xamarin: Week 12 – Advanced Customized Controls

Last week we looked at a simple example of customizing a control and creating a SpecialBoxViewcustom renderer.  This week we go a bit deeper.  Our goal is to give a BoxView a border, and to adjust that border dynamically.

[ This content is taken from my forthcoming Pluralsight course ]

As noted last week, we start with an Element and a Renderer.  In our case, the element will be a SpecialBoxView.  Unlike last week, however, this custom class will have contents; specifically the properties we want to be able to bind to.

Bindable Properties

public class SpecialBoxView : BoxView {     public static readonly BindableProperty BorderColorProperty =          BindableProperty.Create<SpecialBoxView, Color>(             p => p.BorderColor, default(Color));     public Color BorderColor {         get { return (Color)GetValue(BorderColorProperty); }         set { SetValue(BorderColorProperty, value); }     }     public static readonly BindableProperty BorderThicknessProperty =          BindableProperty.Create<SpecialBoxView, double>(             p => p.BorderThickness, default(double));     public double BorderThickness {         get { return (double)GetValue(BorderThicknessProperty); }         set { SetValue(BorderThicknessProperty, value); }     } }

Notice that these come in pairs: a bindable property and the public property that we’ll be using to get and set that value.  Thus, we can bind to BorderColor and/or BorderThickness.

Custom Renderer

With that done, we are ready to implement the custom renderer.  To do so, this week we’ll turn to iOS; next week we’ll follow up by creating the Android renderer.

The first step is to derive from BoxRenderer,

 public class SpecialBoxViewRenderer : BoxRenderer {

Within that class, we’ll override two methods: Draw and OnElementPropertyChanged.  In our override of Draw, we will first get the element itself,

 public override void Draw(CoreGraphics.CGRect rect) {
     SpecialBoxView specialBoxView = (SpecialBoxView)Element;
 

We will then get a context to work with to set our fill and stroke color as well as the width of the stroke.

 using (var context = UIGraphics.GetCurrentContext()) {
 
     context.SetFillColor(specialBoxView.Color.ToCGColor());
     context.SetStrokeColor(specialBoxView.BorderColor.ToCGColor());
     context.SetLineWidth((float)specialBoxView.BorderThickness);

We can then get the inset to draw the border.

    var rectangle = this.Bounds.Inset((int)specialBoxView.BorderThickness, (int)specialBoxView.BorderThickness);

    var path = CGPath.FromRect(rectangle);
    context.AddPath(path);
    context.DrawPath(CGPathDrawingMode.FillStroke);

All that is left is to override OnElementPropertyChanged.  We’ll make sure that it is the BorderThicknessProperty that has changed, and if so we’ll call SetNeedsDisplay which will cause Draw to be called and the border to be redrawn,

 

protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) {
    if (e.PropertyName == SpecialBoxView.BorderThicknessProperty.PropertyName) {
        SetNeedsDisplay();
    }
}

Don’t forget!

If we use this class as is it will build and it will run, but there will be no border, because we haven’t established a link between the renderer and the custom control.  As we did last week, we need to add an attribute outside of any namespace.

 [assembly: ExportRendererAttribute(typeof(SpecialBoxView), 
    typeof(SpecialBoxViewRenderer))]

You will need to add using statements to resolve the SpecialBoxView and the SpecialBoxViewRenderer.

XAML

In the XAML we’ll need a name space for the SpecialBoxView,

   xmlns:local="clr-namespace:CustomBoxView;assembly=CustomBoxView"
 

We can then instantiate a SpecialBoxView.  We’ll want to adjust the width of the border with a slider, so we’ll set the slider to be the BindingContext and the BorderThickness property will bind to the slider’s Value.

 <ContentPage.Content>
   <StackLayout
     Padding="20">
     <Label Text="Customized Control" TextColor="Red" FontSize="24"/> 
     <local:SpecialBoxView
       WidthRequest="200"
       HeightRequest="200"
       Color="Blue"
       BorderColor="Red"
       BindingContext="{x:Reference Name=ThicknessSlider}"
       BorderThickness="{Binding Path=Value}" />
     <Label
       BindingContext="{x:Reference Name=ThicknessSlider}"
       Text="{Binding Path=Value}" />
     <Slider
       x:Name="ThicknessSlider"
       Minimum="0"
       Maximum="100" />
   </StackLayout>
 </ContentPage.Content>
 

The result is shown in the image at the top of this posting.

 

Share

About Jesse Liberty

Jesse Liberty is an independent consultant and programmer with three decades of experience writing and delivering software projects. He is the author of 2 dozen books and multiple Pluralsight courses, and has been a Senior Technical Evangelist for Microsoft, a Distinguished Software Engineer for AT&T, a VP for Information Services for Citibank and a Software Architect for PBS. He is a Xamarin Certified Mobile Developer and a Xamarin MVP, Microsoft MVP and Telerik MVP.
This entry was posted in Xamarin. Bookmark the permalink.

2 Responses to 52 Weeks of Xamarin: Week 12 – Advanced Customized Controls

  1. Josh says:

    The only problem I have with Azure Mobile Services is the ilinibaty to ask for custom permission/scopes on Login with Facebook/Twitter. Sadly this became a deal breaker on my latest project and sent me back to native SDKs

  2. Pingback: Adam J Wolf: Weekly Xamarin Newsletter Issue #64 | XamGeek.com

Leave a Reply

Your email address will not be published.