1

Do Вы знаете, почему это броситьфильтрация CollectionViewSource приносит пустой результат будет бросать исключение

An unhandled exception of type 'System.NullReferenceException' occurred in PresentationFramework.dll 

Additional information: Object reference not set to an instance of an object. 

Когда я пытаюсь фильтровать CollectionViewSource, что не дает достоверных строк?

Код следующий.

XAML:

<ComboBox SelectedItem="{Binding Item}" ItemsSource="{Binding Items}" IsSynchronizedWithCurrentItem="True" /> 

первый код:

public class Model : INotifyPropertyChanged 
    { 
     protected virtual void OnPropertyChanged(string propertyName) 
     { 
      PropertyChangedEventHandler handler = PropertyChanged; 
      if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); 
     } 
     public string Item { get; set; } 
     public ICollectionView Items { get; set; } 
     public Model() 
     { 
      Items = CollectionViewSource.GetDefaultView(new ObservableCollection<string>(new List<string> { "aaa", "bbb" })); 
     } 
     public void DoFirst() 
     { 
      Items.Filter = o => ((string)o).StartsWith("a"); 
     } 
     public void DoSecond() 
     { 
      Items.Filter = o => false; 
     } 

     public event PropertyChangedEventHandler PropertyChanged; 
    } 

DoFirst() работает. DoSecond() нет. Исключение происходит от линии Items.Filter = o => false;.

Если я удалить уведомит свойство вещи, он не будет бросать исключение, но другая интересная ошибка происходит:

второй код:

public class Model 
    { 
     public string Item { get; set; } 
     public ICollectionView Items { get; set; } 
     public Model() 
     { 
      Items = CollectionViewSource.GetDefaultView(new ObservableCollection<string>(new List<string> { "aaa", "bbb" })); 
     } 
     public void DoFirst() 
     { 
      Items.Filter = o => ((string)o).StartsWith("a"); 
     } 
     public void DoSecond() 
     { 
      Items.Filter = o => false; 
     } 
    } 

Пустой список отображается. Это верно. Но тогда, когда я DoFirst(), в списке отображается «aaa» справа, он не выбран по умолчанию. IsSynchronizedWithCurrentItem не срабатывает.

Если я попытаюсь защитить фильтр от NRE, произойдет третий вид поведения.

третий код:

public class Model : INotifyPropertyChanged 
    { 
     protected virtual void OnPropertyChanged(string propertyName) 
     { 
      PropertyChangedEventHandler handler = PropertyChanged; 
      if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); 
     } 
     public string Item { get; set; } 
     public ICollectionView Items { get; set; } 
     public Model() 
     { 
      Items = CollectionViewSource.GetDefaultView(new ObservableCollection<string>(new List<string> { "aaa", "bbb" })); 
     } 
     public void DoFirst() 
     { 
      try 
      { 
       Items.Filter = o => ((string)o).StartsWith("a"); 
      } catch (NullReferenceException) { } 
     } 
     public void DoSecond() 
     { 
      try 
      { 
       Items.Filter = o => false; 
      } catch (NullReferenceException) { } 
     } 

     public event PropertyChangedEventHandler PropertyChanged; 
    } 

В этом случае, выбираемые элементы в выпадающем списке прав. После DoSecond() список пуст, но последний выбранный элемент по-прежнему выбран ... После DoSecond() DoFirst() также выбрасывает NullReferenceException.

Если мы установим текущий элемент в нуль и назовем OnPropertyChanged, то стабильность второго кода будет достигнута. Свойство IsSynchronizedWithCurrentItem для выбора действительного Item из ComboBox все еще потеряно. В следующем коде, если я вызываю DoFirst(), DoThird(), тогда будет выбран «bbb». После установки Item обнулить (назовем DoSecond() раньше), он не будет выбрать "БББ":

четвертый код:

public class Model : INotifyPropertyChanged 
{ 
    protected virtual void OnPropertyChanged(string propertyName) 
    { 
     PropertyChangedEventHandler handler = PropertyChanged; 
     if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); 
    } 

    public string Item { get; set; } 

    public ICollectionView Items { get; set; } 
    public Model() 
    { 
     Items = CollectionViewSource.GetDefaultView(new ObservableCollection<string>(new List<string> { "aaa", "bbb" })); 
    } 
    public void DoFirst() 
    { 
     Items.Filter = o => ((string)o).StartsWith("a"); 
    } 

    public void DoSecond() 
    { 
     Item = null; 
     OnPropertyChanged("Item"); 
     Items.Filter = o => false; 
    } 

    public void DoThird() 
    { 
     Items.Filter = o => ((string)o).StartsWith("b"); 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
} 

