get the selected itemssource binding value on selectedindexchanged on picker xamarin forms - xamarin.forms

I am using Picker in xamarin forms. I am binding the below class to Picker:
public string FieldCode{get; set; }
public string FieldValue{get; set; }
The Picker is like below:
<Picker Grid.Row="1" Grid.Column="0" x:Name="pkrMvmtCat" Style="{StaticResource WOFormPicker}" Title="Select" ItemsSource="{Binding FieldCode}" ItemDisplayBinding="{Binding FieldValue}" SelectedIndexChanged="pkrMvmtCat_SelectedIndexChanged"></Picker>
I want to get the FieldCode value, when i change the picker index.
Please me to resolve this issue.

You have to listen to the event, and then cast the selected item from the list. Here is an example:
void OnPickerSelectedIndexChanged(object sender, EventArgs e)
{
var picker = (Picker)sender;
int selectedIndex = picker.SelectedIndex;
if (selectedIndex != -1)
{
var field = (string)picker.ItemsSource[selectedIndex];
}
}

Related

How to pass data using PopAsync?

I have the following situation:
I have an app, where I want to create notes using a special page for it. I have VM with ObservableCollection NoteItems with all the notes. When I want to create a new note, I add a new note to this ObservableCollection and pass this new note using BindingContext
var editingPage = new EditingPage();
editingPage.BindingContext = NoteItems[NoteItems.Count - 1].Text;
Application.Current.MainPage.Navigation.PushAsync(editingPage);
In the editing page I have an Entry field
<Entry
x:Name="EdtingPageEntryField"
Text="{Binding Text}"
Placeholder="Enter a note"
/>
, which is Binding his text with the Text parameter of a NoteItem.
The problem is that if I change the text in entry field, it does not automatically apply to a Text parameter of a NoteItem. That is why I want to pass this text when I close this EditingPage(go back to the MainPage). So the question is, how can I pass this Text parameter to a NoteItem element from NoteItems ObservableCollection, which is located in VM.
UPD. The value of NoteItem, which is located in NoteItems does not change
UPD2. I was wrong about the value of NoteItem, it has been changed, but the new value does not display on MainPage, that is why I used INotifyPropertyChanged, but it did not work.
Here is Note Item class
public class NoteItem
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private string text;
public string Text
{
get { return text; }
set
{
if(text != value)
{
text = value;
NotifyPropertyChanged();
}
}
}
And MainPage.xaml:
<ContentPage.BindingContext>
<local:NoteItemViewModel/>
</ContentPage.BindingContext>
<FlexLayout>
<ListView ItemsSource="{Binding NoteItems}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding Text}"/>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</FlexLayout>
'''
in order to dynamically update the UI, your model must implement INotifyPropertyChanged
public class NoteItem : INotifyPropertyChanged
{
...
}
simply adding a PropertyChanged method is not the same as implementing INotifyPropertyChanged. You must add the interface to the class definition so that the binding mechanism knows that your class has implemented it

Xamarin Forms Picker Value Binding

When binding to a picker, you can use ItemDisplayBinding to bind the displayed value, but I do not see a way to map each item to a selection value. Because of this, I'm having to write some very convoluted code to keep my pickers in sync with data source changes.
Original Model
// NOTE: this implements INPC, just abbreviated for clarity
public class DataModel
{
public ICollection<DataItem> Items;
pubilc DataItem SelectedItem;
}
Original Picker:
<Picker Title="Select Item..."
ItemsSource="{Binding Items}"
ItemDisplayBinding="{Binding Name}"
SelectedItem="{Binding Path=SelectedItem}"></Picker>
New Model
// NOTE: this implements INPC, just abbreviated for clarity
public class DataModel
{
public ICollection<DataItems> Items;
public ICollection<string> ItemNames;
public DataItem SelectedItem;
public string SelectedItemName;
public DataModel()
{
this.PropertyChanged += (s, e) =>
{
// I feel like I shouldn't have to do this...
if(StringComparer.Ordinal.Equals(e.PropertyName, nameof(Items)))
{
if(!String.IsNullOrWhitespace(this.SelectedItemName))
{
this.SelectedItem = this.Items.FirstOrDefault(x => StringComparer.Ordinal.Equals(x.Name, this.SelectedItemName));
if (this.SelectedItem == null) { this.SelectedItemName = null; }
}
}
}
}
New Picker:
<Picker Title="Select Item..."
ItemsSource="{Binding ItemNames}"
SelectedItem="{Binding Path=SelectedItemName}"></Picker>
I would like to be able to do something like this:
<Picker Title="Select Item..."
ItemsSource="{Binding Items}"
ItemDisplayBinding="{Binding Name}"
ItemValueBinding="{Binding Name}"
SelectedItem="{Binding Path=SelectedItemName}"></Picker>
I do not need a reference to the item, I need a property off of it. In this way, when the Items collection changes, it automatically reselects the correct item if it's still present. I find that I'm adding a second collection everywhere with just the properties I want to choose and doing all this mapping. Every other platform I've worked on, this is pretty straight forward, so I feel like I have to be missing something with Xamarin.Forms.
I think you don't need to do this.The SelectedItem property data binds to the SelectedItem(in your original model) property of the connected view model, which is of type DataItem. Therefore, when the user selects an item in the Picker, the SelectedItem property will be set to the selected DataItem object automatically.
You could test it in its SelectedIndexChanged event like:
<Picker Title="Select Item..."
ItemsSource="{Binding Items}"
ItemDisplayBinding="{Binding Name}"
SelectedItem="{Binding Path=SelectedItem}"
SelectedIndexChanged="Picker_SelectedIndexChanged">
</Picker>
private void Picker_SelectedIndexChanged(object sender, EventArgs e)
{
Picker picker = sender as Picker;
DataItem dt = picker.SelectedItem as DataItem ;
Console.WriteLine(dt.Name); // you will see when you select a item,the SelectedItem will be changed automatically
}
And i suggest you use ObservableCollection<Item> Items, so it will automatically update your Items when it changes.

Picker inside collectionview looses value in SelectedIndexChanged

I have picker inside Xamarin Forms collectionview, which populated from viewModel. In case of changing index, i.e SelectedIndexChanged event, all pickers loose value and only the last one will be updated to the one which I seleced.
Picker in collectionview,which has 4 rows.
<local:CustomPicker x:Name="HoursPicker"
Title="select"
FontSize="14"
Grid.Column="3"
ItemsSource="{Binding Hours}"
SelectedIndex="{Binding AmountHour, Mode=TwoWay}"
SelectedIndexChanged="HoursPicker_SelectedIndexChanged"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand">
</local:CustomPicker>
and its my SelectedChangeIndex event
private void HoursPicker_SelectedIndexChanged(object sender, EventArgs e)
{
Picker picker = (sender) as Picker;
int selectedIndex = picker.SelectedIndex;
int selectedIndexValue = picker.SelectedIndex + 1;
ObservableCollection<OrderCardModel> list = new ObservableCollection<OrderCardModel>();
OrderViewModel vm = new OrderViewModel();
foreach (var item in MyCollectionView.ItemsSource)
{
OrderCardModel model = item as OrderCardModel;
int changedPrice = 0;
if (selectedIndex != -1)
{
changedPrice = selectedIndexValue * model.Price;
}
else
{
changedPrice = model.Price;
}
model.Price = changedPrice;
list.Add(model);
picker.SelectedIndex = selectedIndex;
}
MyCollectionView.ItemsSource = list;
}
Finally I figured out where is the problem. The model, i.e. OrderCardViewModel should implement INotifyPropertyChanged.
The page (view) binded to the ViewModel, but the collectionview itemsource binded to ObservableCollection of model, even the property comes from view model, but it did not work properly, thatswhy pickers cant respond indexchanging. To do that, model must implement the interface and OnPropertyChanged will update values.

How to have Select as default option in Xamarin forms Picker Control

I have a picker control which is optional field and the user can set the selected item of picker to be empty if he wants.
Is it possible to have Select as the additional option in itemsource of xamarin forms picker control.
Code for Custom picker:
<controls:CustomPicker
Grid.Row="1"
Grid.Column="1"
Margin="0,5,0,0"
Image="Downarrow"
ItemDisplayBinding="{Binding abb}"
ItemsSource="{Binding StateList}"
Placeholder="Select"
SelectedIndex="{Binding StateSelectedIndex}"
Style="{StaticResource Key=PickerHeight}" />
StateSelectedIndex = -1;
I tried setting selected index = -1. That works only when nothing is selected in the picker control, but once if an item is selected from the picker, then option "Select" can not be chosen (disappears).
I tried referring below url Default value for Picker but this did not help.
Any help is appreciated.
Solution 1:
You should set the binding mode of SelectedIndex
SelectedIndex="{Binding StateSelectedIndex,Mode=TwoWay}"
Solution 2:
You could binding the value of SelectedItem in ViewModel .
public class YourViewModel: INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public ObservableCollection<string> MyItems { get; set; }
private string selectItem;
public string SelectItem
{
get
{
return selectItem;
}
set
{
if(value!=null)
{
selectItem = value;
NotifyPropertyChanged("SelectItem");
int SelectIndex = MyItems.IndexOf(value);
// this will been invoked when user change the select , do some thing you want
}
}
}

How to refresh UI of a picker control in xamarin forms

I have 3 picker controls and I am trying to bind a single list to all the 3 picker controls. If one option is selected in first picker control then the same option should not repeat in rest of the 2 picker controls.I am not able to figure out how to implement it.
I tried using Security_Question_1_SelectedIndexChanged() in MainPage.cs file but the UI is not getting updated.
MainPage.xaml:
<Label x:Name="Security_Questions" Margin="0,20,0,0" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0" Text="Security Questions" FontSize="Micro" TextColor="MediumVioletRed"></Label>
<Picker x:Name="Security_Question_1" ItemsSource="{Binding SecurityQuestions_List}" Title="Select question one" Grid.Column="0" Grid.Row="1" Margin="-4,0,0,0" FontSize="Micro">
</Picker>
<Entry x:Name="Security_Answer_1" Placeholder="Type answer" Grid.Column="1" Grid.Row="1" FontSize="Micro"/>
<Picker x:Name="Security_Question_2" ItemsSource="{Binding SecurityQuestions_List}" Title="Select question two" Grid.Column="0" Grid.Row="2" Margin="-4,0,0,0" FontSize="Micro">
</Picker>
<Entry x:Name="Security_Answer_2" Placeholder="Type answer" Grid.Column="1" Grid.Row="2" FontSize="Micro"/>
<Picker x:Name="Security_Question_3" ItemsSource="{Binding SecurityQuestions_List}" SelectedIndexChanged="Security_Question_3_SelectedIndexChanged" Title="Select question three" Grid.Column="0" Grid.Row="3" Margin="-4,0,0,0" FontSize="Micro">
MainPage.cs file:
public MainPage()
{
InitializeComponent();
this.BindingContext = new RegistrationPageViewModel();
}
private void Security_Question_1_SelectedIndexChanged(object sender, EventArgs e)
{
try
{
var t1 = ((Xamarin.Forms.Picker)sender).SelectedItem.ToString();
if (t1 == "What is your first vehicle number?")
{
this.Security_Question_2.ItemsSource.Remove("What is your first vehicle number?");
this.Security_Question_3.ItemsSource.Remove("What is your first vehicle number?");
}
else if (t1 == "What is your child's nick name?")
{
this.Security_Question_2.ItemsSource.Remove("What is your child's nick name?");
this.Security_Question_3.ItemsSource.Remove("What is your first vehicle number?");
}
else
{
this.Security_Question_2.ItemsSource.Remove("What is your first school name?");
this.Security_Question_3.ItemsSource.Remove("What is your first vehicle number?");
}
}
catch (Exception)
{
throw;
}
}
RegistrationPageViewModel:
public RegistrationPageViewModel()
{
_department = new List<string>()
{
"What is your first vehicle number?",
"What is your child's nick name?",
"What is your first school name?"
};
}
List<string> _department;
public List<string> SecurityQuestions_List
{
get { return _department; }
private set
{
_department = value;
OnPropertyChanged();
}
}
Any help is appreciated.
You can use converter to 2 other Entries at ItemSource while you use Data Binding, passing the SelectedItem from the Entry as Converter Parameter and inside the converter you can remove the selected item that you passed as parameter.
"I want to avoid the user to select duplicate value in each picker".
You can do something with property SelectedItemProperty, prob not the best way to do it, but one.
For each picker you bind property SelectedItemProperty to a property in the ViewModel. Setting null this property will do the job when a user select a value that is already set in the other picker. Let's say this with two pickers, you can easily adapt it to three pickers.
<Picker x:Name="Security_Question_1" .... SelectedItemProperty="SelectedItemPicker1">
</Picker>
<Picker x:Name="Security_Question_2" .... SelectedItemProperty="SelectedItemPicker2">
</Picker>
ViewModel
public string SelectedItemPicker1
{
get => _selectedItemPicker1;
set
{
if (_selectedItemPicker1== value) return;
if (value == _selectedItemPicker2)
{
_selectedItemPicker2 = null;
OnPropertyChanged("SelectedItemPicker2");
}
_selectedItemPicker1 = value;
OnPropertyChanged("SelectedItemPicker1");
}
}
public string SelectedItemPicker2
{
get => _selectedItemPicker2;
set
{
if (_selectedItemPicker2 == value) return;
_selectedItemPicker2 = value == _selectedItemPicker1 ? null : value;
OnPropertyChanged("SelectedItemPicker2");
}
}
I'm not fan of having such logic in setters but as I said there should be a better approach.

Resources