Hi,
I'm facing a problem during the update of a Picker ItemSource and SelectedItem in the ViewModel during a callback of a received message.
I'm using XF 2.5.0 and I've reproduced the error on emulator with Android 6 and also on a physical device with Android 7 (so actually on every device I have).
I've a modal main page with a picker binded with an observablecollection in the viewmodel. In the main page there is a button that when clicked push a new modal page that allows to create a new item that will replace the Picker ItemSource as unique member.
When the page for adding a new element disappear the picker appears with nothing selected; if I touch the picker the new element is visible.
If I put a breakpoint in the Setter of the ItemSelected I can see that is called twice: the first with the new item value, the second with null.
I've tried using different binding mode but it doesn't work.
This is the MainPage:
<StackLayout>
<Picker x:Name="PickerItems"
ItemsSource="{Binding ItemsList}"
ItemDisplayBinding="{Binding Description}"
SelectedItem="{Binding ItemSelected, Mode=TwoWay}"
HorizontalOptions="FillAndExpand"
TextColor="Black"
Margin="0" />
<Button Command="{Binding AddItemCommand}" Text="Add item" IsVisible="True"/>
</StackLayout>
In the viewModel of the main page the command is managed this way:
public ICommand AddItemCommand => new Command(async () => await AddItemCommandAsync());
protected virtual async Task AddItemCommandAsync()
{
MessagingCenter.Subscribe<UpdateItemData>(this, "UpdateItem", async (model) =>
{
MessagingCenter.Unsubscribe<UpdateItemData>(this, "UpdateItem");
List<PickerItem> list = new List<PickerItem>();
list.Add(model.PickerItem);
ItemsList = new ObservableCollection<PickerItem>(list);
ItemSelected = ItemsList.First();
});
NewPickerItemViewModel viewModel = new NewPickerItemViewModel();
NewItemPage newItemPage = new NewItemPage(viewModel);
await Navigation.PushModalAsync(newItemPage);
}
On AddItemCommand a new modal page is pushed on the navigation stack. This page is very simple:
<StackLayout>
<Entry TextColor="Black"
Text="{Binding Description}" />
<Button Command="{Binding NewItemCommand}"
Text="Create" />
<Button Command="{Binding BackCommand}"
Text="Back" />
</StackLayout>
When NewItemCommand in the viewmodel is:
public ICommand NewItemCommand => new Command(async () => await NewItemCommandAsync());
private async Task NewItemCommandAsync()
{
UpdateItemData updateItemData = new UpdateItemData();
PickerItem pickerItem = new PickerItem();
pickerItem.Description = Description;
pickerItem.Id = 0;
updateItemData.PickerItem = pickerItem;
MessagingCenter.Send(updateItemData, "UpdateItem");
await Navigation.PopModalAsync();
}
Do you have any suggestion?
Best regards
Andrea
Posts
Hi Andrea.
First, you should set Subscribe and Unsubscribe in OnAppearing / OnDisappearing events.. it's a good solution...
Second. I have done some changes... take a look. You should continue to use ItemsList without reassign it with a new List... clear and add... clear and add. It should works
Let me know...
Thanks Alessandro.
It works fine now.
Best regards
Hi Alessandro,
I think I missing something about using Subscribe in OnAppearing and Unsubscribe in OnDisappearing.
When I click the button to navigate to the second window the OnDisappearing on the main windows is called and so the Unsubscribe. Consequently the message sent by the second window is lost.
Thanks
Andrea
I am drinking a coffee at Autogrill and I am taking a look to your code.
I don't understand one thing. You are using Navigation but I don't see where you set the NavigationPage.
Usually you should set the "root" page with a code like
Hi,
I set the viewmodel property Navigation in the MainPage constructor;
So I don't any NavigationPage because I want to use modal page.
Thanks
I think if you want to use Navigation you should have a NavigationPage as root. if you want to use Modal, you can PushModal or (if I remember) remove the navigationbar
I use PushModal... but this method is in the Navigation class....
I've done the same test replacing in App.xaml.cs
MainPage = new XamarinFormPickerTest.MainPage(mainPageViewModel);
with:
MainPage = new NavigationPage(new XamarinFormPickerTest.MainPage(mainPageViewModel));
But during the transition from page1 to page2 the OnDisappearing is called, and so the unsubscribe..
It seems OnDisappearing is always called.
Maybe the mistake is in the approach; I think I could use other 2 approach to get value from a modal page:
Best regards
I will take a look, but it's strange....
Yes, you are right. I think another solution is to pass to NewItemPage the MainPage's ViewModel. Modify it and changes should reflect in MainPage
Hi Alessandro
I am using custom datepicker it not working fine for me, could u help me the same.
I want to bind the date in the format ("ddd MMM dd"), Hour,minute and AM/PM
after the select the date it stored in oneoff the label.
This is the PickerViewPage.xml
<StackLayout.BindingContext>
</StackLayout.BindingContext>
PickerView.cs
public class PickerView : View
{
#region ItemsSource
public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(nameof(ItemsSource),
typeof(IEnumerable), typeof(PickerView), null);
}
NumbersPickerViewModel.cs
internal class NumbersPickerViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string[] _month;
public string[] Month
{
get { return _month; }
set
{
_month = value;
OnPropertyChanged();
}
}
private string[] _hour;
public string[] Hour
{
get { return _hour; }
set
{
_hour = value;
OnPropertyChanged();
}
}
private string[] _minute;
public string[] Minute
{
get { return _minute; }
set
{
_minute = value;
OnPropertyChanged();
}
}
private string[] _format;
public string[] Format
{
get { return _format; }
set
{
_format = value;
OnPropertyChanged();
}
}
private string _iCurrDate;
public string CurrDate
{
get { return _iCurrDate; }
set
{
_iCurrDate = value;
OnPropertyChanged();
}
}
private string _iCurrHour;
public string CurrHour
{
get { return _iCurrHour; }
set
{
_iCurrHour = value;
OnPropertyChanged();
}
}
private string _iCurrMin;
public string CurrMin
{
get { return _iCurrMin; }
set
{
_iCurrMin = value;
OnPropertyChanged();
}
}
private string _iCurrZone;
public string CurrZone
{
get { return _iCurrZone; }
set
{
_iCurrZone = value;
OnPropertyChanged();
}
}
public NumbersPickerViewModel()
{
var dates = new List();
DateTime startdate = DateTime.Now.AddDays(-3);
DateTime enddate = DateTime.Now.AddDays(3);
DateTime currentdate = DateTime.Now;
List hours = new List();
List minutes = new List();
for (DateTime dt = startdate; dt <= enddate; dt = dt.AddDays(1))
{
if (dt.Date == currentdate.Date)
{
dates.Add("Today");
CurrHour = currentdate.ToString("hh");
CurrMin = currentdate.ToString("mm");
//CurrHour = int.Parse(currentdate.ToString("hh"));
//CurrMin = int.Parse(currentdate.ToString("mm"));
}
else
{ dates.Add(dt.ToString("ddd MMM dd")); }
}
PickerViewRenderer.cs for Android
public class PickerViewRenderer : ViewRenderer<PickerView, NumberPicker>
{
protected override void OnElementChanged(ElementChangedEventArgs e)
{
base.OnElementChanged(e);