I use the TimePicker and appear with a default value of "12:00 AM", I want to set the field in blank.
<TimePicker x:Name="timepicker" />
You must create your own class of TimePicker that will allow a NullableTime on XAML.
The Class should be as following:
public class MyTimePicker : TimePicker
{
private string _format = null;
public static readonly BindableProperty NullableDateProperty =
BindableProperty.Create<MyTimePicker, TimeSpan?>(p => p.NullableTime, null);
public TimeSpan? NullableTime
{
get { return (TimeSpan?)GetValue(NullableDateProperty); }
set { SetValue(NullableDateProperty, value); UpdateTime(); }
}
private void UpdateTime()
{
if (NullableTime.HasValue) { if (null != _format) Format = _format; Time = NullableTime.Value; }
else { _format = Format; Format = "pick ..."; }
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
UpdateTime();
}
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == "Time") NullableTime = Time;
}
}
Then on XAML you should create your control and use it as below:
<local:MyTimePicker NullableTime="{x:Null}" />
If you use this sample you will see that the default value should be pick...
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.
got a question on how to convert this code behind into a mvvm style, Here's a sample
void CameraView_MediaCaptured(object sender, MediaCapturedEventArgs e)
{
switch (cameraView.CaptureMode)
{
default:
case CameraCaptureMode.Default:
case CameraCaptureMode.Photo:
previewVideo.IsVisible = false;
previewPicture.IsVisible = true;
previewPicture.Rotation = e.Rotation;
previewPicture.Source = e.Image;
doCameraThings.Text = "Snap Picture";
break;
case CameraCaptureMode.Video:
previewPicture.IsVisible = false;
previewVideo.IsVisible = true;
previewVideo.Source = e.Video;
doCameraThings.Text = "Start Recording";
break;
}
}
A combination of Jason's and Karas's answer .
Bind with the corresponding properties between xaml and viewmodel .
//xaml
IsVisible = "{Binding IsVisible}"
Implement INotifyPropertyChanged inside viewmodel .
public class ViewModel: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private bool isVisible;
public bool IsVisible {
get
{
return isVisible;
}
set
{
isVisible = value;
NotifyPropertyChanged();
}
}
Replace the event with EventToCommandBehavior and change the properties value.
//xaml
<xct:EventToCommandBehavior
EventName="MediaCaptured"
Command="{Binding MyCommand}" />
//viewmodel
public ICommand MyCommand { get; set; }
public ViewModel()
{
MyCommand = new Command((obj)=> {
var cameraView = obj as CameraView;
switch (cameraView.CaptureMode)
{
default:
case CameraCaptureMode.Default:
case CameraCaptureMode.Photo:
IsVisible = false;
break;
case CameraCaptureMode.Video:
IsVisible = false;
break;
}
});
}
First you create a BaseViewModel, which you can now inherit on every other ViewModel. Then your ViewModel and bind it via BindingContext = new YourPageVM() to your page via YourPage.cs. You can now create properties in your ViewModel and Binding them in the XAML. e.g .:
Here Is the BaseViewModel:
public abstract class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string
propertyName =
"")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new
PropertyChangedEventArgs(propertyName));
}
protected bool SetProperty<T>(ref T backingStore, T value,
[CallerMemberName] string propertyName = "",
Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
}
//In Your VM:
public class YourPageVM : BaseViewModel
{
bool isVisPreVideo;
public bool IsVisPreVideo{
get=> isVisPreVideo;
set=> SetProperty(ref isVisPreVideo,value);}
//set the Value in Constructor or in Your Method
public YourPageVM()
{
IsVisPreVideo = false;
}
//........
}
//At Xaml:
xmlns:viewmodel="clr-namespace:YourProject.ViewModel"
x:DataType="viewmodel:YourPageVM"
IsVisible = "{Binding IsVisPreVideo}"
And this you can do also with the other values Rotation, Source and Text.
I have this code that sets a label to one or another color depending on a property called IsEnabled:
public class LinkLabel : Label
{
public LinkLabel()
{
PropertyChanged += LinkLabel_PropertyChanged;
SetDynamicResource(Label.TextColorProperty, "LinkLabelColor");
}
private void LinkLabel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "IsEnabled")
{
var label = sender as LinkLabel;
var newValue = label.IsEnabled;
if ((bool)newValue)
this.SetDynamicResource(Label.TextColorProperty, "LinkLabelColor");
else
this.SetDynamicResource(Label.TextColorProperty, "LinkLabelDisabledColor");
}
}
}
What I would like to do is to add another property called IsImportant
so that if that is set to true and IsEnabled = true, then the LinkLabelColor would be set to red
Can someone give me some suggestions on how I can do this? I do know how to add things like bindable properties but I am not sure in this case how to combine it with the code that I already have?
Add a new property
public static readonly BindableProperty IsImportantProperty = BindableProperty.Create(nameof(IsImportant), typeof(bool), typeof(CustomLabel), false,/*Can remove property changed as well*/ propertyChanged: IsImportantPropertyChanged);
public bool IsImportant
{
get { return (bool)GetValue(IsImportantProperty); }
set { SetValue(IsImportantProperty, value); }
}
New Method UPDATE
protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (propertyName == IsEnabledProperty.PropertyName || propertyName == IsImportantProperty.PropertyName)
{
if (this.IsImportant && this.IsEnabled)
this.SetDynamicResource(Label.TextColorProperty, "LinkLabelColor");
else
this.SetDynamicResource(Label.TextColorProperty, "LinkLabelDisabledColor");
}
}
Some issues with the other answer not working. I revisited the code and came up with this which works good:
public class LinkLabel : Label
{
public LinkLabel()
{
SetDynamicResource(Label.FontFamilyProperty, "Default-Regular");
SetDynamicResource(Label.FontSizeProperty, "LabelTextFontSize");
SetDynamicResource(Label.TextColorProperty, "LinkLabelColor");
VerticalOptions = LayoutOptions.CenterAndExpand;
VerticalTextAlignment = TextAlignment.Center;
}
public static readonly BindableProperty IsImportantProperty =
BindableProperty.Create(nameof(IsImportant), typeof(bool), typeof(LinkLabel), false);
public bool IsImportant
{
get { return (bool)GetValue(IsImportantProperty); }
set { SetValue(IsImportantProperty, value); }
}
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == IsEnabledProperty.PropertyName ||
propertyName == IsImportantProperty.PropertyName)
{
if (this.IsEnabled) {
if (this.IsImportant)
this.SetDynamicResource(Label.TextColorProperty, "LinkLabelImportantColor");
else
this.SetDynamicResource(Label.TextColorProperty, "LinkLabelColor");
}
else
this.SetDynamicResource(Label.TextColorProperty, "LinkLabelDisabledColor");
}
}
}
I am trying to make simple app which will provide features to read/write data to database trough an Web API.
I have view model which is bind to view, but it is not working properly after web api get request, even that call was successfully done. I've tried to check value with display alert, value is correct, but it is not presented in view part, exactly in one label. Here is my 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="SOSEUApp.Pages.NotePage"
Title="Dnevnik">
<ContentPage.ToolbarItems>
<ToolbarItem Text="GET" Clicked="ToolbarItem_Clicked"></ToolbarItem>
</ContentPage.ToolbarItems>
<ContentPage.Content>
<StackLayout Orientation="Vertical">
<ActivityIndicator IsRunning="{Binding IsBusy}" IsVisible="{Binding IsBusy}"></ActivityIndicator>
<StackLayout Orientation="Vertical">
<Label Text="{Binding Date,StringFormat='Date: {0}'}"></Label>
</StackLayout>
<StackLayout>
</StackLayout>
</StackLayout>
</ContentPage.Content>
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class NotePage : ContentPage
{
NoteViewModel nvm = new NoteViewModel();
public NotePage()
{
InitializeComponent();
BindingContext = nvm;
}
private async void ToolbarItem_Clicked(object sender, EventArgs e)
{
nvm.IsBusy = true;
nvm.Notes = await App.NotesWebApiService.GetAll(nvm.CurrentActiveNote.Route);
nvm.GetLastNote();
nvm.IsBusy = false;
await DisplayAlert("Info", nvm.Date.ToString(), "Close");
}
}
public class NoteViewModel : BaseViewModel
{
IList<Note> notes = new List<Note>();
public IList<Note> Notes
{
get { return notes; }
set { SetProperty(ref notes, value); }
}
private Note currentActiveNote = new Note();
public Note CurrentActiveNote { get { return currentActiveNote; } }
public string Date { get { return currentActiveNote.Date.ToString("dd.MM.yyyy"); } }
public string OrderedNumber
{
get { return currentActiveNote.OrderNumber.ToString(); }
set
{
string v = currentActiveNote.OrderNumber.ToString();
SetProperty(ref v, value);
currentActiveNote.OrderNumber = Convert.ToInt16(v);
}
}
public string Description
{
get { return currentActiveNote.Description; }
set
{
string v = currentActiveNote.Description;
SetProperty(ref v, value);
currentActiveNote.Description = v;
}
}
public void GetLastNote()
{
notes.OrderBy(a => a.Date);
currentActiveNote = notes.Last();
}
}
public class BaseViewModel : DataModel, INotifyPropertyChanged
{
bool isBusy = false;
public bool IsBusy
{
get { return isBusy; }
set { SetProperty(ref isBusy, value); }
}
string title = string.Empty;
public string Title
{
get { return title; }
set { SetProperty(ref title, value); }
}
protected bool SetProperty<T>(ref T backingStore, T value,
[CallerMemberName]string propertyName = "",
Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
Thank you in advance!
just called SetProperty method and passed desired values there. It works
Said as Jason, you need to fire a PropertyChanged event when Date changes.Here is official document for reference.
Generally, usually writed in Set methods.As follow:
private string propertyname;
public string PropertyName
{
set { SetProperty(ref propertyname, value); }
get { return propertyname; }
}
Or write as follow:
public string PropertyName
{
set
{
if (propertyname!= value)
{
propertyname= value;
OnPropertyChanged("PropertyName");
}
}
get
{
return propertyname;
}
}
bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Object.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
When data of model change , this will be invoked automatically.
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); }
}
}