Br, Мартон

+0

[Что такое 'NullReferenceException' и как его исправить?] (http://stackoverflow.com/questions/4660142/what-is-a-nullreferenceexception-and-how-do- i-fix-it) –

+0

Первая версия кода не использует 'OnPropertyChanged' в любой точке. Вы что-то убрали? – Nitram

+0

SonerGönül: Спасибо, но я уже знаю, что такое исключение для ссылки. Проблема не связана с постами, которые вы цитируете. Я ожидаю, что выбранный элемент будет иметь значение null после фильтрации. Но почему это происходит, только если класс реализует INotifyPropertyChanged? @ Нитрам: Вы видите правильно. Нет вызова OnPropertyChanged. Я создал минимальный пример, и ему не нужен вызов OnPropertyChanged, но он по-прежнему ведет себя по-другому. – ntohl

ответ

1

По какой-то причине, когда вы установите IsSynchronizedWithCurrentItem в true и исходный объект для SelectedItem связующих орудий INotifyPropertyChanged, ICollectionView не позволяет явно указывать CurrentItem на null (например, путем вызова MoveCurrentToPosition(-1), который в противном случае отлично работает). У меня есть несколько идей, почему это может быть, но я не хочу спекулировать.

Единственный способ я нашел, чтобы установить CurrentItem в null является явно Ассинг null к собственности, к которой SelectedItem связан (Item свойство в вашем случае) и поднять PropertyChanged событие с соответствующим именем свойства. Используя отладчик, вы заметите, что в этот момент CurrentItem будет установлен внутри null. Тогда вы можете применить фильтр, который не даст никаких результатов.

Что касается вашей другой проблемы, которая заключается в том, что после применения фильтра, который не дает никаких результатов, а затем другой, который дает некоторые результаты, IsSynchronizedWithCurrentItem перестает работать, на самом деле это не так. Опять же, если вы используете отладчик, вы заметите, что после применения второго фильтра свойство CurrentItem остается неизменным - оно все равно дает null, поэтому SelectedItem все еще находится в синхронизации с CurrentItem. Я боюсь, что в этом случае вам нужно будет выбрать первый доступный элемент самостоятельно - например, назначив соответствующее значение вашему свойству Item или позвонив по номеру Items.MoveCurrentToFirst().

EDIT

В ответ на ваш комментарий и обновленные сведения о котором идет речь - это именно то, что я имел в виду в предыдущем пункте (особенно последнее предложение). Вы можете заметить, что при применении фильтра, когда текущее значение по-прежнему возможно, оно не будет автоматически изменено. Ну, null (то есть «нет текущего значения») всегда возможно, поэтому его никогда не будет автоматически изменено, поэтому вы должны сделать это сами.

В отличие от примеров вашего вопроса, которые являются произвольными случаями с ожидаемыми результатами (вы знаете, когда вы применяете пустой фильтр), я полагаю, вы попадете в ситуацию, когда вы не сможете сказать заранее будет ли фильтр давать какие-либо результаты или нет. Самый простой (на мой взгляд) решение здесь, чтобы написать простой, но универсальный метод применить любой фильтр к коллекции:

public void SetFilter(Predicate<object> filter) 
{ 
    if (Items.CurrentItem != null && !filter(Items.CurrentItem)) 
    { 
     Item = null; 
     OnPropertyChanged("Item"); 
    } 
    Items.Filter = filter; 
    if (Items.CurrentItem == null && !Items.IsEmpty) 
     Items.MoveCurrentToFirst(); 
} 

Это даст вам следующее поведение:

  • При применении фильтр, который сохраняет текущий элемент, он не изменится
  • При применении пустого фильтра, null будет выбран
  • При применении непустого фильтра, и текущий элемент null, первый доступный пункт будет выбрано
+0

Спасибо! Существует свойство 'IsSynchronizedWithCurrentItem', которое в настоящее время нарушено. Если список теряет текущий выбранный элемент, потому что фильтр отфильтровывает его, чем тогда, будет 'MoveCurrentToFirst()'. Это не работает после пустого фильтра. Стабильность второго кода достигнута, но она все еще нестабильна. – ntohl

+0

Я попытался сохранить поведение по умолчанию, не сильно взломав. Я подумал, что в каком-то контексте просто вызовите 'Refresh()'. Обложка @ Grx70 - хороший способ обхода. Мне нравится сохранить первую пулю. – ntohl

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

  • Нет связанных вопросов^_^