2013-03-12 2 views
5

В течение дня или двух я пытался выяснить странную ошибку WPF. У меня есть элемент управления ListBox, связанный с ObservableCollection PolicyId, который представляет собой класс, реализующий как INotifyProperty, так и IEquatable, и имеет одно и только одно поле, называемое Id (которое является строкой). PolicyId являются указателями на различные элементы политики, которые изменяют рабочий процесс в других частях программы.ListBox теряет способность изменять selecteditem, когда selecteditem данные изменены

ListBox привязан к коллекции, и есть кнопка добавления и удаления, которая просто добавляет и удаляет идентификаторы в ListBox. Существует также выпадающий ComboBox, который можно использовать для изменения PolicyId выбранного элемента в ListBox на другой идентификатор (какие идентификаторы доступны для выбора, в другом месте). Установка значения свойства ListBox.SelectedItem.Id выполняется в бэкэнд при запуске события ComboBox.SelectionChanged, а значения в ComboBox изменяются при запуске события ListBox.SelectionChanged, что гарантирует, что ни один из элементов в ListBox всегда идентичны.

Теперь эта реализация работает по большей части. Все политики перечислены в ListBox, и вы можете добавить новые (которые добавляют первую доступную ранее выбранную политику в нижней части списка) и выбирать и удалять любую политику, которая вам нравится.

До тех пор, пока вы не заменили одну политику другим, используя comboBox. Как только вы это сделаете, индекс застрял в том, который вы изменили, и нет ничего, что вы можете сделать, чтобы выбрать другую политику (установка ListBox.SelectedIndex не влияет). Нажмите достаточно долго, и в итоге вызывается следующее исключение: «System.ArgumentException: элемент с тем же ключом уже добавлен». Я подозреваю, что ошибка вызвана тем, что WPF пытается добавить выбранный элемент в коллекцию selectedItems listBox, хотя элемент уже существует или что-то в этом роде.

Я знаю, что ошибка имеет какое-то отношение к PolicyId, переопределяющему Equals (да, я вспомнил ограничение, что если Equals говорит, что два объекта равны eachother, то GetHashCode также должен возвращать одинаковое значение для двух, поэтому GetHashCode также переопределяется), потому что подобная настройка используется в других местах программы без проблем. Я не знаю, как все это происходит, тем более, что в коллекции нет дубликатов.

Это ListBox:

<ListBox Name="policyIdList" MinHeight="50" ItemsSource="{Binding Path=DataItem}" IsSynchronizedWithCurrentItem="True" SelectionChanged="policyIdList_SelectionChanged"> 
    <ListBox.Resources> 
     <Style TargetType="ScrollViewer"> 
      <Setter Property="HorizontalScrollBarVisibility" Value="Hidden" /> 
     </Style> 
    </ListBox.Resources> 
    <ListBox.ItemTemplate> 
     <DataTemplate> 
      <TextBlock DataContext="{Binding Path=Id}" Text="{Binding Converter={StaticResource PolicyIdToName}}"> 
       <TextBlock.ToolTip> 
        <ContentControl Content="{Binding}" ContentTemplate="{StaticResource PolicyByOidListItem}" /> 
       </TextBlock.ToolTip> 
      </TextBlock> 
     </DataTemplate> 
    </ListBox.ItemTemplate> 
</ListBox> 

И, хорошие меры, COMBOBOX:

<ComboBox Name="policyIdCombo" Grid.Row="1" Grid.Column="0" 
ItemTemplate="{StaticResource PolicyByOidListItem}" SelectionChanged="policyIdCombo_SelectionChanged" /> 
+0

Когда ваш класс PolicyId переопределяет 'Equals', он также переопределяет' GetHashCode'?Где-то в разделе «Замечания» в [Равно] (http://msdn.microsoft.com/en-us/library/bsc2ak47.aspx): «Типы, которые переопределяют Equals (Object), также должны переопределять GetHashCode, иначе хэш-таблицы могут не быть работать правильно ". Возможно, это как-то источник вашей проблемы, по крайней мере, не стоит также переопределять «GetHashCode». – Clemens

+0

Он делает, так что это не так. Я просто забыл упомянуть, что я также забыл переопределить GetHashCode (я добавил это сейчас). В любом случае, поскольку есть только одно поле, получающее оба этих метода, право действительно тривиально (сравните, если они оба одного класса, сравнивают поле, а затем возвращают поле .GetHashCode() в GetHashCode), если вы знаете GetHashCode должен возвращать тот же хеш для объектов, которые имеют равные знаки как истинные. –

ответ

9

Я думаю, что проблема в том, что вы Чаннин хэш-код элемента, когда вы изменить его ID, и ListBox этого не ожидает. See here для получения дополнительной информации.

Я только что обновил приложение от .NET 3.5 до 4.0 и столкнулся с этой проблемой. Если, как мне вы не можете использовать фиксированный хэш-код, то обходной путь является:

  1. Удалить объект, который будет изменен из списка
  2. Уведомить пользовательский интерфейс изменения
  3. Изменить идентификатор вашего объект (приводящий к изменению его hashcode)
  4. Добавьте свой объект обратно в список и сообщите об этом пользовательскому интерфейсу.
+0

Спасибо, это, кстати, как мы в конечном итоге (бессознательно) решили это - поскольку элементы переключения не работали, мы допустили только добавление и удаление политик. Поле со списком было использовано для выбора политики добавления. –

Смежные вопросы