I have found a custom renderer online but there is an error. Does anyone know how can I make a date time control? Currently I am using separate date picker and time picker but I want it to be combined.
I will post the code below that I have found from another post. This is the link to the post Xamarin Forms date and time picker
using System;
using Foundation;
using Test;
using Test.Droid;
using UIKit;
using ObjCRuntime;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly:ExportRenderer(typeof(MyPicker), typeof(MyPickerRenderer))]
namespace Test.Droid
{
public class MyPickerRenderer : PickerRenderer
{
string SelectedValue;
[Obsolete]
public MyPickerRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
base.OnElementChanged(e);
if (Control != null)
{
SetTimePicker();
}
}
void SetTimePicker()
{
UIDatePicker picker = new UIDatePicker
{
Mode = UIDatePickerMode.DateAndTime
};
picker.SetDate(NSDate.Now, true);
picker.AddTarget(this, new Selector("DateChange:"), UIControlEvent.ValueChanged);
Control.InputView = picker;
UIToolbar toolbar = (UIToolbar)Control.InputAccessoryView;
UIBarButtonItem done = new UIBarButtonItem("Done", UIBarButtonItemStyle.Done, (object sender, EventArgs click) =>
{
Control.Text = SelectedValue;
toolbar.RemoveFromSuperview();
picker.RemoveFromSuperview();
Control.ResignFirstResponder();
MessagingCenter.Send<Object, string>(this, "pickerSelected", SelectedValue);
});
UIBarButtonItem empty = new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace, null);
toolbar.Items = new UIBarButtonItem[] { empty, done };
}
[Export("DateChange:")]
void DateChange(UIDatePicker picker)
{
NSDateFormatter formatter = new NSDateFormatter();
formatter.DateFormat = "MM-dd HH:mm aa"; //you can set the format as you want
Control.Text = formatter.ToString(picker.Date);
SelectedValue = formatter.ToString(picker.Date);
MessagingCenter.Send<Object, string>(this, "pickerSelected", SelectedValue);
}
}
}
Make the DateTimePicker inherit a ContentView instead of just an Entry, and then creates the Stacklayout which add the Entry and the date and time pickers to content.
See the DateTimePicker2.cs:
public class DateTimePicker2 : ContentView, INotifyPropertyChanged
{
public Entry _entry { get; private set; } = new Entry();
public DatePicker _datePicker { get; private set; } = new DatePicker() { MinimumDate = DateTime.Today, IsVisible = false };
public TimePicker _timePicker { get; private set; } = new TimePicker() { IsVisible = false };
string _stringFormat { get; set; }
public string StringFormat { get { return _stringFormat ?? "dd/MM/yyyy HH:mm"; } set { _stringFormat = value; } }
public DateTime DateTime
{
get { return (DateTime)GetValue(DateTimeProperty); }
set { SetValue(DateTimeProperty, value); OnPropertyChanged("DateTime"); }
}
private TimeSpan _time
{
get
{
return TimeSpan.FromTicks(DateTime.Ticks);
}
set
{
DateTime = new DateTime(DateTime.Date.Ticks).AddTicks(value.Ticks);
}
}
private DateTime _date
{
get
{
return DateTime.Date;
}
set
{
DateTime = new DateTime(DateTime.TimeOfDay.Ticks).AddTicks(value.Ticks);
}
}
BindableProperty DateTimeProperty = BindableProperty.Create("DateTime", typeof(DateTime), typeof(DateTimePicker2), DateTime.Now, BindingMode.TwoWay, propertyChanged: DTPropertyChanged);
public DateTimePicker2()
{
BindingContext = this;
Content = new StackLayout()
{
Children =
{
_datePicker,
_timePicker,
_entry
}
};
_datePicker.SetBinding<DateTimePicker2>(DatePicker.DateProperty, p => p._date);
_timePicker.SetBinding<DateTimePicker2>(TimePicker.TimeProperty, p => p._time);
_timePicker.Unfocused += (sender, args) => _time = _timePicker.Time;
_datePicker.Focused += (s, a) => UpdateEntryText();
GestureRecognizers.Add(new TapGestureRecognizer()
{
Command = new Command(() => _datePicker.Focus())
});
_entry.Focused += (sender, args) =>
{
Device.BeginInvokeOnMainThread(() => _datePicker.Focus());
};
_datePicker.Unfocused += (sender, args) =>
{
Device.BeginInvokeOnMainThread(() =>
{
_timePicker.Focus();
_date = _datePicker.Date;
UpdateEntryText();
});
};
}
private void UpdateEntryText()
{
_entry.Text = DateTime.ToString(StringFormat);
}
static void DTPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var timePicker = (bindable as DateTimePicker2);
timePicker.UpdateEntryText();
}
}
Usage in Xaml:
<StackLayout>
<local:DateTimePicker2></local:DateTimePicker2>
</StackLayout>
To make it easier for anyone wanting to use the DateTimePicker with XAML integration (with a Binding), I merged answers from the original post of Wendy Zhang, the comments below it and the question (Custom control not being set to content width) from csharpdude77.
Code:
public class DateTimePicker : ContentView, INotifyPropertyChanged
{
private Entry _entry { get; set; } = new Entry() { WidthRequest = 300 };
private DatePicker _datePicker { get; set; } = new DatePicker() { MinimumDate = DateTime.Today, IsVisible = false };
private TimePicker _timePicker { get; set; } = new TimePicker() { IsVisible = false };
private string _stringFormat { get; set; }
private TimeSpan _time
{
get { return TimeSpan.FromTicks(DateTime.Ticks); }
set { DateTime = new DateTime(DateTime.Date.Ticks).AddTicks(value.Ticks); }
}
private DateTime _date
{
get { return DateTime.Date; }
set { DateTime = new DateTime(DateTime.TimeOfDay.Ticks).AddTicks(value.Ticks); }
}
public string StringFormat { get { return _stringFormat ?? "dd/MM/yyyy HH:mm"; } set { _stringFormat = value; } }
public DateTime DateTime
{
get { return (DateTime)GetValue(DateTimeProperty); }
set { SetValue(DateTimeProperty, value); OnPropertyChanged(nameof(DateTime)); }
}
public static BindableProperty DateTimeProperty = BindableProperty.Create(nameof(DateTime), typeof(DateTime), typeof(DateTimePicker), DateTime.Now, BindingMode.TwoWay, propertyChanged: DTPropertyChanged);
public DateTimePicker()
{
Content = new StackLayout()
{
Children =
{
_datePicker,
_timePicker,
_entry
}
};
_datePicker.SetBinding(DatePicker.DateProperty, nameof(_date));
_timePicker.SetBinding(TimePicker.TimeProperty, nameof(_time));
_timePicker.Unfocused += (sender, args) => _time = _timePicker.Time;
_datePicker.Focused += (s, a) => UpdateEntryText();
GestureRecognizers.Add(new TapGestureRecognizer()
{
Command = new Command(() => _datePicker.Focus())
});
_entry.Focused += (sender, args) =>
{
Device.BeginInvokeOnMainThread(() => _datePicker.Focus());
};
_datePicker.Unfocused += (sender, args) =>
{
Device.BeginInvokeOnMainThread(() =>
{
_timePicker.Focus();
_date = _datePicker.Date;
UpdateEntryText();
});
};
}
private void UpdateEntryText()
{
_entry.Text = DateTime.ToString(StringFormat);
}
private static void DTPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var timePicker = bindable as DateTimePicker;
timePicker.UpdateEntryText();
}
}
Sample XAML usage:
<StackLayout>
<local:DateTimePicker DateTime="{Binding Source={RelativeSource AncestorType={x:Type class:NewRitViewModel}}, Path=DateTime}" StringFormat="dd-MM-yyyy HH:mm"></local:DateTimePicker>
</StackLayout>
On Xamarin.forms Android, you could try the code below.
Create a DateTimePicker.cs class.
public class DateTimePicker : Entry, INotifyPropertyChanged
{
public DatePicker _datePicker { get; private set; } = new DatePicker() { MinimumDate = DateTime.Today, IsVisible = false };
public TimePicker _timePicker { get; private set; } = new TimePicker() { IsVisible = false };
string _stringFormat { get; set; }
public string StringFormat { get { return _stringFormat ?? "dd/MM/yyyy HH:mm"; } set { _stringFormat = value; } }
public DateTime DateTime
{
get { return (DateTime)GetValue(DateTimeProperty); }
set { SetValue(DateTimeProperty, value); OnPropertyChanged("DateTime"); }
}
private TimeSpan _time
{
get
{
return TimeSpan.FromTicks(DateTime.Ticks);
}
set
{
DateTime = new DateTime(DateTime.Date.Ticks).AddTicks(value.Ticks);
}
}
private DateTime _date
{
get
{
return DateTime.Date;
}
set
{
DateTime = new DateTime(DateTime.TimeOfDay.Ticks).AddTicks(value.Ticks);
}
}
BindableProperty DateTimeProperty = BindableProperty.Create("DateTime", typeof(DateTime), typeof(DateTimePicker), DateTime.Now, BindingMode.TwoWay, propertyChanged: DTPropertyChanged);
public DateTimePicker()
{
BindingContext = this;
_datePicker.SetBinding<DateTimePicker>(DatePicker.DateProperty, p => p._date);
_timePicker.SetBinding<DateTimePicker>(TimePicker.TimeProperty, p => p._time);
_timePicker.Unfocused += (sender, args) => _time = _timePicker.Time;
_datePicker.Focused += (s, a) => UpdateEntryText();
GestureRecognizers.Add(new TapGestureRecognizer()
{
Command = new Command(() => _datePicker.Focus())
});
Focused += (sender, args) =>
{
Device.BeginInvokeOnMainThread(() => _datePicker.Focus());
};
_datePicker.Unfocused += (sender, args) =>
{
Device.BeginInvokeOnMainThread(() =>
{
_timePicker.Focus();
_date = _datePicker.Date;
UpdateEntryText();
});
};
}
private void UpdateEntryText()
{
Text = DateTime.ToString(StringFormat);
}
static void DTPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var timePicker = (bindable as DateTimePicker);
timePicker.UpdateEntryText();
}
}
Uasge in App.xaml.cs
var dtPicker = new DateTimePicker()
{
VerticalOptions = LayoutOptions.FillAndExpand,
HorizontalOptions = LayoutOptions.FillAndExpand,
StringFormat = "HH:mm dd/MM/yyyy"
};
MainPage = new ContentPage
{
Content = new StackLayout
{
VerticalOptions = LayoutOptions.Center,
Children = {
dtPicker._datePicker,
dtPicker._timePicker,
dtPicker
},
BackgroundColor = Color.Aqua
}
};
This is my Code Behind
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
void OnDateSelected(object sender, DateChangedEventArgs args)
{
Recalculate();
}
void OnSwitchToggled(object sender, ToggledEventArgs args)
{
Recalculate();
}
void Recalculate()
{
TimeSpan timeSpan = endDatePicker.Date - startDatePicker.Date +
(includeSwitch.IsToggled ? TimeSpan.FromDays(1) : TimeSpan.Zero);
resultLabel.Text = String.Format("{0} day{1} between dates",
timeSpan.Days, timeSpan.Days == 1 ? "" : "s");
}
}
I have attached code for date time picker
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DaysBetweenDates"
x:Class="DaysBetweenDates.MainPage">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="0, 20, 0, 0" />
</OnPlatform>
</ContentPage.Padding>
<StackLayout Margin="10">
<Label Text="Days Between Dates"
Style="{DynamicResource TitleStyle}"
Margin="0, 20"
HorizontalTextAlignment="Center" />
<Label Text="Start Date:" />
<DatePicker x:Name="startDatePicker"
Format="D"
Margin="30, 0, 0, 30"
DateSelected="OnDateSelected" />
<Label Text="End Date:" />
<DatePicker x:Name="endDatePicker"
MinimumDate="{Binding Source={x:Reference startDatePicker},
Path=Date}"
Format="D"
Margin="30, 0, 0, 30"
DateSelected="OnDateSelected" />
<StackLayout Orientation="Horizontal"
Margin="0, 0, 0, 30">
<Label Text="Include both days in total: "
VerticalOptions="Center" />
<Switch x:Name="includeSwitch"
Toggled="OnSwitchToggled" />
</StackLayout>
<Label x:Name="resultLabel"
FontAttributes="Bold"
HorizontalTextAlignment="Center" />
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:DaysBetweenDates"
x:Class="DaysBetweenDates.MainPage">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="0, 20, 0, 0" />
</OnPlatform>
</ContentPage.Padding>
<StackLayout Margin="10">
<Label Text="Days Between Dates"
Style="{DynamicResource TitleStyle}"
Margin="0, 20"
HorizontalTextAlignment="Center" />
<Label Text="Start Date:" />
<DatePicker x:Name="startDatePicker"
Format="D"
Margin="30, 0, 0, 30"
DateSelected="OnDateSelected" />
<Label Text="End Date:" />
<DatePicker x:Name="endDatePicker"
MinimumDate="{Binding Source={x:Reference startDatePicker},
Path=Date}"
Format="D"
Margin="30, 0, 0, 30"
DateSelected="OnDateSelected" />
<StackLayout Orientation="Horizontal"
Margin="0, 0, 0, 30">
<Label Text="Include both days in total: "
VerticalOptions="Center" />
<Switch x:Name="includeSwitch"
Toggled="OnSwitchToggled" />
</StackLayout>
<Label x:Name="resultLabel"
FontAttributes="Bold"
HorizontalTextAlignment="Center" />
</StackLayout>
Related
i want to display a picker (or list) when i click in my imageButton but it didn't work, but when i create only a picker , it worked.
Xaml
<StackLayout Orientation="Horizontal" x:Name="stacklayout">
<Entry Placeholder="préciser l'entité correspondante"
ClearButtonVisibility="WhileEditing" x:Name="entit"/>
<ImageButton Source="list.png" WidthRequest="30" HeightRequest="30" x:Name="listEntité" Clicked="listEntité_Clicked"/>
Xaml.cs
private void listEntité_Clicked(object sender, EventArgs e)
{
Picker p = new Picker();
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("http://192.168.1.3:3000/api/adepApi/GetCurrencyLists");
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await httpClient.GetAsync("http://192.168.1.3:3000/api/adepApi/GetCurrencyLists");
var content = await response.Content.ReadAsStringAsync();
ResponseDataD EL = JsonConvert.DeserializeObject<ResponseDataD>(content);
p.ItemsSource= EL.Data.DeviseList;
stacklayout.Children.Add(p.ItemsSource);
}
class DeviseModel
public class DeviseModel
{
public class DeviseL
{
// public string devis;
[JsonProperty("Label")]
public string Devis { get; set; }
[JsonProperty("Value")]
public int id { get; set; }
}
public class ResponseDataD
{
public RootModelDevise Data;
}
public class RootModelDevise : INotifyPropertyChanged
{
List<DeviseL> deviseList;
[JsonProperty("list")]
public List<DeviseL> DeviseList
{
get { return deviseList; }
set
{
if (deviseList != value)
{
deviseList = value;
OnPropertyChanged();
}
}
}
DeviseL itemDevise;
public DeviseL ItemDevise
{
get { return itemDevise; }
set
{
if (itemDevise != value)
{
itemDevise = value;
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}}
i have an error in the line
stacklayout.Children.Add(p.ItemsSource);
under p.itemsSource :unable to convert from 'System.Collections.Ilist' to 'Xamarin.Forms.View'
i have tried to add picker by imageButton click event , but nothing appears.
Firstly, please confirm that the EL.Data.DeviseList have list value, you have list binding to Picker, then adding picker to current contentPage.
public partial class Page16 : ContentPage
{
private List<string> list;
public Page16()
{
InitializeComponent();
list = new List<string>();
list.Add("test 1");
list.Add("test 2");
list.Add("test 3");
list.Add("test 4");
}
private void imagebutton1_Clicked(object sender, EventArgs e)
{
Picker p = new Picker();
p.ItemsSource = list;
stacklayout.Children.Add(p);
}
}
<StackLayout x:Name="stacklayout">
<ImageButton
x:Name="imagebutton1"
Clicked="imagebutton1_Clicked"
HeightRequest="30"
Source="plu3.png"
WidthRequest="30" />
</StackLayout>
Update:
private void imagebutton1_Clicked(object sender, EventArgs e)
{
Picker p = new Picker();
p.SelectedIndexChanged += P_SelectedIndexChanged;
p.ItemsSource = devs;
p.ItemDisplayBinding = new Binding("Devis");
stacklayout.Children.Add(p);
}
private void P_SelectedIndexChanged(object sender, EventArgs e)
{
Picker p = sender as Picker;
DeviseL selectitem = p.SelectedItem as DeviseL;
entit.Text =selectitem.Devis;
}
I have multiple Entry views in my page connecting to a View Model using Data Binding. Everything works as intended except I need to calculate a values as the user enters it.
public decimal DownPayment
{
get => this.loanValues.DownPayment;
set
{
this.loanValues.DownPayment = value;
this.CalculateValues();
}
}
public decimal TradeInPrice
{
get => this.loanValues.TradeInPrice;
set
{
this.loanValues.TradeInPrice = value;
this.CalculateValues();
}
}
// Other properties
Now the problem is the property is set whenever user is entering the value. Is there anyway to change the event on which the Data Binding happens (i.e on Completed instead of TextChanged)?
I see there is UpdateSourceEventName property in XAML binding but I don't know why it doesn't work:
<Entry Placeholder="Down Payment ($)" Text="{Binding DownPayment, UpdateSourceEventName=Completed}" />
UPDATE: The documentation of UpdateSourceEventName says it's For internal use by the Xamarin.Forms platform
You could use EventToCommandBehavior to convert the Event Completed of Entry to Command and handle the logic in ViewModel .
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;
namespace App18
{
public class BehaviorBase<T> : Behavior<T> where T : BindableObject
{
public T AssociatedObject { get; private set; }
protected override void OnAttachedTo(T bindable)
{
base.OnAttachedTo(bindable);
AssociatedObject = bindable;
if (bindable.BindingContext != null)
{
BindingContext = bindable.BindingContext;
}
bindable.BindingContextChanged += OnBindingContextChanged;
}
protected override void OnDetachingFrom(T bindable)
{
base.OnDetachingFrom(bindable);
bindable.BindingContextChanged -= OnBindingContextChanged;
AssociatedObject = null;
}
void OnBindingContextChanged(object sender, EventArgs e)
{
OnBindingContextChanged();
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
BindingContext = AssociatedObject.BindingContext;
}
}
}
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Windows.Input;
using Xamarin.Forms;
namespace App18
{
public class EventToCommandBehavior : BehaviorBase<View>
{
Delegate eventHandler;
public static readonly BindableProperty EventNameProperty = BindableProperty.Create("EventName", typeof(string), typeof(EventToCommandBehavior), null, propertyChanged: OnEventNameChanged);
public static readonly BindableProperty CommandProperty = BindableProperty.Create("Command", typeof(ICommand), typeof(EventToCommandBehavior), null);
public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create("CommandParameter", typeof(object), typeof(EventToCommandBehavior), null);
public static readonly BindableProperty InputConverterProperty = BindableProperty.Create("Converter", typeof(IValueConverter), typeof(EventToCommandBehavior), null);
public string EventName
{
get { return (string)GetValue(EventNameProperty); }
set { SetValue(EventNameProperty, value); }
}
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public object CommandParameter
{
get { return GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
public IValueConverter Converter
{
get { return (IValueConverter)GetValue(InputConverterProperty); }
set { SetValue(InputConverterProperty, value); }
}
protected override void OnAttachedTo(View bindable)
{
base.OnAttachedTo(bindable);
RegisterEvent(EventName);
}
protected override void OnDetachingFrom(View bindable)
{
DeregisterEvent(EventName);
base.OnDetachingFrom(bindable);
}
void RegisterEvent(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
return;
}
EventInfo eventInfo = AssociatedObject.GetType().GetRuntimeEvent(name);
if (eventInfo == null)
{
throw new ArgumentException(string.Format("EventToCommandBehavior: Can't register the '{0}' event.", EventName));
}
MethodInfo methodInfo = typeof(EventToCommandBehavior).GetTypeInfo().GetDeclaredMethod("OnEvent");
eventHandler = methodInfo.CreateDelegate(eventInfo.EventHandlerType, this);
eventInfo.AddEventHandler(AssociatedObject, eventHandler);
}
void DeregisterEvent(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
return;
}
if (eventHandler == null)
{
return;
}
EventInfo eventInfo = AssociatedObject.GetType().GetRuntimeEvent(name);
if (eventInfo == null)
{
throw new ArgumentException(string.Format("EventToCommandBehavior: Can't de-register the '{0}' event.", EventName));
}
eventInfo.RemoveEventHandler(AssociatedObject, eventHandler);
eventHandler = null;
}
void OnEvent(object sender, object eventArgs)
{
if (Command == null)
{
return;
}
object resolvedParameter;
if (CommandParameter != null)
{
resolvedParameter = CommandParameter;
}
else if (Converter != null)
{
resolvedParameter = Converter.Convert(eventArgs, typeof(object), null, null);
}
else
{
resolvedParameter = eventArgs;
}
if (Command.CanExecute(resolvedParameter))
{
Command.Execute(resolvedParameter);
}
}
static void OnEventNameChanged(BindableObject bindable, object oldValue, object newValue)
{
var behavior = (EventToCommandBehavior)bindable;
if (behavior.AssociatedObject == null)
{
return;
}
string oldEventName = (string)oldValue;
string newEventName = (string)newValue;
behavior.DeregisterEvent(oldEventName);
behavior.RegisterEvent(newEventName);
}
}
}
in xaml
</StackLayout>
<Entry WidthRequest="100" Text="{Binding Value1,Mode=OneWayToSource}" Keyboard="Numeric" >
<Entry.Behaviors>
<local:EventToCommandBehavior EventName="Unfocused" Command="{Binding CompletedCommand}" />
</Entry.Behaviors>
</Entry>
<Entry WidthRequest="100" Text="{Binding Value2,Mode=OneWayToSource}" Keyboard="Numeric">
<Entry.Behaviors>
<local:EventToCommandBehavior EventName="Unfocused" Command="{Binding CompletedCommand}" />
</Entry.Behaviors>
</Entry>
<Label WidthRequest="100" Text="{Binding Sum}" TextColor="Black" />
</StackLayout>
in ViewModel
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
double value1;
public double Value1
{
get
{
return value1;
}
set
{
if (value1 != value)
{
value1 = value;
NotifyPropertyChanged("Value1");
}
}
}
double value2;
public double Value2
{
get
{
return value2;
}
set
{
if (value2 != value)
{
value2 = value;
NotifyPropertyChanged("Value2");
}
}
}
private string sum;
public string Sum
{
get { return sum; }
set
{
if (sum != value)
{
sum = value;
NotifyPropertyChanged("Sum");
}
}
}
public ICommand CompletedCommand { get; set; }
public MyViewModel()
{
CompletedCommand = new Command(()=> {
Sum = (Value1 + Value2).ToString();
});
}
}
im working with custon entry rendered, i need to hear from xaml in my custom render when i clicked my button
i have this code in my xaml
<local:MyEntry eventRefresh="true">
when i clicked my button this function is actived
private async void Execute(object sender)
{
var entry = ((MyEntry)view);
entry.eventRefresh = "false";
but my EntryRendered donot hear the change
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control != null)
{
var element = Element as MyEntry;
You should define the property eventRefresh as Bindable Property .
in your custom Entry
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Xamarin.Forms;
namespace xxx
{
public class MyEntry:Entry,INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public static readonly BindableProperty eventRefreshProperty = BindableProperty.Create("eventRefresh", typeof(bool), typeof(MyEntry), true,propertyChanged:(obj,oldValue,newValue)=> {
//var entry = obj as MyEntry;
// entry.Text = newValue.ToString();
});
bool refresh;
public bool eventRefresh
{
get { return refresh; }
set {
if(refresh !=value)
{
refresh = value;
NotifyPropertyChanged("eventRefresh");
}
}
}
public MyEntry()
{
}
}
}
in xaml
<StackLayout VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand">
<local:MyEntry eventRefresh="{Binding Refresh}" BackgroundColor="{Binding BGcolor}" WidthRequest="200" HeightRequest="50" />
<Button Command="{Binding ClickCommand}" />
</StackLayout>
in View Model
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Color color;
public Color BGcolor
{
get { return color; }
set
{
if (color != value)
{
color = value;
NotifyPropertyChanged("BGcolor");
}
}
}
bool refresh;
public bool Refresh
{
get { return refresh; }
set
{
if (refresh != value)
{
refresh = value;
NotifyPropertyChanged("Refresh");
}
}
}
public ICommand ClickCommand { get; set; }
public MyViewModel()
{
BGcolor = Color.LightPink;
ClickCommand = new Command(()=> {
BGcolor = Color.Red;
});
}
}
in Custom Renderer
using System.ComponentModel;
using Android.Content;
using xxx;
using xxx.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly:ExportRenderer(typeof(MyEntry),typeof(NyEntryRenderer))]
namespace xxx.Droid
{
public class NyEntryRenderer : EntryRenderer
{
public NyEntryRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if(Control!=null)
{
Element.TextChanged += Element_TextChanged;
}
}
private void Element_TextChanged(object sender, TextChangedEventArgs e)
{
// var content = Element.Text;
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == MyEntry.BackgroundColorProperty.PropertyName)
{
// will been invoked when click button
}
}
}
}
Make your view model like this.
public class YourViewModel
{
public Command command
{
get
{
return new Command(() => {
//Change here button background colors
BackgroundColor = Color.Green;
});
}
}
private _backgroundColor = Color.White;
public Color BackgroundColor
{
get { return _backgroundColor;}
set
{
if (value == _backgroundColor)
return;
_backgroundColor = value;
NotifyOnPropertyChanged(nameof(BackgroundColor));
}
}
}
Your XAML
<local:MyEntry Text="{Binding Password}" Placeholder="Enter" />
<Button Text="send" Command="{Binding command}" BackgroundColor="{Binding BackgroundColor}"></Button>
I'm very new at Xamarin.Forms and I have one issue that I can't solve.
When I click on a button, it is supposed to navigate to page RegistracijaKorisnika.xaml (It is on Bosnian language .. sorry for that), but it does not because of the exception:
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation
The page RegistracijaKorisnika.xaml has ViewModel page RegistracijaKorisnikaViewModel.cs and when i put in RegistracijaKorisnika.xaml.cs line :
BindingContext = new RegistracijaKorisnikaViewModel()
The exception occurs.
When I delete line
BindingContext = new RegistracijaKorisnikaViewModel()
I can navigate to the RegistracijaKorisnika.xaml.
Here is the code and I am hoping that you can help me.
This is RegistracijaKorisnika.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"
x:Class="ITJobFinder.View.RegistracijaKorisnika">
<ContentPage.Content>
<StackLayout>
<Entry Placeholder="Ime" x:Name="entryImeKorisnika" Text="{Binding ImeKorisnika}"/>
<Entry Placeholder="Prezime" x:Name="entryPrezimeKorisnika" Text="{Binding PrezimeKorisnika}"/>
<Entry Placeholder="Email" x:Name="entryEmailKorisnika" Text="{Binding EmailKorisnika}"/>
<Entry Placeholder="Password" x:Name="entryPasswordKorisnika" Text="{Binding PasswordKorisnika}"/>
<Entry Placeholder="Naziv zavrsene obrazovne institucije" x:Name="entryNazivFakulteta" Text="{Binding NazivObrazovanjaKOrisnika}"/>
<Entry Placeholder="Godina zavrsetka obrazovanja" x:Name="entryGodinaZavrsetkaObrazovanja" Text="{Binding GodinaZavrsetkaObrazovanja}"/>
<Button Text="Registruj se!" Command="{Binding SaveNewUser}"/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
This is RegistracijaKorisnika.xaml.cs
namespace ITJobFinder.View
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class RegistracijaKorisnika : ContentPage
{
public RegistracijaKorisnika ()
{
InitializeComponent ();
BindingContext = new RegistracijaKorisnikaViewModel();
}
}
}
This is RegistracijaKorisnikaViewModel.cs
namespace ITJobFinder.ViewModel
{
public class RegistracijaKorisnikaViewModel : BaseViewModel
{
private string imeKorisnika;
private string prezimeKorisnika;
private string emailKorisnika;
private string passwordKorisnika;
private string nazivObrazovanjaKOrisnika;
private string godinaZavrsetkaObrazovanja;
public string ImeKorisnika
{
get => imeKorisnika;
set
{
imeKorisnika = value;
OnPropertyChanged();
}
}
public string PrezimeKorisnika
{
get => PrezimeKorisnika;
set
{
prezimeKorisnika = value;
OnPropertyChanged();
}
}
public string EmailKorisnika
{
get => emailKorisnika;
set
{
emailKorisnika = value;
OnPropertyChanged();
}
}
public string PasswordKorisnika
{
get => passwordKorisnika;
set
{
passwordKorisnika = value;
OnPropertyChanged();
}
}
public string NazivObrazovanjaKOrisnika
{
get => nazivObrazovanjaKOrisnika;
set
{
nazivObrazovanjaKOrisnika = value;
OnPropertyChanged();
}
}
public string GodinaZavrsetkaObrazovanja
{
get => godinaZavrsetkaObrazovanja;
set
{
godinaZavrsetkaObrazovanja = value;
OnPropertyChanged();
}
}
public ICommand SaveNewUser { get; private set; }
private DataBaseContext _context;
public RegistracijaKorisnikaViewModel()
{
_context = new DataBaseContext();
SaveNewUser = new Command(SaveUser);
}
void SaveUser()
{
Korisnik k = new Korisnik
{
ImeKorisnika = ImeKorisnika,
PrezimeKorisnika = PrezimeKorisnika,
EmailKorisnika = EmailKorisnika,
PasswordKorisnika = PasswordKorisnika,
GodinaZavrsetkaObrazovanja = GodinaZavrsetkaObrazovanja,
NazivObrazovanjaKOrisnika = NazivObrazovanjaKOrisnika
};
_context.Korisnici.Add(k);
_context.SaveChanges();
}
}
}
this get will cause an self-referential loop - your get should be using the lower-case internal variable, not the upper case Property name
public string PrezimeKorisnika
{
// should be "prezimeKorisnika"
get => PrezimeKorisnika;
set
{
prezimeKorisnika = value;
OnPropertyChanged();
}
}
I have Page with an Activity Indicator with the following code:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.ClientSearch" Title="Search">
<ContentPage.Content>
<StackLayout Orientation="Vertical">
<StackLayout.Children>
<SearchBar x:Name="txtSearchClient" TextChanged="OnTextChanged"></SearchBar>
<ActivityIndicator IsVisible="{Binding IsBusy}" IsRunning="{Binding IsBusy}" x:Name="indicadorCargando" />
<ListView x:Name="lstClients"></ListView>
</StackLayout.Children>
</StackLayout>
</ContentPage.Content>
</ContentPage>
In the partial class associated to this xaml, I have:
namespace MyApp
{
public partial class ClientSearch: ContentPage
{
public BusquedaClientes()
{
InitializeComponent();
}
async void OnTextChanged(object sender, TextChangedEventArgs e)
{
if (this.txtSearchClient.Text.Length >= 3)
{
var list_clients = App.ClientsManager.GetTasksAsync(txtSearchClient.Text);
this.IsBusy = true;
var template = new DataTemplate(typeof(TextCell));
template.SetBinding(TextCell.DetailProperty, "name_ct");
template.SetBinding(TextCell.TextProperty, "cod_ct");
lstClients.ItemTemplate = template;
lstClients.ItemsSource = await list_clients;
this.IsBusy = false;
}
}
}
}
As you can see, this.IsBusy is setting the Page property, so tried to bind to that property in the XAML. Unfortunetly it doesn't work:
<ActivityIndicator IsVisible="{Binding IsBusy}" IsRunning="{Binding IsBusy}" x:Name="indicadorCargando" />
How can I bind the values of the ActivityIndicator to the IsBusy page property?
I already know that setting the values like this:
this.IsBusy = true;
indicadorCargando.IsVisible=true;
indicadorCargando.IsRunning=true;
But I don't want to do that, I want to set one value instead of three.
You certainly could go the route of a separate view model, which is not a bad idea. However for the specific question, it doesn't look like you're setting the BindingContext anywhere, so it isn't going to get the IsBusy property that you want.
I haven't tried setting the BindingContext for a control to itself, but something like this ought to work:
<ActivityIndicator
IsVisible="{Binding IsBusy}"
IsRunning="{Binding IsBusy}"
x:Name="indicadorCargando"
BindingContext="{x:Reference indicadorCargando}" />
You need to give your page a name (x:Name="myPage") and then declare the binding using a reference to it using {Binding Source={x:Reference myPage}, Path=IsBusy} for the ActivityIndicator's IsVisible and IsRunning values. More info on this answer.
Here is how I would rewrite it:
public class ClientSearch : ContentPage
{
public ClientSearch()
{
BindingContext = new ClientSearchViewModel();
var stack = new StackLayout();
var searchBar = new SearchBar();
searchBar.SetBinding<ClientSearchViewModel>(SearchBar.TextProperty, x => x.Text);
var actInd = new ActivityIndicator();
actInd.SetBinding<ClientSearchViewModel>(ActivityIndicator.IsVisibleProperty, x => x.IsBusy);
actInd.SetBinding<ClientSearchViewModel>(ActivityIndicator.IsRunningProperty, x => x.IsBusy);
var lv = new ListView
{
ItemTemplate = new DataTemplate(() =>
{
var txtCell = new TextCell();
txtCell.SetBinding<MyItemModel>(TextCell.TextProperty, x => x.Name_Ct);
txtCell.SetBinding<MyItemModel>(TextCell.TextProperty, x => x.Cod_Ct);
return txtCell;
})
};
lv.SetBinding<ClientSearchViewModel>(ListView.ItemsSourceProperty, x => x.items);
stack.Children.Add(searchBar);
stack.Children.Add(actInd);
stack.Children.Add(lv);
Content = stack;
}
}
public class ClientSearchViewModel : BaseViewModel
{
public string Text { get; set; }
public bool IsBusy { get; set; }
public IEnumerable<MyItemModel> items { get; set; }
protected override async void OnPropertyChanged(string propertyName = null)
{
if (propertyName != nameof(Text) || Text.Length < 3) return;
IsBusy = true;
items = await App.ClientsManager.GetTasksAsync(Text);
IsBusy = false;
}
}
[ImplementPropertyChanged]
public abstract class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}