问题
I'm trying to do a two way binding of the SelectedIndex attribute of a ListPicker in a Windows Phone 7 UserControl.
It raises the following exception when I set the DataContext:
SelectedIndex must always be set to a valid value.
This is the XAML code
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<toolkit:ListPicker
Grid.Row="0"
x:Name="List1"
SelectionChanged="Picker_SelectionChanged"
SelectedIndex="{Binding PickerSelectedIndex, Mode=TwoWay}"
ItemTemplate="{StaticResource PickerTemplate}"
ItemsSource="{Binding MyList}"/>
</Grid>
And the code behind in DataContext
private ObservableCollection<MyClass> myList = null;
public ObservableCollection<MyClass> MyList
{
get { return this.myList; }
set
{
if (value != this.myList)
{
this.myList= value;
NotifyPropertyChanged("MyList");
this.PickerSelectedIndex = 0;
}
}
}
private int pickerSelectedIndex = 0;
public int PickerSelectedIndex
{
get
{
return this.pickerSelectedIndex;
}
set
{
this.pickerSelectedIndex= value;
}
}
Putting a breakpoint in PickerSelectedIndex.get
I can see that it is returned correctly (0
).
I am sure that the problem is SelectedIndex="{Binding PickerSelectedIndex, Mode=TwoWay}"
because deleting this line solves the problem, and I can see the ListPicker correctly loaded with the data from MyList.
I can't see where is the problem...
回答1:
Moving SelectedIndex
after ItemsSource
solved the problem.
This is the working snippet
<toolkit:ListPicker
Grid.Row="0"
x:Name="List1"
SelectionChanged="Picker_SelectionChanged"
ItemTemplate="{StaticResource PickerTemplate}"
ItemsSource="{Binding MyList}"
SelectedIndex="{Binding PickerSelectedIndex, Mode=TwoWay}"/>
Anyone have an explanation for this?
回答2:
My guess would be that the binding is being applied with a default value of zero when created and before the items exist. It's therefore trying to select the first item (with a zero index) before that item is created.
Try making sure that the ViewModel property of PickerSelectedIndex
defaults to -1.
You may also want to delay setting the binding until the items are created.
回答3:
Matt Lacey is right; binding happens before the data items get populated & hence the error. If you have an event handler for SelectionChanged, you will notice that a breakpoint in it will be hit as the page/listpicker loads. Here is one way to get around this initialization issue:
private void SomeListPicker_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// Make sure we don't handle the event during initiation.
if (e.RemovedItems != null && e.RemovedItems.Count > 0)
{
if (this.SomeListPicker.SelectedItem != null)
{
// Do actual stuff.
}
}
}
回答4:
I had the same issue and the ordering of the properties in XAML didn't help. In my case, I was binding ItemsSource to a property on a StaticResource, but I was binding SelectedIndex with a property of my page's ViewModel. The moment I changed the binding of ItemsSource to bind to a property on the ViewModel itself (i.e. duplicated a property of the StaticResource), my issue went away and I was able to perform 2-way binding on SelectedIndex as well.
回答5:
I've found the same problem with my app. But I noticed that it happens when I delete all the elements of the list bounded to the ListPicker in the ViewModel. So it isn't necessary to Bind SelectedIndex with another property since the problem depends only on the list bounded. Here is my code which work fine for me:
<toolkit:ListPicker x:Name="ListaCorsi"
SelectionChanged="ListaCorsi_SelectionChanged"
ItemsSource="{Binding ListaCorsiUser}"
SelectionMode="Single"
ItemTemplate="{StaticResource CorsiDataTemplate}"
ItemsPanel="{StaticResource ItemsPanelTemplateListaCorsi}"/>
The list in view model:
private ObservableCollection<Corsi> _listaCorsiUser;
public ObservableCollection<Corsi> ListaCorsiUser
{
get { return _listaCorsiUser; }
set
{
_listaCorsiUser = value;
OnPropertyChanged("ListaCorsiUser");
}
}
The handler for SelectionChanged:
void ListaCorsi_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (ListaCorsi.SelectedItem != null)
{
---
this.CorsoSelected = ListaCorsi.SelectedItem as Corsi;
}
}
Where Corsi is the class type of the list.
Here ListPicker template:
<DataTemplate x:Key="CorsiDataTemplate">
<Grid>
<Grid.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black" Offset="0"/>
<GradientStop Color="#FF3523FF" Offset="0.25"/>
<GradientStop Color="Black" Offset="1"/>
<GradientStop Color="#FF3523FF" Offset="0.75"/>
</LinearGradientBrush>
</Grid.Background>
<TextBlock TextWrapping="Wrap" Text="{Binding NomeCorso}" FontSize="24" FontFamily="Freestyle Script" TextAlignment="Center"/>
</Grid>
</DataTemplate>
And, in the end, the method delete that checks if the list returned by IsolatedStorage is empty, if so, I put a fake empty element in the list bounded to the ListPicker, in order to not receive the error mentioned in this post.
if (this.CorsoSelected != null)
{
---
List<Corsi> corsi = new List<Corsi>(DBHelper.GetCorsiByUserId(PassaggioValori.UserId));
if (corsi.Count > 0)
{
this.ListaCorsiUser = new ObservableCollection<Corsi>(corsi);
}
else
{
this.ListaCorsiUser = new ObservableCollection<Corsi>(new List<Corsi>() { new Corsi()});
}
----
}
The strange thing was that, if the list was empty when the page has been loaded, nothing happens, instead, when I removed the last element from the list, the application raised the exception "SelectedItem must always be set to a valid value". Now the problem is solved.
来源:https://stackoverflow.com/questions/6032882/binding-listpicker-selectedindex-problem