为什么在 for 循环内设置 WPF ListCollectionView 过滤器谓词会导致奇怪的行为?
Why does setting a WPF ListCollectionView Filter predicate inside a for loop cause strange behavior?
我最近专注于 WPF 应用程序的一个问题,在 for 循环中设置过滤器谓词导致 UI 中没有任何内容出现:
private void SetListCollectionViewFiltersHasProblem()
{
for (int i = 0; i <= 1; ++i)
{
_peopleViews[i].Filter = obj => ((Person)obj).Number == i;
}
}
而我认为等效的代码工作得很好:
private void SetListCollectionViewFiltersWorks()
{
_peopleViews[0].Filter = obj => ((Person)obj).Number == 0;
_peopleViews[1].Filter = obj => ((Person)obj).Number == 1;
}
这与For循环中的lambda表达式有关。 This sort of problem exists in Javascript 但我认为 C# 不一样。
使用 For 循环设置 Filter 函数没有问题的最佳解决方案是什么?
完整代码:
<Window x:Class="WpfListCollectionView.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel Orientation="Horizontal">
<Button Name="AddList0" Click="AddList0_Click">
Add to list 0
</Button>
<Button Name="AddList1" Click="AddList1_Click">
Add to list 1
</Button>
<ItemsControl Name="_list0" ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=FirstName}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl Name="_list1" ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=FirstName}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Window>
namespace WpfListCollectionView
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public class Person
{
public string FirstName { get; set; }
public int Number { get; set; }
}
private ObservableCollection<Person> _people = new ObservableCollection<Person>();
private ListCollectionView[] _peopleViews = new ListCollectionView[2];
public MainWindow()
{
InitializeComponent();
_peopleViews[0] = new ListCollectionView(this._people);
_peopleViews[1] = new ListCollectionView(this._people);
SetListCollectionViewFiltersHasProblem();
//SetListCollectionViewFiltersWorks();
// Set up the DataContexts
_list0.DataContext = _peopleViews[0];
_list1.DataContext = _peopleViews[1];
// Put some data in
_people.Add(new Person { FirstName = "Mike", Number = 0 });
_people.Add(new Person { FirstName = "Joe", Number = 1 });
}
private void SetListCollectionViewFiltersHasProblem()
{
for (int i = 0; i <= 1; ++i)
{
_peopleViews[i].Filter = obj => ((Person)obj).Number == i;
}
}
private void SetListCollectionViewFiltersWorks()
{
_peopleViews[0].Filter = obj => ((Person)obj).Number == 0;
_peopleViews[1].Filter = obj => ((Person)obj).Number == 1;
}
private void AddList0_Click(object sender, RoutedEventArgs e)
{
_people.Add(new Person() { FirstName = "AddedAlan", Number = 0 });
}
private void AddList1_Click(object sender, RoutedEventArgs e)
{
_people.Add(new Person() { FirstName = "AddedAdam", Number = 1 });
}
}
}
您设置了稍后将针对您 collection 的每个项目调用的委托。此时没有调用它,问题是它捕获 i 变量而不是它的值。您可以通过在循环中使用局部变量来解决它
for (int i = 0; i <= 1; ++i)
{
var localValue = i;
_peopleViews[i].Filter = obj => ((Person)obj).Number == localValue;
}
我最近专注于 WPF 应用程序的一个问题,在 for 循环中设置过滤器谓词导致 UI 中没有任何内容出现:
private void SetListCollectionViewFiltersHasProblem()
{
for (int i = 0; i <= 1; ++i)
{
_peopleViews[i].Filter = obj => ((Person)obj).Number == i;
}
}
而我认为等效的代码工作得很好:
private void SetListCollectionViewFiltersWorks()
{
_peopleViews[0].Filter = obj => ((Person)obj).Number == 0;
_peopleViews[1].Filter = obj => ((Person)obj).Number == 1;
}
这与For循环中的lambda表达式有关。 This sort of problem exists in Javascript 但我认为 C# 不一样。
使用 For 循环设置 Filter 函数没有问题的最佳解决方案是什么?
完整代码:
<Window x:Class="WpfListCollectionView.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel Orientation="Horizontal">
<Button Name="AddList0" Click="AddList0_Click">
Add to list 0
</Button>
<Button Name="AddList1" Click="AddList1_Click">
Add to list 1
</Button>
<ItemsControl Name="_list0" ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=FirstName}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl Name="_list1" ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=FirstName}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Window>
namespace WpfListCollectionView
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public class Person
{
public string FirstName { get; set; }
public int Number { get; set; }
}
private ObservableCollection<Person> _people = new ObservableCollection<Person>();
private ListCollectionView[] _peopleViews = new ListCollectionView[2];
public MainWindow()
{
InitializeComponent();
_peopleViews[0] = new ListCollectionView(this._people);
_peopleViews[1] = new ListCollectionView(this._people);
SetListCollectionViewFiltersHasProblem();
//SetListCollectionViewFiltersWorks();
// Set up the DataContexts
_list0.DataContext = _peopleViews[0];
_list1.DataContext = _peopleViews[1];
// Put some data in
_people.Add(new Person { FirstName = "Mike", Number = 0 });
_people.Add(new Person { FirstName = "Joe", Number = 1 });
}
private void SetListCollectionViewFiltersHasProblem()
{
for (int i = 0; i <= 1; ++i)
{
_peopleViews[i].Filter = obj => ((Person)obj).Number == i;
}
}
private void SetListCollectionViewFiltersWorks()
{
_peopleViews[0].Filter = obj => ((Person)obj).Number == 0;
_peopleViews[1].Filter = obj => ((Person)obj).Number == 1;
}
private void AddList0_Click(object sender, RoutedEventArgs e)
{
_people.Add(new Person() { FirstName = "AddedAlan", Number = 0 });
}
private void AddList1_Click(object sender, RoutedEventArgs e)
{
_people.Add(new Person() { FirstName = "AddedAdam", Number = 1 });
}
}
}
您设置了稍后将针对您 collection 的每个项目调用的委托。此时没有调用它,问题是它捕获 i 变量而不是它的值。您可以通过在循环中使用局部变量来解决它
for (int i = 0; i <= 1; ++i)
{
var localValue = i;
_peopleViews[i].Filter = obj => ((Person)obj).Number == localValue;
}