你知道吗,为什么会抛出
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;
行。
如果我删除了notify属性,它不会抛出异常,但会发生另一个有趣的错误:
第二个代码:
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
。
如果我们将当前项设置为null,并在其上调用OnPropertyChanged
,则会达到第二个代码的稳定性。从ComboBox中选择有效IsSynchronizedWithCurrentItem
的{{1}}属性仍然丢失。在以下代码中,如果我拨打Item
,DoFirst()
,则会选择“bbb”。将DoThird()
设置为null(之前调用Item
)后,它不会选择“bbb”:
第四个代码:
DoSecond()
BR, 马顿
答案 0 :(得分:1)
出于某种原因,当您将IsSynchronizedWithCurrentItem
设置为true
并且SelectedItem
绑定实现INotifyPropertyChanged
的源对象时,ICollectionView
不允许您明确设置CurrentItem
到null
(例如,调用MoveCurrentToPosition(-1)
,否则可以正常工作)。我有一些想法,为什么会这样,但我不想推测。
我发现将CurrentItem
设置为null
的唯一方法是明确地将null
分配给SelectedItem
绑定到的属性({{1}您的案例中的属性)并使用适当的属性名称引发Item
事件。使用调试器,您会注意到此时PropertyChanged
将在内部设置为CurrentItem
。然后你很清楚应用一个没有结果的过滤器。
至于你的另一个问题,那就是在应用一个不产生结果的过滤器然后另一个产生一些结果null
的过滤器停止工作之后,它实际上不是真的。同样,如果您使用调试器,您会注意到在应用第二个过滤器后,IsSynchronizedWithCurrentItem
属性保持不变 - 它仍然会产生CurrentItem
,因此null
仍然与SelectedItem
保持同步{1}}。我担心在这种情况下,您需要自己选择第一个可用项目 - 例如为CurrentItem
媒体资源分配适当的价值或致电Item
。
修改强>
回应你的评论和有关的更新细节 - 这正是我在前一段中提到的(特别是最后一句)。您可能会注意到,在应用过滤器时,只要当前值仍然可行,它就不会自动更改。好吧,Items.MoveCurrentToFirst()
(意思是“没有当前价值”)总是可行的,所以它永远不会自动改变,这就是你必须自己做的原因。
与您的问题中的示例相反,这些示例是具有预期结果的任意情况(您知道何时应用空滤镜)我认为您将无法预先判断是否会出现这种情况过滤器将产生任何结果。这里最简单的(在我看来)解决方案是编写一个简单但通用的方法来将任何过滤器应用于集合:
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
时,将选择第一个可用项目