Xamarin Forms DatePicker Not binding with viewmodel property in UWP? - xamarin.forms

My DatePicker is not binding with my viewmodel property in UWP whereas its working in android and ios.
My view:
<DatePicker x:Name="StartDate" Date="{Binding StartDate, Mode=TwoWay}" Opacity="0.45" HeightRequest="50" Focused="OnScreenTapped" />
ViewModel:
private DateTime? _startDate;
public DateTime? StartDate
{
get
{
return _startDate;
}
set
{
_startDate = value;
OnPropertyChanged("StartDate");
}
}
I initailaised my date with StartDate = new DateTime(2000, 01, 01); but it shows current date only......
Also when I change the date in date picker it does not change in viewmodel....
Any help is appreciated...Thanks

I have tried to reproduce your issue, But DatePicker binding works in my side. You could refer to the following code to check if you have missed some key procedure.
MainPage.xaml
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XamarinDatePickerTest"
x:Class="XamarinDatePickerTest.MainPage"
xmlns:ViewMode ="clr-namespace:XamarinDatePickerTest.ViewModel"
xmlns:sys="clr-namespace:System;assembly=System.Runtime"
>
<ContentPage.BindingContext>
<ViewMode:MainPageViewModel/>
</ContentPage.BindingContext>
<StackLayout>
<DatePicker VerticalOptions="CenterAndExpand" Date="{Binding Datetime}">
<DatePicker.Format>yyyy-MM-dd</DatePicker.Format>
<DatePicker.MinimumDate>
<sys:DateTime x:FactoryMethod="Parse">
<x:Arguments>
<x:String>Jan 1 2000</x:String>
</x:Arguments>
</sys:DateTime>
</DatePicker.MinimumDate>
</DatePicker>
</StackLayout>
</ContentPage>
MainPageViewModel.cs
public class MainPageViewModel : INotifyPropertyChanged
{
private DateTime? _datetime;
public DateTime? Datetime
{
get => _datetime;
set
{
_datetime = value;
OnPropertyChanged();
}
}
public MainPageViewModel()
{
this.Datetime = new DateTime(2011, 12, 18);
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And I have uploaded the code sample to github, please check.

Just experienced the same issue. My problem was the nullable date. By default, the DatePicker doesn't allow nullable dates (defaults to todays date), but you can add a custom control to get it to work.

Related

Execute ReactiveUI Command from Event in Xamarin Forms

I have entry fields in a Xamarin Forms page that I want to trigger a ReactiveUI command when the user is finished entering text into them. I am using ReactiveUI.Events.XamForms and am trying to trigger a command based off of the Unfocused event, but I am not sure how to set up the command to get that to work.
Here is my XAML:
<?xml version="1.0" encoding="utf-8" ?>
<rxui:ReactiveContentPage
x:Class="XamarinReactiveUISwipeView.MainPage"
x:TypeArguments="vm:MainPageViewModel"
xmlns:vm="clr-namespace:XamarinReactiveUITest.ViewModel;assembly=XamarinReactiveUITest"
xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:ios="clr- namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
xmlns="http://xamarin.com/schemas/2014/forms"
ios:Page.UseSafeArea="true">
<StackLayout>
<StackLayout Margin="10,0,0,0" Orientation="Horizontal">
<Label Text="Task ID: " />
<Entry x:Name="EntryTaskID" />
</StackLayout>
<StackLayout Margin="10,0,0,0" Orientation="Horizontal">
<Label Text="Task Name: " />
<Entry x:Name="EntryTaskName" />
</StackLayout>
</StackLayout>
</rxui:ReactiveContentPage>
Here is my code behind:
public partial class MainPage : ReactiveContentPage<MainPageViewModel>
{
public MainPage()
{
InitializeComponent();
ViewModel = new MainPageViewModel();
this.WhenActivated(disposable =>
{
this.Bind(ViewModel, vm => vm.TheTaskItem.TaskID, page => page.EntryTaskID.Text)
.DisposeWith(disposable);
this.Bind(ViewModel, vm => vm.TheTaskItem.TaskName, page => page.EntryTaskName.Text)
.DisposeWith(disposable);
EntryTaskName.Events().Unfocused.InvokeCommand(ViewModel, vm => vm.TheCommand);
});
}
}
here is my model:
public class TaskItem
{
public TaskItem() { }
public string TaskID { get; set; }
public string TaskName { get; set; }
}
and here is my view model:
public class MainPageViewModel : ReactiveObject
{
public MainPageViewModel()
{
TheTaskItem = new TaskItem { TaskID = "1", TaskName = "TheTaskName" };
TheCommand = ReactiveCommand.Create<FocusEventArgs, Unit>(ExecuteTheCommand);
}
public ReactiveCommand<FocusEventArgs, Unit> TheCommand { get; }
private void ExecuteTheCommand(FocusEventArgs args)
{
//do something
}
private TaskItem _theTaskItem;
public TaskItem TheTaskItem
{
get => _theTaskItem;
set => this.RaiseAndSetIfChanged(ref _theTaskItem, value);
}
}
In the view model above, it won't compile, but I can't figure out how to set up the ExecuteTheCommand method. The error is:
'void MainPageViewModel.ExecuteTheCommand(FocusEventArgs)' has the wrong return type
But in looking at examples, it looked like methods with void returns use the Unit type.
What do I need to do here to set up the command properly to get this to work?
In comment above, OP says that this works if change:
TheCommand = ReactiveCommand.Create<FocusEventArgs, Unit>(ExecuteTheCommand);
to:
TheCommand = ReactiveCommand.Create<FocusEventArgs>(ExecuteTheCommand);
Unexpectedly, the type of the variable that holds the result of Create, does not use that same generic type signature. It needs to be:
public ReactiveCommand<FocusEventArgs, Unit> TheCommand { get; }
According to the doc in link by Rodney Littles in comment, Unit is used to represent "void return type". (Generic type "TOutput" would be "void"; but "void" is not a valid Generic type.)

How to bind map/pin in view model properly in Xamarin.Forms?

I followed the doc, trying to bind the pin, but failed. The map is always showing the default position Rome. Here is the source code:
In DetailPage.xmal:
<Frame Margin="10,5"
CornerRadius="10"
Padding="0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="300" />
</Grid.RowDefinitions>
<maps:Map MapType="Street" Grid.Row="0" ItemsSource="{Binding WorkPlace}">
<maps:Map.ItemTemplate>
<DataTemplate>
<maps:Pin Position="{Binding Position}"
Address="{Binding Address}"
Label="{Binding Description}" />
</DataTemplate>
</maps:Map.ItemTemplate>
</maps:Map>
</Grid>
</Frame>
In DetailPageModel.cs:
public class DetailPageModel : PageModelBase
{
private Timesheet _detailedTimesheet;
private ObservableCollection<Location> _workPlace;
public ObservableCollection<Location> WorkPlace
{
get => _workPlace;
set => SetProperty(ref _workPlace, value);
}
public ReportDetailPageModel()
{
}
public override async Task InitializeAsync(object navigationData)
{
if (navigationData is Timesheet selectedTimesheet)
{
_detailedTimesheet = selectedTimesheet;
WorkPlace = new ObservableCollection<Location>()
{
new Location(
_detailedTimesheet.ProjectAddress,
"Test Location",
new Position(_detailedTimesheet.ProjectLatitude, _detailedTimesheet.ProjectLongitude))
};
}
await base.InitializeAsync(navigationData);
}
}
In Location.cs:
public class Location : ExtendedBindableObject
{
Position _position;
public string Address { get; }
public string Description { get; }
public Position Position
{
get => _position;
set => SetProperty(ref _position, value);
}
public Location(string address, string description, Position position)
{
Address = address;
Description = description;
Position = position;
}
}
In ExtendedBindableObject.cs:
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(storage, value))
{
return false;
}
storage = value;
OnPropertyChanged(propertyName);
return true;
}
Since the navigationData is correctly received in view model and the page's binding context is also working, I just don't know what could be missing. Any hint would be appreciated!
And actually I have one more confusion, why does the official doc use a custom Location class instead of the Pin class, as Pin inherits from Element/BindableObject/Object?

