Ошибка Listpicker SelectedItem всегда должна быть установлена ​​на допустимое значение

У меня есть страница в приложении Windows Phone 7, где пользователь может редактировать или удалять объект Transaction. Объект Transaction - это класс Linq-to-Sql, который имеет отношение к классу Account и классу Category. На странице я использую ListPicker, чтобы пользователь мог выбрать учетную запись и категорию для данной транзакции, например:

<toolkit:ListPicker Grid.Row="1" FullModeHeader="Choose the Account" FullModeItemTemplate="{StaticResource FullModeItemTemplate}" ExpansionMode="FullScreenOnly" Background="#26000000" Margin="10,0,10,0" Name="Account" SelectedItem="{Binding Account, Mode=TwoWay}" Tap="ListPicker_Tap" />

<toolkit:ListPicker Grid.Row="7" FullModeHeader="Choose the Category" FullModeItemTemplate="{StaticResource FullModeItemTemplate}" ExpansionMode="FullScreenOnly" Background="#26000000" Margin="10,0,10,0" Name="Category" SelectedItem="{Binding Category, Mode=TwoWay}" Tap="ListPicker_Tap" />

Событие ListPicker_Tap является исправлением ошибки в версии WPF Toolkit для Windows Phone версии Aug/2011 и просто следующее:

    private void ListPicker_Tap(object sender, System.Windows.Input.GestureEventArgs e)
    {
        ListPicker lp = (ListPicker)sender; 
        lp.Open();
    }

Если пользователь отредактирует транзакцию, все будет в порядке, но если пользователь попытается удалить его, я получаю сообщение об ошибке, указывающее, что "SelectedItem всегда должен быть установлен на допустимое значение".

Здесь код, если пользователь нажимает кнопку удаления в панели приложений в TransactionPage.xaml.cs:

    private void appBarDelete_Click(object sender, EventArgs e)
    {
        MessageBoxResult result = MessageBox.Show("Are you sure?\n", "Confirm", MessageBoxButton.OKCancel);

        if (result == MessageBoxResult.OK)
        {
            App.ViewModel.DeleteTransaction(transaction);
        }

        NavigationService.GoBack();
    }

Мой метод ViewModel.DeleteTransaction:

    public void DeleteTransaction(Transaction transaction)
    {
        AllTransactions.Remove(transaction);
        transactionRepository.Delete(transaction);
    }

Мой метод transactionRepository.Delete:

    public void Delete(Transaction transaction)
    {
        Context.Transactions.DeleteOnSubmit(transaction);
        Context.SubmitChanges();
    }

Я получаю ошибку в выполнении Context.SubmitChanges(), отладка указывает на NotifyPropertyChanged внутри класса Transaction, строка, в которой я получаю ошибку, такова:

    protected virtual void SendPropertyChanged(String propertyName)
    {
        if ((this.PropertyChanged != null))
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

В атрибуте propertyName значение равно "Категория". Похоже, что при удалении объекта отправляется событие propertychanged категории и учетных записей, а поскольку listpicker находится в режиме TwoWay, у него есть некоторые проблемы с этим. Как я могу это исправить? Мне нужна помощь.

Ответ 1

Проблема в том, что ListPicker ожидает, что SelectedItem будет ListPickerItem, тогда как вы привязываете его к объекту типа Transaction. Вы можете обойти проблему, привязав ее к свойству SelectedIndex, а затем выберите соответствующий объект из ViewModel на основе индекса.

Кроме того, если причина, по которой вы определили обработчик Tap, определяется из-за ошибки, когда ListPicker не открывается, когда помещается внутри ScrollViewer, посмотрите патч-код 10247. Если вы перекомпилируете набор инструментов с этим исправлением, он устранит проблему.

Ответ 2

Эта ошибка также может быть вызвана порядком свойств XAML:

Это НЕ работает (генерирует исключение, потому что ItemSource имеет значение null, когда установлен SelectedItem):

<toolkit:ListPicker DisplayMemberPath="Title" SelectionMode="Single" 
SelectedItem="{Binding SelectedCategory, Mode=TwoWay}"
ItemsSource="{Binding Categories}" />

Это работает, поскольку сначала инициализируется исходный источник:

<toolkit:ListPicker DisplayMemberPath="Title" SelectionMode="Single" 
ItemsSource="{Binding Categories}"
SelectedItem="{Binding SelectedCategory, Mode=TwoWay}" />

Ответ 3

ListPicker использует Items.IndexOf для получения индекса экземпляра элемента, который он должен выбрать.

Если экземпляр не соответствует (это не экземпляр объекта из коллекции), IndexOf вернет -1, а InvalidOperationException будет выведено с сообщением: "SelectedItem всегда должен быть установлен на допустимое значение".

Переопределить метод Equals типа элемента в коллекции, и он будет работать как ожидалось.

Пример:

public override bool Equals(object obj)
{
         var target = obj as ThisType;
         if (target == null)
             return false;

         if (this.ID == target.ID)
             return true;

         return false;
 }

Надеюсь, что это поможет

Ответ 4

Есть только две проверки, которая выдает InvalidOperationException на SelectedItem

  • Элементы Listpicker равны нулю (Декларативный: порядок атрибутов имеет значение. Если selecteditem должен появиться после itemsource (Программный: убедитесь, что источник товаров загружен)
  • Listpicker применяет Indexof к элементам для установки выбранного элемента. Поэтому убедитесь, что вы при необходимости замените Equals.

Отладка с помощью watch on listpicker.Items и переопределенный метод Equals помогут нам идентифицировать проблему