knob control in xamarin forms
you can create knob control by following steps
Add the following class in portable project :
public class Knob : View
{
public static readonly BindableProperty MinProperty = BindableProperty.Create<Knob,float> (p => p.Min, 0);
public static readonly BindableProperty MaxProperty = BindableProperty.Create<Knob,float> (p => p.Max, 100);
public static readonly BindableProperty CurrentProperty = BindableProperty.Create<Knob,float> (p => p.Current, 50);
public static readonly BindableProperty BoundsProperty = BindableProperty.Create<Knob,Rectangle> (p => p.Bounds, new Rectangle(0,0,300,300));
public static readonly BindableProperty ShowTouchPathProperty = BindableProperty.Create<Knob,bool> (p => p.ShowTouchPath, false);
public float Min {
get { return (float) GetValue(MinProperty); }
set { SetValue (MinProperty, value); OnPropertyChanged(); }
}
public float Max {
get { return (float) GetValue(MaxProperty); }
set { SetValue (MaxProperty, value); OnPropertyChanged(); }
}
public float Current {
get { return (float) GetValue(CurrentProperty); }
set { SetValue (CurrentProperty, value); OnPropertyChanged();}
}
public new Rectangle Bounds {
get { return (Rectangle)GetValue(BoundsProperty); }
set { SetValue(BoundsProperty, value); }
}
public bool ShowTouchPath {
get { return (bool)GetValue(ShowTouchPathProperty); }
set { SetValue(ShowTouchPathProperty, value); }
}
public bool IsRotationEndedRegistered { get { return RotationEnded != null; }}
public Action<float> RotationStarted;
public Action<float> RotationEnded;
public Knob(Rectangle frame, float min, float max, float current, bool showTouchPath = false)
{
SetValue (MinProperty, min);
SetValue (MaxProperty, max);
SetValue (CurrentProperty, current);
SetValue (BoundsProperty, frame);
SetValue (ShowTouchPathProperty, showTouchPath);
}
public Knob(Rectangle frame)
{
Bounds = frame;
}
}
Add render class in android & IOSproject :
using Xamarin.Forms;
using KnobControl;
using KnobControl.Droid;
using Xamarin.Forms.Platform.Android;
using Android.Graphics;
using System.ComponentModel;
[assembly:ExportRenderer(typeof(Knob), typeof(KnobRenderer))]
namespace KnobControl.Droid
{
public class KnobRenderer: ViewRenderer<Knob, RoundKnobButton>
{
RoundKnobButton knob;
static Bitmap bmpRotorOn, bmpRotorOff;
protected override void OnElementChanged(ElementChangedEventArgs<Knob> e)
{
base.OnElementChanged(e);
if (e.OldElement != null || this.Element == null)
return;
var context = Forms.Context;
var metrics = Resources.DisplayMetrics;
var w = metrics.HeightPixels / 2 - 20;
var h = metrics.HeightPixels / 2 - 20;
// Load pictures once
if (bmpRotorOn == null || bmpRotorOff == null)
{
Console.WriteLine ("OnElementChanged :: Create Bitmaps");
Bitmap srcon = BitmapFactory.DecodeResource (context.Resources, Resource.Drawable.knob_handle);
float scaleWidth = ((float)w) / srcon.Width;
float scaleHeight = ((float)h) / srcon.Height;
Matrix matrix = new Matrix ();
matrix.PostScale (scaleWidth, scaleHeight);
bmpRotorOn = Bitmap.CreateBitmap (srcon, 0, 0, srcon.Width, srcon.Height, matrix, true);
srcon.Recycle ();
Bitmap srcoff = BitmapFactory.DecodeResource (context.Resources, Resource.Drawable.knob_handle);
bmpRotorOff = Bitmap.CreateBitmap (srcoff, 0, 0, srcoff.Width, srcoff.Height, matrix, true);
srcoff.Recycle ();
}
Console.WriteLine("KnobRenderer :: OnElementChanged :: Element.Width = " + Element.Width);
Console.WriteLine("KnobRenderer :: OnElementChanged :: Element.Height = " + Element.Height);
Console.WriteLine("KnobRenderer :: OnElementChanged :: Element.Bounds.Width = " + Element.Bounds.Width);
Console.WriteLine("KnobRenderer :: OnElementChanged :: Element.Bounds.Height = " + Element.Bounds.Height);
knob = new RoundKnobButton(context, Resource.Drawable.knob_bg, bmpRotorOn, bmpRotorOff, w, h);
knob.ShowTouchPath = Element.ShowTouchPath;
knob.MaxValue = Element.Max;
knob.MinValue = Element.Min;
knob.CurrentValue = Element.Current;
knob.RotationEnded += (float obj) =>
{
Console.WriteLine( "knob.RotationEnded with {0} ", obj );
if (Element != null && Element.RotationEnded != null)
{
Element.RotationEnded((float)obj);
}
};
knob.IsEnabled = Element.IsEnabled;
knob.OnRotate += ((float obj) =>
{
Console.WriteLine("percentage = {0}", obj);
});
SetNativeControl(knob);
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (Control == null || Element == null) return;
if (e.PropertyName == Knob.MaxProperty.PropertyName)
{
Control.MaxValue = Element.Max;
}
else if (e.PropertyName == Knob.MinProperty.PropertyName)
{
Control.MinValue = Element.Min;
}
else if (e.PropertyName == Knob.CurrentProperty.PropertyName)
{
Control.CurrentValue = Element.Current;
}
else if (e.PropertyName == Knob.BoundsProperty.PropertyName)
{
// var frame = new Rect((nfloat)Element.Bounds.X, (nfloat)Element.Bounds.Y, (nfloat)Element.Bounds.Width, (nfloat)Element.Bounds.Height);
// Control.Bounds = frame;
}
else if (e.PropertyName == Knob.IsEnabledProperty.PropertyName)
{
Control.IsEnabled = Element.IsEnabled;
}
}
}
}
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using KnobControl;
using KnobControl.iOS;
using CoreGraphics;
using UIKit;
[assembly:ExportRenderer(typeof(Knob), typeof(KnobRenderer))]
namespace KnobControl.iOS
{
public class KnobRenderer : ViewRenderer<Knob, UIKnobControl>
{
public KnobRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Knob> e)
{
base.OnElementChanged(e);
if (e.OldElement != null || this.Element == null)
return;
Console.WriteLine("OnElementChanged :: Element.Width" + Element.Width);
Console.WriteLine("OnElementChanged :: Element.Height" + Element.Height);
Console.WriteLine("OnElementChanged :: Element.Bounds.Width" + Element.Bounds.Width);
Console.WriteLine("OnElementChanged :: Element.Bounds.Height" + Element.Bounds.Height);
var mainScreen = UIScreen.MainScreen.Bounds;
var frame = new CGRect((nfloat)Element.Bounds.X, (nfloat)Element.Bounds.Y, (nfloat)mainScreen.Height/2-20, (nfloat)mainScreen.Height/2-20);
var knob = new UIKnobControl(frame, Element.Min, Element.Max, Element.Current, Element.ShowTouchPath);
knob.RotationEnded += (nfloat obj) =>
{
if(Element != null)
{
Element.Current = (float)obj;
}
};
SetNativeControl(knob);
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (Control == null || Element == null)
return;
if (e.PropertyName == Knob.MaxProperty.PropertyName)
{
Control.MaxValue = Element.Max;
}
else if (e.PropertyName == Knob.MinProperty.PropertyName)
{
Control.MinValue = Element.Min;
}
else if (e.PropertyName == Knob.CurrentProperty.PropertyName)
{
Control.CurrentValue = Element.Current;
}
else if (e.PropertyName == Knob.BoundsProperty.PropertyName)
{
var frame = new CGRect((nfloat)Element.Bounds.X, (nfloat)Element.Bounds.Y, (nfloat)Element.Bounds.Width, (nfloat)Element.Bounds.Height);
Control.Bounds = frame;
}
}
}
}
{
Padding = 0,
BackgroundImage = Device.OnPlatform("view_bg.png", "view_bg.png", "view_bg.png"),
Content = new StackLayout
{
Padding = 0,
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.CenterAndExpand,
Children =
{
new Knob(rectangle, 0, 100, 25),
new Knob(rectangle, 0, 100, 75, true)
}
}
};
** you can find sample at :
https://github.com/corneliu-serediuc/Xamarin.Forms.KnobControl
Step 1 :
Add the following class in portable project :
public class Knob : View
{
public static readonly BindableProperty MinProperty = BindableProperty.Create<Knob,float> (p => p.Min, 0);
public static readonly BindableProperty MaxProperty = BindableProperty.Create<Knob,float> (p => p.Max, 100);
public static readonly BindableProperty CurrentProperty = BindableProperty.Create<Knob,float> (p => p.Current, 50);
public static readonly BindableProperty BoundsProperty = BindableProperty.Create<Knob,Rectangle> (p => p.Bounds, new Rectangle(0,0,300,300));
public static readonly BindableProperty ShowTouchPathProperty = BindableProperty.Create<Knob,bool> (p => p.ShowTouchPath, false);
public float Min {
get { return (float) GetValue(MinProperty); }
set { SetValue (MinProperty, value); OnPropertyChanged(); }
}
public float Max {
get { return (float) GetValue(MaxProperty); }
set { SetValue (MaxProperty, value); OnPropertyChanged(); }
}
public float Current {
get { return (float) GetValue(CurrentProperty); }
set { SetValue (CurrentProperty, value); OnPropertyChanged();}
}
public new Rectangle Bounds {
get { return (Rectangle)GetValue(BoundsProperty); }
set { SetValue(BoundsProperty, value); }
}
public bool ShowTouchPath {
get { return (bool)GetValue(ShowTouchPathProperty); }
set { SetValue(ShowTouchPathProperty, value); }
}
public bool IsRotationEndedRegistered { get { return RotationEnded != null; }}
public Action<float> RotationStarted;
public Action<float> RotationEnded;
public Knob(Rectangle frame, float min, float max, float current, bool showTouchPath = false)
{
SetValue (MinProperty, min);
SetValue (MaxProperty, max);
SetValue (CurrentProperty, current);
SetValue (BoundsProperty, frame);
SetValue (ShowTouchPathProperty, showTouchPath);
}
public Knob(Rectangle frame)
{
Bounds = frame;
}
}
Step 2 :
Add render class in android & IOSproject :
1- android :
using System;using Xamarin.Forms;
using KnobControl;
using KnobControl.Droid;
using Xamarin.Forms.Platform.Android;
using Android.Graphics;
using System.ComponentModel;
[assembly:ExportRenderer(typeof(Knob), typeof(KnobRenderer))]
namespace KnobControl.Droid
{
public class KnobRenderer: ViewRenderer<Knob, RoundKnobButton>
{
RoundKnobButton knob;
static Bitmap bmpRotorOn, bmpRotorOff;
protected override void OnElementChanged(ElementChangedEventArgs<Knob> e)
{
base.OnElementChanged(e);
if (e.OldElement != null || this.Element == null)
return;
var context = Forms.Context;
var metrics = Resources.DisplayMetrics;
var w = metrics.HeightPixels / 2 - 20;
var h = metrics.HeightPixels / 2 - 20;
// Load pictures once
if (bmpRotorOn == null || bmpRotorOff == null)
{
Console.WriteLine ("OnElementChanged :: Create Bitmaps");
Bitmap srcon = BitmapFactory.DecodeResource (context.Resources, Resource.Drawable.knob_handle);
float scaleWidth = ((float)w) / srcon.Width;
float scaleHeight = ((float)h) / srcon.Height;
Matrix matrix = new Matrix ();
matrix.PostScale (scaleWidth, scaleHeight);
bmpRotorOn = Bitmap.CreateBitmap (srcon, 0, 0, srcon.Width, srcon.Height, matrix, true);
srcon.Recycle ();
Bitmap srcoff = BitmapFactory.DecodeResource (context.Resources, Resource.Drawable.knob_handle);
bmpRotorOff = Bitmap.CreateBitmap (srcoff, 0, 0, srcoff.Width, srcoff.Height, matrix, true);
srcoff.Recycle ();
}
Console.WriteLine("KnobRenderer :: OnElementChanged :: Element.Width = " + Element.Width);
Console.WriteLine("KnobRenderer :: OnElementChanged :: Element.Height = " + Element.Height);
Console.WriteLine("KnobRenderer :: OnElementChanged :: Element.Bounds.Width = " + Element.Bounds.Width);
Console.WriteLine("KnobRenderer :: OnElementChanged :: Element.Bounds.Height = " + Element.Bounds.Height);
knob = new RoundKnobButton(context, Resource.Drawable.knob_bg, bmpRotorOn, bmpRotorOff, w, h);
knob.ShowTouchPath = Element.ShowTouchPath;
knob.MaxValue = Element.Max;
knob.MinValue = Element.Min;
knob.CurrentValue = Element.Current;
knob.RotationEnded += (float obj) =>
{
Console.WriteLine( "knob.RotationEnded with {0} ", obj );
if (Element != null && Element.RotationEnded != null)
{
Element.RotationEnded((float)obj);
}
};
knob.IsEnabled = Element.IsEnabled;
knob.OnRotate += ((float obj) =>
{
Console.WriteLine("percentage = {0}", obj);
});
SetNativeControl(knob);
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (Control == null || Element == null) return;
if (e.PropertyName == Knob.MaxProperty.PropertyName)
{
Control.MaxValue = Element.Max;
}
else if (e.PropertyName == Knob.MinProperty.PropertyName)
{
Control.MinValue = Element.Min;
}
else if (e.PropertyName == Knob.CurrentProperty.PropertyName)
{
Control.CurrentValue = Element.Current;
}
else if (e.PropertyName == Knob.BoundsProperty.PropertyName)
{
// var frame = new Rect((nfloat)Element.Bounds.X, (nfloat)Element.Bounds.Y, (nfloat)Element.Bounds.Width, (nfloat)Element.Bounds.Height);
// Control.Bounds = frame;
}
else if (e.PropertyName == Knob.IsEnabledProperty.PropertyName)
{
Control.IsEnabled = Element.IsEnabled;
}
}
}
}
2 - IOS :
using System;using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using KnobControl;
using KnobControl.iOS;
using CoreGraphics;
using UIKit;
[assembly:ExportRenderer(typeof(Knob), typeof(KnobRenderer))]
namespace KnobControl.iOS
{
public class KnobRenderer : ViewRenderer<Knob, UIKnobControl>
{
public KnobRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Knob> e)
{
base.OnElementChanged(e);
if (e.OldElement != null || this.Element == null)
return;
Console.WriteLine("OnElementChanged :: Element.Width" + Element.Width);
Console.WriteLine("OnElementChanged :: Element.Height" + Element.Height);
Console.WriteLine("OnElementChanged :: Element.Bounds.Width" + Element.Bounds.Width);
Console.WriteLine("OnElementChanged :: Element.Bounds.Height" + Element.Bounds.Height);
var mainScreen = UIScreen.MainScreen.Bounds;
var frame = new CGRect((nfloat)Element.Bounds.X, (nfloat)Element.Bounds.Y, (nfloat)mainScreen.Height/2-20, (nfloat)mainScreen.Height/2-20);
var knob = new UIKnobControl(frame, Element.Min, Element.Max, Element.Current, Element.ShowTouchPath);
knob.RotationEnded += (nfloat obj) =>
{
if(Element != null)
{
Element.Current = (float)obj;
}
};
SetNativeControl(knob);
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (Control == null || Element == null)
return;
if (e.PropertyName == Knob.MaxProperty.PropertyName)
{
Control.MaxValue = Element.Max;
}
else if (e.PropertyName == Knob.MinProperty.PropertyName)
{
Control.MinValue = Element.Min;
}
else if (e.PropertyName == Knob.CurrentProperty.PropertyName)
{
Control.CurrentValue = Element.Current;
}
else if (e.PropertyName == Knob.BoundsProperty.PropertyName)
{
var frame = new CGRect((nfloat)Element.Bounds.X, (nfloat)Element.Bounds.Y, (nfloat)Element.Bounds.Width, (nfloat)Element.Bounds.Height);
Control.Bounds = frame;
}
}
}
}
Usage :
MainPage = new ContentPage{
Padding = 0,
BackgroundImage = Device.OnPlatform("view_bg.png", "view_bg.png", "view_bg.png"),
Content = new StackLayout
{
Padding = 0,
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.CenterAndExpand,
Children =
{
new Knob(rectangle, 0, 100, 25),
new Knob(rectangle, 0, 100, 75, true)
}
}
};
** you can find sample at :
https://github.com/corneliu-serediuc/Xamarin.Forms.KnobControl
when i touch the knob control it doesn't move to current position.but when i drag the knob it moves to current position, how to resolve this
ReplyDelete?