How to invoke Binding.Path programmatically in Xamarin.Forms?

I have a DateTimeViewModel with DateTime property of type DateTime.
I can do actually in XAML as follows. And it works.
<StackLayout.BindingContext>
<Binding Path="DateTime">
<Binding.Source>
<toolkit:DateTimeViewModel/>
</Binding.Source>
</Binding>
</StackLayout.BindingContext>
<Label Text="{Binding Hour,StringFormat='The hours are {0}'}" />
<Label Text="{Binding Minute,StringFormat='The minutes are {0}'}" />
<Label Text="{Binding Second,StringFormat='The seconds are {0}'}" />
<Label Text="{Binding Millisecond,StringFormat='The milliseconds are {0}'}" />
However I am interested in learning whether I can remove
<StackLayout.BindingContext>
<Binding Path="DateTime">
<Binding.Source>
<toolkit:DateTimeViewModel/>
</Binding.Source>
</Binding>
</StackLayout.BindingContext>
and replace it with something like as follows (not works anyway)
BindingContext = new DateTimeViewModel();
this.SetBinding(Label.TextProperty, "DateTime");
Could you tell me how to set the binding path programmatically?
Edit
To make less confusing, let me add the details here.
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
private void OnPropertyChanged(string propertyName)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public class DateTimeViewModel : ViewModelBase
{
public DateTimeViewModel()
{
Device.StartTimer(TimeSpan.FromMilliseconds(15),
() =>
{
DateTime = DateTime.Now;
Counter++;
return true;
}
);
}
private DateTime dateTime = DateTime.Now;
public DateTime DateTime
{
get => dateTime;
private set => SetProperty(ref dateTime, value);
}
private long counter = 0;
public long Counter { get => counter; private set => SetProperty(ref counter, value); }
}
To set the binding path programmatically you have to use the following code:
BindableObject.SetBinding(BindableProperty targetProperty, new Binding() {Path = "path"})
In your case it would be something like the following:
this.SetBinding(Label.TextProperty, new Binding() {Path = "DateTime"});
You should bind properties of the same type, in your code you are trying to bind a Label.TextProperty with a property of the type DateTime. make the DateTime property in your ViewModel one of the type "string", and then do the conversion in the ViewModel to "DateTime" or vice versa.
You should not also set parameters, variables, or functions names equal to their type.

