I am having a requirement like a Month picker which is showing only month and year and not the date .How can we achieve the same for both IOS and Android platform using Xamarin forms ?
You could implement it by using Custom Renderer
in Forms
create a custom View
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;
namespace App20
{
public class MonthYearPickerView :View
{
public static readonly BindableProperty FontSizeProperty = BindableProperty.Create(
propertyName: nameof(FontSize),
returnType: typeof(double),
declaringType: typeof(MonthYearPickerView),
defaultValue: (double)24,
defaultBindingMode: BindingMode.TwoWay);
[TypeConverter(typeof(FontSizeConverter))]
public double FontSize
{
get => (double)GetValue(FontSizeProperty);
set => SetValue(FontSizeProperty, value);
}
public static readonly BindableProperty TextColorProperty = BindableProperty.Create(
propertyName: nameof(TextColor),
returnType: typeof(Color),
declaringType: typeof(MonthYearPickerView),
defaultValue: Color.White,
defaultBindingMode: BindingMode.TwoWay);
public Color TextColor
{
get => (Color)GetValue(TextColorProperty);
set => SetValue(TextColorProperty, value);
}
public static readonly BindableProperty InfiniteScrollProperty = BindableProperty.Create(
propertyName: nameof(InfiniteScroll),
returnType: typeof(bool),
declaringType: typeof(MonthYearPickerView),
defaultValue: true,
defaultBindingMode: BindingMode.TwoWay);
public bool InfiniteScroll
{
get => (bool)GetValue(InfiniteScrollProperty);
set => SetValue(InfiniteScrollProperty, value);
}
public static readonly BindableProperty DateProperty = BindableProperty.Create(
propertyName: nameof(Date),
returnType: typeof(DateTime),
declaringType: typeof(MonthYearPickerView),
defaultValue: default,
defaultBindingMode: BindingMode.TwoWay);
public DateTime Date
{
get => (DateTime)GetValue(DateProperty);
set => SetValue(DateProperty, value);
}
public static readonly BindableProperty MaxDateProperty = BindableProperty.Create(
propertyName: nameof(MaxDate),
returnType: typeof(DateTime?),
declaringType: typeof(MonthYearPickerView),
defaultValue: default,
defaultBindingMode: BindingMode.TwoWay);
public DateTime? MaxDate
{
get => (DateTime?)GetValue(MaxDateProperty);
set => SetValue(MaxDateProperty, value);
}
public static readonly BindableProperty MinDateProperty = BindableProperty.Create(
propertyName: nameof(MinDate),
returnType: typeof(DateTime?),
declaringType: typeof(MonthYearPickerView),
defaultValue: default,
defaultBindingMode: BindingMode.TwoWay);
public DateTime? MinDate
{
get => (DateTime?)GetValue(MinDateProperty);
set => SetValue(MinDateProperty, value);
}
}
}
in iOS
using System;
using App20;
using App20.iOS;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(MonthYearPickerView), typeof(MonthYearPickerRenderer))]
namespace App20.iOS
{
public class MonthYearPickerRenderer : ViewRenderer<MonthYearPickerView, UITextField>
{
private DateTime _selectedDate;
private UITextField _dateLabel;
private PickerDateModel _pickerModel;
protected override void OnElementChanged(ElementChangedEventArgs<MonthYearPickerView> e)
{
base.OnElementChanged(e);
_dateLabel = new UITextField();
_dateLabel.TextAlignment = UITextAlignment.Center;
var dateToday = DateTime.Today;
SetupPicker(new DateTime(dateToday.Year, dateToday.Month, 1));
SetNativeControl(_dateLabel);
Control.EditingChanged += ControlOnEditingChanged;
Element.PropertyChanged += Element_PropertyChanged;
}
private void ControlOnEditingChanged(object sender, EventArgs e)
{
var currentDate = $"{Element.Date.Month:D2} | {Element.Date.Year}";
if (_dateLabel.Text != currentDate)
{
_dateLabel.Text = currentDate;
}
}
protected override void Dispose(bool disposing)
{
Element.PropertyChanged -= Element_PropertyChanged;
base.Dispose(disposing);
}
private void SetupPicker(DateTime date)
{
var datePicker = new UIPickerView();
_pickerModel = new PickerDateModel(datePicker, date, Element.MaxDate, Element.MinDate);
datePicker.ShowSelectionIndicator = true;
_selectedDate = date;
_pickerModel.PickerChanged += (sender, e) =>
{
_selectedDate = e;
};
datePicker.Model = _pickerModel;
_pickerModel.MaxDate = Element.MaxDate ?? DateTime.MaxValue;
_pickerModel.MinDate = Element.MinDate ?? DateTime.MinValue;
var toolbar = new UIToolbar
{
BarStyle = UIBarStyle.Default,
Translucent = true
};
toolbar.SizeToFit();
var doneButton = new UIBarButtonItem("Done", UIBarButtonItemStyle.Done,
(s, e) =>
{
Element.Date = _selectedDate;
_dateLabel.Text = $"{Element.Date.Month:D2} | {Element.Date.Year}";
_dateLabel.ResignFirstResponder();
});
toolbar.SetItems(new[] { new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace), doneButton }, true);
_dateLabel.InputView = datePicker;
_dateLabel.Text = $"{Element.Date.Month:D2} | {Element.Date.Year}";
_dateLabel.InputAccessoryView = toolbar;
_dateLabel.TextColor = Element.TextColor.ToUIColor();
}
private void Element_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == MonthYearPickerView.MaxDateProperty.PropertyName)
{
_pickerModel.MaxDate = Element.MaxDate ?? DateTime.MinValue;
}
else if (e.PropertyName == MonthYearPickerView.MinDateProperty.PropertyName)
{
_pickerModel.MinDate = Element.MinDate ?? DateTime.MaxValue;
}
}
}
}
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using UIKit;
namespace App20.iOS
{
public class PickerDateModel : UIPickerViewModel
{
public event EventHandler<DateTime> PickerChanged;
#region Fields
private readonly List<string> _mainNamesOfMonthSource;
private readonly List<int> _mainYearsSource;
private readonly UIPickerView _picker;
private readonly int _numberOfComponents;
private readonly int _minYear;
private readonly int _maxYear;
private List<int> _years;
private List<string> _namesOfMonth;
private DateTime _selectedDate;
private DateTime _maxDate;
private DateTime _minDate;
#endregion Fields
#region Constructors
public PickerDateModel(UIPickerView datePicker, DateTime selectedDate, DateTime? maxDate, DateTime? minDate)
{
_mainNamesOfMonthSource = DateTimeFormatInfo.CurrentInfo?.MonthNames
.Where(x => !string.IsNullOrWhiteSpace(x))
.ToList();
_maxDate = maxDate ?? DateTime.MaxValue;
_minDate = minDate ?? DateTime.MinValue;
_maxYear = _maxDate.Year;
_minYear = _minDate.Year;
_years = new List<int>();
_picker = datePicker;
_namesOfMonth = _mainNamesOfMonthSource;
_numberOfComponents = 2;
SelectedDate = selectedDate;
}
#endregion Constructors
#region Properties
public DateTime SelectedDate
{
get => _selectedDate;
set
{
_selectedDate = value;
ReloadSections();
PickerChanged?.Invoke(this, value);
}
}
public DateTime MaxDate
{
get => _maxDate;
set
{
_maxDate = value;
ReloadSections();
}
}
public DateTime MinDate
{
get => _minDate;
set
{
_minDate = value;
ReloadSections();
}
}
#endregion Properties
#region Private Methods
private void ReloadSections()
{
var selectedDate = SelectedDate == DateTime.MinValue
? DateTime.Today
: SelectedDate;
_years.Clear();
for (int i = _minYear; i <= _maxYear; i++)
{
_years.Add(i);
}
_namesOfMonth = _mainNamesOfMonthSource;
if (SelectedDate.Year == MinDate.Year)
{
_namesOfMonth = _mainNamesOfMonthSource.Skip(MinDate.Month - 1).ToList();
}
if (SelectedDate.Year == MaxDate.Year)
{
_namesOfMonth = _mainNamesOfMonthSource.Take(MaxDate.Month).ToList();
}
SetCarousels(selectedDate);
}
#endregion Private Methods
#region Public Methods
public void SetCarousels(DateTime dateTime)
{
if (_picker.NumberOfComponents != _numberOfComponents) return;
var y = DateTimeFormatInfo.CurrentInfo?.GetMonthName(dateTime.Month);
var x = _namesOfMonth.IndexOf(y);
_picker.Select(x, 0, false);
_picker.Select(_years.IndexOf(dateTime.Year), 1, false);
_picker.ReloadComponent(0);
_picker.ReloadComponent(1);
}
public override nint GetComponentCount(UIPickerView pickerView)
{
return _numberOfComponents;
}
public override nint GetRowsInComponent(UIPickerView pickerView, nint component)
{
if (component == 0)
{
return _namesOfMonth.Count;
}
else if (component == 1)
{
return _years.Count;
}
else
{
return 0;
}
}
public override string GetTitle(UIPickerView pickerView, nint row, nint component)
{
if (component == 0)
{
return _namesOfMonth.Count==0 ? _namesOfMonth.First() : _namesOfMonth[(int)row];
}
else if (component == 1)
{
var list = _years;
return _years.Count==0? _years.First().ToString() : _years[(int)row].ToString();
}
else
{
return row.ToString();
}
}
public override void Selected(UIPickerView pickerView, nint row, nint component)
{
var month = GetMonthNumberByName(_namesOfMonth[(int)pickerView.SelectedRowInComponent(0)]);
var year = _years[(int)pickerView.SelectedRowInComponent(1)];
if (year == MinDate.Year)
{
month = month >= MinDate.Month ? month : MinDate.Month;
}
if (year == MaxDate.Year)
{
month = month <= MaxDate.Month ? month : MaxDate.Month;
}
SelectedDate = new DateTime(year, month, 1);
ReloadSections();
pickerView.ReloadAllComponents();
int GetMonthNumberByName(string monthName) =>
DateTime.ParseExact(monthName, "MMMM", CultureInfo.CurrentCulture).Month;
}
#endregion Public Methods
}
}
in Android
in MainActivity
public static MainActivity Instance { get; private set; }
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
Instance = this;
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
}
using Android.Content;
using Android.Support.V7.App;
using Android.Widget;
using App20;
using App20.Droid;
using System;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(MonthYearPickerView), typeof(MonthYearPickerRenderer))]
namespace App20.Droid
{
public class MonthYearPickerRenderer : ViewRenderer<MonthYearPickerView, EditText>
{
private readonly Context _context;
private MonthYearPickerDialog _monthYearPickerDialog;
public MonthYearPickerRenderer(Context context) : base(context)
{
_context = context;
}
protected override void OnElementChanged(ElementChangedEventArgs<MonthYearPickerView> e)
{
base.OnElementChanged(e);
CreateAndSetNativeControl();
Control.KeyListener = null;
Element.Focused += Element_Focused;
}
protected override void Dispose(bool disposing)
{
if (Control == null) return;
Element.Focused -= Element_Focused;
if (_monthYearPickerDialog != null)
{
_monthYearPickerDialog.OnDateTimeChanged -= OnDateTimeChanged;
_monthYearPickerDialog.OnClosed -= OnClosed;
_monthYearPickerDialog.Hide();
_monthYearPickerDialog.Dispose();
_monthYearPickerDialog = null;
}
base.Dispose(disposing);
}
#region Private Methods
private void ShowDatePicker()
{
if (_monthYearPickerDialog == null)
{
_monthYearPickerDialog = new MonthYearPickerDialog();
_monthYearPickerDialog.OnDateTimeChanged += OnDateTimeChanged;
_monthYearPickerDialog.OnClosed += OnClosed;
}
_monthYearPickerDialog.Date = Element.Date;
_monthYearPickerDialog.MinDate = FormatDateToMonthYear(Element.MinDate);
_monthYearPickerDialog.MaxDate = FormatDateToMonthYear(Element.MaxDate);
_monthYearPickerDialog.InfiniteScroll = Element.InfiniteScroll;
var appcompatActivity = MainActivity.Instance;
var mFragManager = appcompatActivity?.SupportFragmentManager;
if (mFragManager != null)
{
_monthYearPickerDialog.Show(mFragManager, nameof(MonthYearPickerDialog));
}
}
private void ClearPickerFocus()
{
((IElementController)Element).SetValueFromRenderer(VisualElement.IsFocusedProperty, false);
Control.ClearFocus();
}
private DateTime? FormatDateToMonthYear(DateTime? dateTime) =>
dateTime.HasValue ? (DateTime?) new DateTime(dateTime.Value.Year, dateTime.Value.Month, 1) : null;
private void CreateAndSetNativeControl()
{
var tv = new EditText(_context);
tv.SetTextColor(Element.TextColor.ToAndroid());
tv.TextSize = (float)Element.FontSize;
tv.Text = $"{Element.Date.Month:D2} | {Element.Date.Year}";
tv.Gravity = Android.Views.GravityFlags.Center;
tv.SetBackgroundColor(Element.BackgroundColor.ToAndroid());
SetNativeControl(tv);
}
#endregion
#region Event Handlers
private void Element_Focused(object sender, FocusEventArgs e)
{
if (e.IsFocused)
{
ShowDatePicker();
}
}
private void OnClosed(object sender, DateTime e)
{
ClearPickerFocus();
}
private void OnDateTimeChanged(object sender, DateTime e)
{
Element.Date = e;
Control.Text = $"{Element.Date.Month:D2} | {Element.Date.Year}";
ClearPickerFocus();
}
#endregion
}
}
using Android.App;
using Android.OS;
using Android.Views;
using Android.Widget;
using System;
using System.Linq;
namespace App20.Droid
{
public class MonthYearPickerDialog : Android.Support.V4.App.DialogFragment
{
public event EventHandler<DateTime> OnDateTimeChanged;
public event EventHandler<DateTime> OnClosed;
#region Private Fields
private const int DefaultDay = 1;
private const int MinNumberOfMonths = 1;
private const int MaxNumberOfMonths = 12;
private const int MinNumberOfYears = 1900;
private const int MaxNumberOfYears = 2100;
private NumberPicker _monthPicker;
private NumberPicker _yearPicker;
#endregion
#region Public Properties
public DateTime? MinDate { get; set; }
public DateTime? MaxDate { get; set; }
public DateTime? Date { get; set; }
public bool InfiniteScroll { get; set; }
#endregion
public void Hide() => base.Dialog?.Hide();
public override Dialog OnCreateDialog(Bundle savedInstanceState)
{
var builder = new AlertDialog.Builder(Activity);
var inflater = Activity.LayoutInflater;
var selectedDate = GetSelectedDate();
var dialog = inflater.Inflate(Resource.Layout.date_picker_dialog, null);
_monthPicker = (NumberPicker)dialog.FindViewById(Resource.Id.picker_month);
_yearPicker = (NumberPicker)dialog.FindViewById(Resource.Id.picker_year);
InitializeMonthPicker(selectedDate.Month);
InitializeYearPicker(selectedDate.Year);
SetMaxMinDate(MaxDate, MinDate);
builder.SetView(dialog)
.SetPositiveButton("Ok", (sender, e) =>
{
selectedDate = new DateTime(_yearPicker.Value, _monthPicker.Value, DefaultDay);
OnDateTimeChanged?.Invoke(dialog, selectedDate);
})
.SetNegativeButton("Cancel", (sender, e) =>
{
Dialog.Cancel();
OnClosed?.Invoke(dialog, selectedDate);
});
return builder.Create();
}
protected override void Dispose(bool disposing)
{
if (_yearPicker != null)
{
_yearPicker.ScrollChange -= YearPicker_ScrollChange;
_yearPicker.Dispose();
_yearPicker = null;
}
_monthPicker?.Dispose();
_monthPicker = null;
base.Dispose(disposing);
}
#region Private Methods
private DateTime GetSelectedDate() => Date ?? DateTime.Now;
private void InitializeYearPicker(int year)
{
_yearPicker.MinValue = MinNumberOfYears;
_yearPicker.MaxValue = MaxNumberOfYears;
_yearPicker.Value = year;
_yearPicker.ScrollChange += YearPicker_ScrollChange;
if (!InfiniteScroll)
{
_yearPicker.WrapSelectorWheel = false;
_yearPicker.DescendantFocusability = DescendantFocusability.BlockDescendants;
}
}
private void InitializeMonthPicker(int month)
{
_monthPicker.MinValue = MinNumberOfMonths;
_monthPicker.MaxValue = MaxNumberOfMonths;
_monthPicker.SetDisplayedValues(GetMonthNames());
_monthPicker.Value = month;
if (!InfiniteScroll)
{
_monthPicker.WrapSelectorWheel = false;
_monthPicker.DescendantFocusability = DescendantFocusability.BlockDescendants;
}
}
private void YearPicker_ScrollChange(object sender, View.ScrollChangeEventArgs e)
{
SetMaxMinDate(MaxDate, MinDate);
}
private void SetMaxMinDate(DateTime? maxDate, DateTime? minDate)
{
try
{
if (maxDate.HasValue)
{
var maxYear = maxDate.Value.Year;
var maxMonth = maxDate.Value.Month;
if (_yearPicker.Value == maxYear)
{
_monthPicker.MaxValue = maxMonth;
}
else if (_monthPicker.MaxValue != MaxNumberOfMonths)
{
_monthPicker.MaxValue = MaxNumberOfMonths;
}
_yearPicker.MaxValue = maxYear;
}
if (minDate.HasValue)
{
var minYear = minDate.Value.Year;
var minMonth = minDate.Value.Month;
if (_yearPicker.Value == minYear)
{
_monthPicker.MinValue = minMonth;
}
else if (_monthPicker.MinValue != MinNumberOfMonths)
{
_monthPicker.MinValue = MinNumberOfMonths;
}
_yearPicker.MinValue = minYear;
}
_monthPicker.SetDisplayedValues(GetMonthNames(_monthPicker.MinValue));
}
catch (Exception e)
{
}
}
private string[] GetMonthNames(int start = 1) =>
System.Globalization.DateTimeFormatInfo.CurrentInfo?.MonthNames.Skip(start - 1).ToArray();
#endregion
}
}
create date_picker_dialog.xml in Resource ->layout
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="horizontal">
<NumberPicker
android:id="#+id/picker_month"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="20dp"
android:layout_marginRight="20dp">
</NumberPicker>
<NumberPicker
android:id="#+id/picker_year"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</NumberPicker>
</LinearLayout>
</LinearLayout>
Now you can reference it in Xaml
<StackLayout VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand">
<local:MonthYearPickerView
Date="06.15.2020"
BackgroundColor="LightBlue"
WidthRequest="150"
MinDate="01.01.2020"
MaxDate="12.31.2050"
HorizontalOptions="CenterAndExpand"
VerticalOptions="Center" />
</StackLayout>
Related
I created a custom DatePicker to look like it has placeholder, but the
problem is, when clicking the DatePicker then when the DatePicker
Dialog opens and I click on okay with the preselected/default date
value of today, The date is not written in the field. Below is my
code.
I think the reason is that since "Date" property has a default value
of today and there is no change in the selection when the "OK" button
was clicked
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace XXX.XXX.Mobile.Controls
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class DatePickerWithPlaceHolder : DatePicker
{
public static readonly BindableProperty NullableDateProperty = BindableProperty.Create(nameof(NullableDate),typeof(DateTime?),typeof(DatePickerWithPlaceHolder), null, BindingMode.TwoWay);
private string _format;
public DateTime? NullableDate
{
get => (DateTime?)GetValue(NullableDateProperty);
set
{
SetValue(NullableDateProperty, value);
UpdateDate();
}
}
private void UpdateDate()
{
DatePickerWithPlaceHolder datePicker = this;
if (NullableDate.HasValue)
{
if (null != _format) Format = _format;
Date = NullableDate.Value;
datePicker.FontAttributes = FontAttributes.None;
datePicker.TextColor = Color.Black;
}
else
{
_format = Format;
Format = "Select Date";
datePicker.FontAttributes = FontAttributes.Italic;
datePicker.TextColor = Color.Gray;
}
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
UpdateDate();
}
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == "Date") NullableDate = Date;
if (propertyName == "NullableDate") UpdateDate();
}
}
}
I think you need set NullableDate to null in the background code.
Below is the sample code for your reference:
DatePickerWithPlaceHolder.cs
public class DatePickerWithPlaceHolder : DatePicker
{
private string _format = null;
public static readonly BindableProperty NullableDateProperty = BindableProperty.Create<DatePickerWithPlaceHolder, DateTime?>(p => p.NullableDate, null, BindingMode.TwoWay);
public DateTime? NullableDate
{
get { return (DateTime?)GetValue(NullableDateProperty); }
set { SetValue(NullableDateProperty, value); UpdateDate(); }
}
private void UpdateDate()
{
if (NullableDate.HasValue) { if (null != _format) Format = _format; Date = NullableDate.Value; }
else { _format = Format; Format = "Select Date ..."; FontAttributes = FontAttributes.Italic;
TextColor = Color.Green;
}
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
UpdateDate();
}
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == "Date") NullableDate = Date;
}
}
Consume it in Xaml:
<appentrymask:DatePickerWithPlaceHolder x:Name="myDatePicker"/>
Initialize the NullableDate in code behind:
public MainPage()
{
InitializeComponent();
myDatePicker.NullableDate = null;
}
Last but not least,for detecting the OK or Cancel event, you could refer to this thread.
Ok, so I've been trying to get the custom renderer working but I have a problem.
The Xamarin Forums discussion is here: https://forums.xamarin.com/discussion/comment/376813#Comment_376813
The problem is tht when I zoom out the webview stays in a part of the screen and doesn't fit the whole screen.
I add the code of the project:
MyWebView.cs
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;
namespace CofarLE_Ejemplo_5
{
public class MyWebView : WebView
{
public int ZoomInLevel
{
get { return (int)GetValue(ZoomInLevelProperty); }
set { SetValue(ZoomInLevelProperty, value); }
}
public bool EnableZoomControl
{
get { return (bool)GetValue(EnableZoomControlProperty); }
set { SetValue(EnableZoomControlProperty, value); }
}
public static readonly BindableProperty ZoomInLevelProperty = BindableProperty.Create(propertyName: "ZoomInLevel", returnType: typeof(int), declaringType: typeof(MyWebView), defaultValue: 100, propertyChanged: OnZoomInLevelPropertyChanged);
public static readonly BindableProperty EnableZoomControlProperty = BindableProperty.Create(propertyName: "EnableZoomControl", returnType: typeof(bool), declaringType: typeof(MyWebView), defaultValue: false, propertyChanged: OnEnableZoomChanged);
private static void OnZoomInLevelPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var control1 = (MyWebView)bindable;
control1.ZoomInLevel = (int)newValue;
}
private static void OnEnableZoomChanged(BindableObject bindable, object oldValue, object newValue)
{
var control1 = (MyWebView)bindable;
control1.EnableZoomControl = (bool)newValue;
}
}
}
MyWebViewRendereriOS.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using CofarLE_Ejemplo_5;
using CofarLE_Ejemplo_5.iOS;
using Foundation;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(MyWebView), typeof(MyWebViewRendereriOS))]
namespace CofarLE_Ejemplo_5.iOS
{
public class MyDelegate : UIScrollViewDelegate
{
public UIView myView;
public UIView ViewForZoom;
public MyDelegate(UIView view)
{
myView = view;
ViewForZoom = view;
}
public override UIView ViewForZoomingInScrollView(UIScrollView scrollView)
{
MessagingCenter.Subscribe<object, bool>(this, "zoom", (sender, arg) => {
if (arg == true)
{
myView.ContentMode = UIViewContentMode.ScaleToFill;
ViewForZoom = myView;
}
else
{
ViewForZoom = null;
}
});
return ViewForZoom;
}
}
public class MyWebViewRendereriOS : WebViewRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (NativeView != null && e.NewElement != null)
{
var control1 = NativeView as UIWebView;
if (e.OldElement != null)
{
e.OldElement.PropertyChanged -= OnElementPropertyChanged;
}
if (e.NewElement != null)
{
e.NewElement.PropertyChanged += OnElementPropertyChanged;
}
control1.ScalesPageToFit = true;
control1.ScrollView.Delegate = new MyDelegate(control1);
control1.ContentMode = UIViewContentMode.ScaleToFill;
control1.ScrollView.SizeToFit();
}
}
private void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
var control1 = NativeView as UIWebView;
if (control1 == null)
{
return;
}
control1.ScalesPageToFit = true;
ScrollView.MaximumZoomScale = 2;
ScrollView.MinimumZoomScale = nfloat.Parse("0.5");
control1.ScrollView.MaximumZoomScale = 2;
control1.ScrollView.SizeToFit();
control1.ScrollView.MinimumZoomScale = nfloat.Parse("0.5");
}
}
}
you could try this,is it the effect you need(here is for Android)
1.create a custom view ZoomWebView:
public class ZoomWebView : View
{
public static readonly BindableProperty UriProperty = BindableProperty.Create(
propertyName: "Uri",
returnType: typeof(string),
declaringType: typeof(ZoomWebView),
defaultValue: default(string));
public string Uri {
get { return (string)GetValue (UriProperty); }
set { SetValue (UriProperty, value); }
}
}
2. custom renderer ZoomWebViewRenderer
public class ZoomWebViewRenderer : ViewRenderer<ZoomWebView, Android.Webkit.WebView>
{
Context _context;
public ZoomWebViewRenderer(Context context) : base(context)
{
_context = context;
}
protected override void OnElementChanged(ElementChangedEventArgs<ZoomWebView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
var webView = new Android.Webkit.WebView(_context);
webView.Settings.UseWideViewPort = true;
webView.Settings.LoadWithOverviewMode = true;
webView.Settings.SetSupportZoom(true);
webView.Settings.BuiltInZoomControls = true;
webView.Settings.DisplayZoomControls = false;
webView.SetWebViewClient(new ZoomWebViewClient());
SetNativeControl(webView);
Control.LoadUrl(Element.Uri);
}
}
class ZoomWebViewClient : WebViewClient
{
public override bool ShouldOverrideUrlLoading(WebView view, IWebResourceRequest request)
{
view.LoadUrl(request.Url.ToString());
return true;
}
}
}
3.call in the page xaml:
<ContentPage.Content>
<local:ZoomWebView Uri="https://www.microsoft.com" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" />
</ContentPage.Content>
I am trying to create Suggestion Searchbar using Entry
i was able to create the suggestion searchbar but i am not able to change the look of this Entry, i want to remove the tooltip above (Search by Speciality..) and just want it to look like other entry in the app
could anyone help me what and where i had to make change for getting that look
here is my code
Xamarin Android :
[assembly: ExportRenderer(typeof(AutoCompleteViewv2),
typeof(AutoCompleteViewRendererv2))]
namespace App.Droid.Renderers.RevisedRenderer
{
public class AutoCompleteViewRendererv2 :
FormsAppCompat.ViewRenderer<AutoCompleteViewv2, TextInputLayout>
{
public AutoCompleteViewRendererv2(Context context) : base(context)
{
}
private AppCompatAutoCompleteTextView NativeControl => Control?.EditText
as AppCompatAutoCompleteTextView;
protected override TextInputLayout CreateNativeControl()
{
var textInputLayout = new TextInputLayout(Context);
var autoComplete = new AppCompatAutoCompleteTextView(Context)
{
BackgroundTintList = ColorStateList.ValueOf(GetPlaceholderColor()),
Text = Element?.Text,
Hint = Element?.Placeholder,
};
GradientDrawable gd = new GradientDrawable();
gd.SetColor(global::Android.Graphics.Color.Transparent);
autoComplete.SetBackground(gd);
if (Element != null)
{
autoComplete.SetHintTextColor(Element.PlaceholderColor.ToAndroid());
autoComplete.SetTextColor(Element.TextColor.ToAndroid());
}
textInputLayout.AddView(autoComplete);
return textInputLayout;
}
protected override void OnElementChanged(ElementChangedEventArgs<AutoCompleteViewv2> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
// unsubscribe
NativeControl.ItemClick -= AutoCompleteOnItemSelected;
var elm = e.OldElement;
elm.CollectionChanged -= ItemsSourceCollectionChanged;
}
if (e.NewElement != null)
{
SetNativeControl(CreateNativeControl());
// subscribe
SetItemsSource();
SetThreshold();
KillPassword();
NativeControl.TextChanged += (s, args) => Element.RaiseTextChanged(NativeControl.Text);
NativeControl.ItemClick += AutoCompleteOnItemSelected;
var elm = e.NewElement;
elm.CollectionChanged += ItemsSourceCollectionChanged;
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == Entry.IsPasswordProperty.PropertyName)
KillPassword();
if (e.PropertyName == AutoCompleteViewv2.ItemsSourceProperty.PropertyName)
SetItemsSource();
else if (e.PropertyName == AutoCompleteViewv2.ThresholdProperty.PropertyName)
SetThreshold();
}
private void AutoCompleteOnItemSelected(object sender, AdapterView.ItemClickEventArgs args)
{
var view = (AutoCompleteTextView)sender;
var selectedItemArgs = new SelectedItemChangedEventArgs(view.Text);
var element = (AutoCompleteViewv2)Element;
element.OnItemSelectedInternal(Element, selectedItemArgs);
}
private void ItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
{
var element = (AutoCompleteViewv2)Element;
ResetAdapter(element);
}
private void KillPassword()
{
if (Element.IsPassword)
throw new NotImplementedException("Cannot set IsPassword on a AutoComplete");
}
private void ResetAdapter(AutoCompleteViewv2 element)
{
var adapter = new BoxArrayAdapter(Context,
Android.Resource.Layout.SimpleDropDownItem1Line,
element.ItemsSource.ToList(),
element.SortingAlgorithm);
NativeControl.Adapter = adapter;
adapter.NotifyDataSetChanged();
}
private void SetItemsSource()
{
var element = (AutoCompleteViewv2)Element;
if (element.ItemsSource == null) return;
ResetAdapter(element);
}
private void SetThreshold()
{
var element = (AutoCompleteViewv2)Element;
NativeControl.Threshold = element.Threshold;
}
#region Section 2
protected AColor GetPlaceholderColor() => Element.PlaceholderColor.ToAndroid(Color.FromHex("#80000000"));
#endregion
}
internal class BoxArrayAdapter : ArrayAdapter
{
private readonly IList<string> _objects;
private readonly Func<string, ICollection<string>, ICollection<string>> _sortingAlgorithm;
public BoxArrayAdapter(
Context context,
int textViewResourceId,
List<string> objects,
Func<string, ICollection<string>, ICollection<string>> sortingAlgorithm) : base(context, textViewResourceId, objects)
{
_objects = objects;
_sortingAlgorithm = sortingAlgorithm;
}
public override Filter Filter
{
get
{
return new CustomFilter(_sortingAlgorithm) { Adapter = this, Originals = _objects };
}
}
}
internal class CustomFilter : Filter
{
private readonly Func<string, ICollection<string>, ICollection<string>> _sortingAlgorithm;
public CustomFilter(Func<string, ICollection<string>, ICollection<string>> sortingAlgorithm)
{
_sortingAlgorithm = sortingAlgorithm;
}
public BoxArrayAdapter Adapter { private get; set; }
public IList<string> Originals { get; set; }
protected override FilterResults PerformFiltering(ICharSequence constraint)
{
var results = new FilterResults();
if (constraint == null || constraint.Length() == 0)
{
results.Values = new Java.Util.ArrayList(Originals.ToList());
results.Count = Originals.Count;
}
else
{
var values = new Java.Util.ArrayList();
var sorted = _sortingAlgorithm(constraint.ToString(), Originals).ToList();
for (var index = 0; index < sorted.Count; index++)
{
var item = sorted[index];
values.Add(item);
}
results.Values = values;
results.Count = sorted.Count;
}
return results;
}
protected override void PublishResults(ICharSequence constraint, FilterResults results)
{
if (results.Count == 0)
Adapter.NotifyDataSetInvalidated();
else
{
Adapter.Clear();
var vals = (Java.Util.ArrayList)results.Values;
foreach (var val in vals.ToArray())
{
Adapter.Add(val);
}
Adapter.NotifyDataSetChanged();
}
}
}
}
how can i get rid of that tooltip or placeholder and make the text in middle
i have created one custom entry field with floating label using custom renderer like from this uri https://github.com/AlejandroRuiz/FloatingTextEntry
i need to change entry underline color as well placeholder color on button click .
Custom Entry Code :
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using Xamarin.Forms;
namespace BloodTrace.CustomControls
{
public class XfxEntry : Entry
{
public static readonly BindableProperty ErrorTextProperty = BindableProperty.Create(nameof(ErrorText),
typeof(string),
typeof(XfxEntry),
default(string), propertyChanged: OnErrorTextChangedInternal);
public static readonly BindableProperty FloatingHintEnabledProperty = BindableProperty.Create(nameof(FloatingHintEnabled),
typeof(bool),
typeof(XfxEntry),
true);
public static readonly BindableProperty ActivePlaceholderColorProperty = BindableProperty.Create(nameof(ActivePlaceholderColor),
typeof(Color),
typeof(XfxEntry),
Color.Accent);
/// <summary>
/// ActivePlaceholderColor summary. This is a bindable property.
/// </summary>
public Color ActivePlaceholderColor
{
get { return (Color)GetValue(ActivePlaceholderColorProperty); }
set { SetValue(ActivePlaceholderColorProperty, value); }
}
public Color ErrorPlaceholderColor
{
get { return (Color)GetValue(ErrorPlaceholderColorProperty); }
set { SetValue(ErrorPlaceholderColorProperty, value); }
}
public static readonly BindableProperty ErrorPlaceholderColorProperty = BindableProperty.Create(nameof(ErrorPlaceholderColor),
typeof(Color),
typeof(XfxEntry),
Color.Green);
/// <summary>
/// <c>true</c> to float the hint into a label, otherwise <c>false</c>. This is a bindable property.
/// </summary>
public bool FloatingHintEnabled
{
get { return (bool)GetValue(FloatingHintEnabledProperty); }
set { SetValue(FloatingHintEnabledProperty, value); }
}
/// <summary>
/// Gets or Sets whether or not the Error Style is 'Underline' or 'None'
/// </summary>
public ErrorDisplay ErrorDisplay { get; set; } = ErrorDisplay.None;
public bool IsErrorPlaceholderColorChange { get; set; } = false;
/// <summary>
/// Error text for the entry. An empty string removes the error. This is a bindable property.
/// </summary>
public string ErrorText
{
get { return (string)GetValue(ErrorTextProperty); }
set { SetValue(ErrorTextProperty, value); }
}
/// <summary>
/// Raised when the value of the error text changes
/// </summary>
public event EventHandler<TextChangedEventArgs> ErrorTextChanged;
private static void OnErrorTextChangedInternal(BindableObject bindable, object oldvalue, object newvalue)
{
var materialEntry = (XfxEntry)bindable;
materialEntry.OnErrorTextChanged(bindable, oldvalue, newvalue);
materialEntry.ErrorTextChanged?.Invoke(materialEntry, new TextChangedEventArgs((string)oldvalue, (string)newvalue));
}
protected virtual void OnErrorTextChanged(BindableObject bindable, object oldvalue, object newvalue) { }
}
public enum ErrorDisplay
{
Underline,
None
}
}
Android Renderer : -
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using BloodTrace;
using BloodTrace.Droid.Renderer;
using Application = Android.App.Application;
using Color = Xamarin.Forms.Color;
using AColor = Android.Graphics.Color;
using FormsAppCompat = Xamarin.Forms.Platform.Android.AppCompat;
using BloodTrace.CustomControls;
using Android.Support.Design.Widget;
using Android.Text;
using Xamarin.Forms;
using Android.Content.Res;
using Android.Views.InputMethods;
using Java.Lang;
using Android.Support.V7.Widget;
using Android.Util;
using Xamarin.Forms.Platform.Android;
using System.ComponentModel;
using Android.Text.Method;
using Android.Support.V4.View;
using BloodTrace.Droid.Extensions;
using BloodTraceSharedProject.Extensions;
[assembly: ExportRenderer(typeof(XfxEntry), typeof(XfxEntryRendererDroid))]
namespace BloodTrace.Droid.Renderer
{
public class XfxEntryRendererDroid : FormsAppCompat.ViewRenderer<XfxEntry, TextInputLayout>,
ITextWatcher,
TextView.IOnEditorActionListener
{
private bool _hasFocus;
private ColorStateList _defaultTextColor;
public XfxEntryRendererDroid(Context context) : base(context)
{
AutoPackage = false;
}
protected EditText EditText => Control.EditText;
public bool OnEditorAction(TextView v, ImeAction actionId, KeyEvent e)
{
if ((actionId == ImeAction.Done) || ((actionId == ImeAction.ImeNull) && (e.KeyCode == Keycode.Enter)))
{
Control.ClearFocus();
HideKeyboard();
((IEntryController)Element).SendCompleted();
}
return true;
}
public virtual void AfterTextChanged(IEditable s)
{
}
public virtual void BeforeTextChanged(ICharSequence s, int start, int count, int after)
{
}
public virtual void OnTextChanged(ICharSequence s, int start, int before, int count)
{
if (string.IsNullOrWhiteSpace(Element.Text) && (s.Length() == 0)) return;
((IElementController)Element).SetValueFromRenderer(Entry.TextProperty, s.ToString());
}
protected override TextInputLayout CreateNativeControl()
{
var textInputLayout = new TextInputLayout(Context);
var editText = new AppCompatEditText(Context)
{
SupportBackgroundTintList = ColorStateList.ValueOf(GetPlaceholderColor())
};
editText.SetTextSize(ComplexUnitType.Sp, (float)Element.FontSize);
textInputLayout.AddView(editText);
return textInputLayout;
}
protected override void OnElementChanged(ElementChangedEventArgs<XfxEntry> e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
if (Control != null)
EditText.FocusChange -= ControlOnFocusChange;
if (e.NewElement != null)
{
var ctrl = CreateNativeControl();
SetNativeControl(ctrl);
if (!string.IsNullOrWhiteSpace(Element.AutomationId))
EditText.ContentDescription = Element.AutomationId;
_defaultTextColor = EditText.TextColors;
Focusable = true;
EditText.ShowSoftInputOnFocus = true;
// Subscribe
EditText.FocusChange += ControlOnFocusChange;
EditText.AddTextChangedListener(this);
EditText.SetOnEditorActionListener(this);
EditText.ImeOptions = ImeAction.Done;
SetText();
SetHintText();
SetErrorText();
SetFontAttributesSizeAndFamily();
SetInputType();
SetTextColor();
SetHorizontalTextAlignment();
SetFloatingHintEnabled();
SetIsEnabled();
SetErrorDisplay();
SetLabelAndUnderlineColor();
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == Entry.PlaceholderProperty.PropertyName)
SetHintText();
else if (e.PropertyName == XfxEntry.ErrorTextProperty.PropertyName)
SetErrorText();
else if (e.PropertyName == Entry.IsPasswordProperty.PropertyName ||
e.PropertyName == InputView.KeyboardProperty.PropertyName)
SetInputType();
else if (e.PropertyName == Entry.TextProperty.PropertyName)
SetText();
else if (e.PropertyName == Entry.HorizontalTextAlignmentProperty.PropertyName)
SetHorizontalTextAlignment();
else if (e.PropertyName == XfxEntry.FloatingHintEnabledProperty.PropertyName)
SetFloatingHintEnabled();
else if (e.PropertyName == VisualElement.IsEnabledProperty.PropertyName)
SetIsEnabled();
else if ((e.PropertyName == Entry.FontAttributesProperty.PropertyName) ||
(e.PropertyName == Entry.FontFamilyProperty.PropertyName) ||
(e.PropertyName == Entry.FontSizeProperty.PropertyName))
SetFontAttributesSizeAndFamily();
else if (e.PropertyName == XfxEntry.ActivePlaceholderColorProperty.PropertyName ||
e.PropertyName == Entry.PlaceholderColorProperty.PropertyName)
SetLabelAndUnderlineColor();
else if (e.PropertyName == Entry.TextColorProperty.PropertyName)
SetTextColor();
}
private void ControlOnFocusChange(object sender, FocusChangeEventArgs args)
{
_hasFocus = args.HasFocus;
if (_hasFocus)
{
var manager = (InputMethodManager)Application.Context.GetSystemService(Context.InputMethodService);
EditText.PostDelayed(() =>
{
EditText.RequestFocus();
manager.ShowSoftInput(EditText, 0);
},
100);
}
var isFocusedPropertyKey = Element.GetInternalField<BindablePropertyKey>("IsFocusedPropertyKey");
((IElementController)Element).SetValueFromRenderer(isFocusedPropertyKey, _hasFocus);
//This will change floating label entry SetUnderlineColor >>>> SetUnderlineColor color basedon focus or non focus
SetUnderlineColor(_hasFocus ? GetActivePlaceholderColor() : GetPlaceholderColor());
}
protected AColor GetPlaceholderColor() => Element.PlaceholderColor.ToAndroid(Color.FromHex("#80000000"));
private AColor GetActivePlaceholderColor() => Element.ActivePlaceholderColor.ToAndroid(global::Android.Resource.Attribute.ColorAccent, Context);
protected virtual void SetLabelAndUnderlineColor()
{
var defaultColor = GetPlaceholderColor();
var activeColor = GetActivePlaceholderColor();
SetHintLabelDefaultColor(defaultColor);
SetHintLabelActiveColor(activeColor);
SetUnderlineColor(_hasFocus ? activeColor : defaultColor);
}
//This will change entry under line color
private void SetUnderlineColor(AColor color)
{
var element = (ITintableBackgroundView)EditText;
element.SupportBackgroundTintList = ColorStateList.ValueOf(color);
}
private void SetHintLabelActiveColor(AColor color)
{
var hintText = Control.Class.GetDeclaredField("mFocusedTextColor");
hintText.Accessible = true;
hintText.Set(Control, new ColorStateList(new int[][] { new[] { 0 } }, new int[] { color }));
}
private void SetHintLabelDefaultColor(AColor color)
{
var hint = Control.Class.GetDeclaredField("mDefaultTextColor");
hint.Accessible = true;
hint.Set(Control, new ColorStateList(new int[][] { new[] { 0 } }, new int[] { color }));
}
private void SetText()
{
if (EditText.Text != Element.Text)
{
EditText.Text = Element.Text;
if (EditText.IsFocused)
EditText.SetSelection(EditText.Text.Length);
}
}
private void SetHintText()
{
Control.Hint = Element.Placeholder;
}
private void SetTextColor()
{
if (Element.TextColor == Color.Default)
{
EditText.SetTextColor(_defaultTextColor);
}
else
{
EditText.SetTextColor(Element.TextColor.ToAndroid());
}
}
private void SetHorizontalTextAlignment()
{
switch (Element.HorizontalTextAlignment)
{
case Xamarin.Forms.TextAlignment.Center:
EditText.Gravity = GravityFlags.CenterHorizontal;
break;
case Xamarin.Forms.TextAlignment.End:
EditText.Gravity = GravityFlags.Right;
break;
default:
EditText.Gravity = GravityFlags.Left;
break;
}
}
public void SetFloatingHintEnabled()
{
Control.HintEnabled = Element.FloatingHintEnabled;
Control.HintAnimationEnabled = Element.FloatingHintEnabled;
SetFontAttributesSizeAndFamily();
}
public void SetErrorDisplay()
{
switch (Element.ErrorDisplay)
{
case ErrorDisplay.None:
Control.ErrorEnabled = false;
break;
case ErrorDisplay.Underline:
Control.ErrorEnabled = true;
break;
}
}
protected void HideKeyboard()
{
var manager = (InputMethodManager)Application.Context.GetSystemService(Context.InputMethodService);
manager.HideSoftInputFromWindow(EditText.WindowToken, 0);
}
//here we can set entry floating label >>> input text size
private void SetFontAttributesSizeAndFamily()
{
EditText.Typeface = Control.Typeface = Element.ToTypeface();
EditText.SetTextSize(ComplexUnitType.Sp, (float)Element.FontSize);
//var a = (float)Element.FontSize; //18
}
private void SetErrorText()
{
Control.Error = !string.IsNullOrEmpty(Element.ErrorText) ? Element.ErrorText : null;
}
private void SetIsEnabled()
{
EditText.Enabled = Element.IsEnabled;
}
private void SetInputType()
{
EditText.InputType = Element.Keyboard.ToInputType();
if (Element.IsPassword && (EditText.InputType & InputTypes.ClassText) == InputTypes.ClassText)
{
EditText.TransformationMethod = new PasswordTransformationMethod();
EditText.InputType = EditText.InputType | InputTypes.TextVariationPassword;
}
if (Element.IsPassword && (EditText.InputType & InputTypes.ClassNumber) == InputTypes.ClassNumber)
{
EditText.TransformationMethod = new PasswordTransformationMethod();
EditText.InputType = EditText.InputType | InputTypes.NumberVariationPassword;
}
}
}
}
XML : -
<xfx:XfxEntry x:Name="EntPassword" Placeholder="Password" IsPassword="True" Focused="EntPassword_Focused"></xfx:XfxEntry>
<Button Text="Sign Up" BackgroundColor="#C62729" TextColor="White" x:Name="BtnSignUp" Clicked="BtnSignUp_Clicked" ></Button>
Please help.
Thanks in advance
I have a custom checkbox control that I created with an ICommand property and the corresponding bindable property (my checkbox is a Xamarin.Forms XAML Page), the code is:
CheckBox.xaml
<Image x:Name="imgCheckBox"
WidthRequest="20"
HeightRequest="20"/>
<Label x:Name="lblCheckBox"
TextColor="Black"
VerticalOptions="CenterAndExpand"/>
<TapGestureRecognizer Tapped="OnCheckBoxTapped"/>
CheckBox.xaml.cs
public partial class CheckBox : ContentView
{
private static ImageSource uncheckedImage;
private static ImageSource checkedImage;
public CheckBox()
{
InitializeComponent();
uncheckedImage = ImageSource.FromResource("cbUnchecked.png");
checkedImage = ImageSource.FromResource("cbChecked.png");
imgCheckBox.Source = uncheckedImage;
}
public static readonly BindableProperty IsCheckedProperty =
BindableProperty.Create<CheckBox, bool>(
checkbox =>
checkbox.IsChecked,
false,
propertyChanged: (bindable, oldValue, newValue) =>
{
CheckBox checkbox = (CheckBox)bindable;
EventHandler<bool> eventHandler = checkbox.CheckedChanged;
if (eventHandler != null)
{
eventHandler(checkbox, newValue);
}
});
public bool IsChecked
{
set { SetValue(IsCheckedProperty, value); }
get { return (bool)GetValue(IsCheckedProperty); }
}
void OnCheckBoxTapped(object sender, EventArgs args)
{
IsChecked = !IsChecked;
if (IsChecked)
{
imgCheckBox.Source = checkedImage;
}
else
{
imgCheckBox.Source = uncheckedImage;
}
}
public static readonly BindableProperty CheckBoxCommandProperty =
BindableProperty.Create<CheckBox, ICommand>(
checkbox =>
checkbox.CheckBoxCommand,
null,
BindingMode.TwoWay,
propertyChanged: (bindable, oldValue, newValue) =>
{
CheckBox checkbox = (CheckBox)bindable;
EventHandler<bool> eventHandler = checkbox.CheckedChanged;
if (eventHandler != null)
{
eventHandler(checkbox, checkbox.IsChecked);
}
});
public event EventHandler<bool> CheckedChanged;
public ICommand CheckBoxCommand
{
get { return (ICommand)GetValue(CheckBoxCommandProperty); }
set { SetValue(CheckBoxCommandProperty, value); }
}
}
This checkbox implementation is on another Page called TermsAndConditionsPage, that is also a a Xamarin.Forms XAML Page, the code of the implementation is:
<toolkit:CheckBox Text="{Binding txtCheckBox}"
FontSize="Small"
CheckBoxCommand="{Binding OnCheckBoxTapChanged}"
IsChecked="{Binding IsCheckedChanged, Mode=TwoWay}"/>
<Button Text="Next"
Command="{Binding Next_OnClick}"
IsEnabled="{Binding Next_IsEnabled}"
HorizontalOptions="CenterAndExpand"
Clicked="OnNextClicked"/>
The Code Behind of this page is empty (Constructur with InitializeComponent()).
I also have the ViewModel of this page with this code:
TermsAndConditionsViewModel.cs
private string _txtCheckBox;
public string txtCheckBox
{ get { return _txtCheckBox; }
set
{
_txtCheckBox = value;
OnPropertyChanged("txtCheckBox");
}
}
private bool _Next_IsEnabled;
public bool Next_IsEnabled
{
get { return _Next_IsEnabled; }
set
{
_Next_IsEnabled = value;
OnPropertyChanged("Next_IsEnabled");
}
}
private bool _IsCheckedChanged;
public bool IsCheckedChanged
{
get { return _IsCheckedChanged; }
set
{
_IsCheckedChanged = value;
OnPropertyChanged("IsCheckedChanged");
}
}
public ICommand Next_OnClick { get; set; }
public ICommand OnCheckBoxTapChanged { get; set; }
public TermsAndConditionsViewModel()
{
txtCheckBox = "I agree with the terms and conditions";
Next_OnClick = new Command(NextClicked);
OnCheckBoxTapChanged = new Command(CheckBoxTapped);
}
private void CheckBoxTapped()
{
if (IsCheckedChanged)
{ Next_IsEnabled = true; }
else
{ Next_IsEnabled = false; }
}
private void NextClicked()
{ App.Current.MainPage = new Views.HelloWorld(); }
#region INPC
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{ PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); }
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
Now, the question time: the problem I'm having is the CheckBoxTapped Command is not working, I mean, it doesn't do anything, although the checkbox image changes every time I touch it, it does not change the Next_IsEnabled property of my button. I'd like to know what I am missing here to make this command work properly.
EDIT
What I'm looking for is a Command that behaves similarly to the one that Buttons have.
Thanks all for your time!
Since the original answer is now obsolete, here is the new method:
using System.Windows.Input;
public partial class MyControlExample : ContentView
{
// BindableProperty implementation
public static readonly BindableProperty CommandProperty =
BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(MyControlExample), null);
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
// Helper method for invoking commands safely
public static void Execute(ICommand command)
{
if (command == null) return;
if (command.CanExecute(null))
{
command.Execute(null);
}
}
public MyControlExample()
{
InitializeComponent();
}
// this is the command that gets bound by the control in the view
// (ie. a Button, TapRecognizer, or MR.Gestures)
public Command OnTap => new Command(() => Execute(Command));
}
Something like that (pseudocode):
public class YourClassName : View
{
public YourClassName()
{
var gestureRecognizer = new TapGestureRecognizer();
gestureRecognizer.Tapped += (s, e) => {
if (Command != null && Command.CanExecute(null)) {
Command.Execute(null);
}
};
var label = new Label();
label.GestureRecognizers.Add(gestureRecognizer);
}
public static readonly BindableProperty CommandProperty =
BindableProperty.Create<YourClassName, ICommand>(x => x.Command, null);
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
}