I have a picker inside a CollectionView that should select a predefined item ColorNav
but when it starts the item is not selected.
XAML:
<StackLayout>
<Label Text="Players" />
<CollectionView ItemsSource="{Binding PlayerList}">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="model:Player">
<SwipeView>
<Border>
<Grid ColumnDefinitions="*, *, *" ColumnSpacing="1" >
<Label Grid.Column="0" Text="{Binding Name}" />
<Picker Grid.Column="1" ItemsSource="{Binding ColorsPicker, Source={RelativeSource AncestorType={x:Type viewmodel:MainViewModel}}}" ItemDisplayBinding="{Binding Name}" SelectedItem="{Binding ColorNav}"/>
<Label Grid.Column="2" Text="{Binding ColorNav.Name}"/>
</Grid>
</Border>
</SwipeView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<Button Text="Save" Command="{Binding SaveCommand}"/>
</StackLayout>
C#
public class Player
{
public string Name { get; set; } = default!;
public int ColorId { get; set; } = default!;
public Colour ColorNav { get; set; } = default!;
}
public class Colour
{
public int ColorId { get; set; }
public string Name { get; set; } = default!;
}
public MainViewModel() {
ColorsPicker = [
new Colour {
ColorId = 1,
Name = "Red"
},
new Colour {
ColorId = 2,
Name = "Blue"
},
new Colour {
ColorId = 3,
Name = "Yellow"
},
];
PlayerList = [
new Player {
Name = "John",
ColorId = 1,
ColorNav = ColorsPicker[0],
},
new Player {
Name = "Carla",
ColorId = 3,
ColorNav = ColorsPicker[2],
},
];
}
While debugging, I can see that ColorNav is loaded in the BindingContext, but somehow it becomes null when the page starts. When I go to 'Save, ColorNav
is null for every player. If I remove SelectedItem="{Binding ColorNav}"
from the Picker, ColorNav remains as expected.Every player in the BindingContext has ColorNav initialized
You can put variable ColorsPicker
and variable ColorNav
inside of model Player.cs
and implement interface INotifyPropertyChanged
for model Player.cs
.
public class Player: INotifyPropertyChanged
{
public string Name { get; set; } = default!;
public int ColorId { get; set; } = default!;
// public Colour ColorNav { get; set; } = default!;
public ObservableCollection<Colour> ColorsPicker { get; set; } = new ObservableCollection<Colour>();
//add a property for the SelectedItem property of picker
private Colour _colorNav;
public Colour ColorNav
{
set
{
SetProperty(ref _colorNav, value);
}
get { return _colorNav; }
}
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));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Besides, if we want UI update(<Label Grid.Column="2" Text="{Binding ColorNav.Name}"
) while changing the selected Item of current Picker, we also need to implement interface INotifyPropertyChanged
for model Colour.cs
public class Colour: INotifyPropertyChanged
{
public int ColorId { get; set; }
//public string Name { get; set; } = default!;
private string _name;
public string Name
{
set
{
SetProperty(ref _name, value);
}
get { return _name; }
}
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));
}
public event PropertyChangedEventHandler PropertyChanged;
}
Then we can initialize MainViewModel.cs
as follows:
public class MainViewModel
{
public ObservableCollection<Colour> ColorsPicker { get; set; } = new ObservableCollection<Colour>();
public ObservableCollection<Player> PlayerList { get; set; } = new ObservableCollection<Player>();
public MainViewModel() {
ColorsPicker = [
new Colour {
ColorId = 1,
Name = "Red"
},
new Colour {
ColorId = 2,
Name = "Blue"
},
new Colour {
ColorId = 3,
Name = "Yellow"
},
];
PlayerList = [
new Player {
Name = "John",
ColorId = 1,
ColorNav = ColorsPicker[0],
ColorsPicker = ColorsPicker // initialize ColorsPicker
},
new Player {
Name = "Carla",
ColorId = 3,
ColorNav = ColorsPicker[2],
ColorsPicker = ColorsPicker // initialize ColorsPicker
},
];
}
}
And then, we can bind data as follows:
<CollectionView ItemsSource="{Binding PlayerList}">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="model:Player">
<SwipeView>
<Border>
<Grid ColumnDefinitions="*, *, *" ColumnSpacing="1" >
<Label Grid.Column="0" Text="{Binding Name}" />
<!--<Picker Grid.Column="1" ItemsSource="{Binding ColorsPicker, Source={RelativeSource AncestorType={x:Type viewmodel:MainViewModel}}}" ItemDisplayBinding="{Binding Name}" SelectedItem="{Binding ColorNav}"/>-->
<Picker Grid.Column="1" ItemsSource="{Binding ColorsPicker}" ItemDisplayBinding="{Binding Name}" SelectedItem="{Binding ColorNav ,Mode=TwoWay}"/>
<Label Grid.Column="2" Text="{Binding ColorNav.Name}"/>
</Grid>
</Border>
</SwipeView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>