Xamarin Forms connect products with user accounts

I'm making a shopping app, it has account for the user, and when the user purchases a product, it should be added to a listview in his account. So I tried to put a static object of the User Class that has a list of Products, and whenever the user clicks the buying button, it should be added to the list. At the same time, the user.xaml is binding to the same object. But it doesn't work. What's the error in my method?
Are there any better ideas to do this?
here's the static field in the App.xaml.cs file
private IDataService _dataService;
public static User TheUser;
public App(IDataService dataService)
{
TheUser = new User();
InitializeComponent();
var unity = new UnityContainer();
unity.RegisterType<IDataService, DataServices>();
ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(unity));
_dataService = dataService;
MainPage = new NavigationPage(new MainPage());
}
and here's the User.xaml.cs property
public User User
{
get { return App.TheUser; }
set
{
if(App.TheUser!= null)
App.TheUser = value;
}
//User class
public class User : Base //Base class implements INotifyPropertyChanged
{
public int Id { get; set; }
public string Name { get; set; }
public ObservableCollection<Product> Products = new ObservableCollection<Product>();
}
public class Base : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Here's the User.Xaml file
<StackLayout>
<AbsoluteLayout>
<Image AbsoluteLayout.LayoutBounds="1 , 0 ,96 ,96" AbsoluteLayout.LayoutFlags="PositionProportional"/>
<Label AbsoluteLayout.LayoutBounds="0 , 50 , 100 , 20" AbsoluteLayout.LayoutFlags="XProportional" Text="First Name"/>
<Label AbsoluteLayout.LayoutBounds="0 , 100 , 100 , 20" AbsoluteLayout.LayoutFlags="XProportional" Text="Last Name"/>
</AbsoluteLayout>
<ListView x:Name="UserListView"
SelectedItem="{Binding SelectedItemCommand}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Spacing="3" FlowDirection="RightToLeft" >
<Image Source="{Binding ProductMainImage}" Aspect="AspectFill" Margin="3" HeightRequest="300" />
<Label Text="{Binding Name ,StringFormat=' الاسم : {0}'}"/>
<Label Text="{Binding Price ,StringFormat=' السعر : {0}'}"/>
<Label Text="{Binding Description ,StringFormat=' الوصف : {0}'}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
ok so the property notification doesn't happen automatically. You need to invoke that code to raise the event. This should takes care of the code, but without seeing the xaml, I don't know if the binding is setup correctly.
public User User
{
get { return App.TheUser; }
set
{
if(App.TheUser!= null)
App.TheUser = value;
}
//User class
public class User : Base //Base class implements INotifyPropertyChanged
{
private int _id
public int Id {
get{
return this._id;
}
set{
this._id = value;
OnPropertyChanged("Id");
}
}
private string _name;
public string Name {
get{
return this._name;
}
set{
this._name = value;
OnPropertyChanged("Name");
}
}
private ObservableCollection<Product> _products;
public ObservableCollection<Product> Products
{
get{
return this._products;
}
set{
this._products = value;
OnPropertyChanged("Products");
}
}
}
}
so your listview is not bound to anything...
<ListView x:Name="UserListView"
ItemsSource={Binding Products}
SelectedItem="{Binding SelectedItemCommand}">

Windows store 8.1 dynamic binding not updating in UI

I have made a simple test for updating binded values in the UI but nothing seems to update, only intial values are set but never updated, what would i be missing?
code:
//the model class
public class DemoCustomer : INotifyPropertyChanged
{
// These fields hold the values for the public properties.
private Guid idValue = Guid.NewGuid();
private string customerNameValue = String.Empty;
private string phoneNumberValue = String.Empty;
public event PropertyChangedEventHandler PropertyChanged= delegate { };
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
// The constructor is private to enforce the factory pattern.
public DemoCustomer()
{
customerNameValue = "Customer";
phoneNumberValue = "(312)555-0100";
}
// This is the public factory method.
public static DemoCustomer CreateNewCustomer()
{
return new DemoCustomer();
}
// This property represents an ID, suitable
// for use as a primary key in a database.
public Guid ID
{
get
{
return this.idValue;
}
}
public string CustomerName
{
get
{
return this.customerNameValue;
}
set
{
if (value != this.customerNameValue)
{
this.customerNameValue = value;
NotifyPropertyChanged();
}
}
}
public string PhoneNumber
{
get
{
return this.phoneNumberValue;
}
set
{
if (value != this.phoneNumberValue)
{
this.phoneNumberValue = value;
NotifyPropertyChanged();
}
}
}
}
Then simply in my main page i do this:
public ObservableCollection<DemoCustomer> progcollection = new ObservableCollection<DemoCustomer>();
public MainPage()
{
this.InitializeComponent();
progcollection = new ObservableCollection<DemoCustomer>();
this.progcollection.Add(new DemoCustomer());
this.txtblk.DataContext = progcollection[0].CustomerName;
}
Then in a click listener for example i do this:
private void Button_Click_1(object sender, RoutedEventArgs e)
{
progcollection[0].CustomerName = "we changed the name!";
}
But nothing updates in the UI!!!
And here is my XAML:
<Page
x:Class="downloadprogressbinding.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:simpledownload"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock x:Name="txtblk" HorizontalAlignment="Left" Margin="994,421,0,0" TextWrapping="Wrap" Text="{Binding Mode=TwoWay}" VerticalAlignment="Top" Height="89" Width="226" FontSize="36"/>
<Button Content="Button" HorizontalAlignment="Left" Height="51" Margin="116,24,0,0" VerticalAlignment="Top" Width="407" Click="Button_Click_1"/>
</Grid>
Using path keyword in binding and specifying the field solved it,like this:
{Binding Path=thetext, Mode=TwoWay}

Resources