如何使 ListView 在后台更新并且在 ItemsSource 更新时不冻结 UI?
How to make ListView update at background and not freez UI when ItemsSource updated?
我有一个 ListView:
<ListView ItemsSource="{Binding Students, IsAsync=True}">
<ListView.View>
<GridView>
<!--Some view things -->
</GridView>
</ListView.View>
</ListView>
这是我的 ViewModel 中的 Students
:
public ICollection<Student> Students
{
get
{
return _students;
}
set
{
_students = value;
OnPropertyChanged(() => this.Students);
}
}
我正在更新 Students
这样的一些任务(当用户在另一个 TreeView 中选择某些内容时我正在更新 SourceState
):
private State _sourceState;
public State SourceState
{
get
{
return _sourceState;
}
set
{
_sourceState = value;
OnSourceStateChanged();
OnPropertyChanged(() => this.SourceState);
}
}
private void OnSourceStateChanged()
{
Task updateStudentFromSourceStateTask = new Task(() =>
{
Students = _stateService.GetAllStudents(_sourceState);
});
updateStudentFromSourceStateTask.Start();
}
问题是每当发生变化时 Students
,UI 会冻结,直到 ListView 在新的 Students
中显示数据,我不知道如何让 ListView 刷新它的数据在后台线程中(我已经尝试 IsAsync=True
绑定但没有帮助)
你必须使用 "virtualizing"。
<ListView ItemsSource="{Binding Students}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.Resources>
<DataTemplate DataType="...">
<Grid>
...
<ListView/>
编辑:
也许您的问题在于来源类型以及您如何评估它。在我确实使用它的特定情况下,我的来源声明如下:
public IEnumerable<MyElementType> Elements
{
get { return (IEnumerable<MyElementType>)GetValue(ElementsProperty); }
set { SetValue(ElementsProperty, value); }
}
public static readonly DependencyProperty ElementsProperty =
DependencyProperty.Register("Elements", typeof(IEnumerable<MyElementType>), typeof(MyUiUserControlType), new UIPropertyMetadata(null));
当我将新过滤器应用于更大的块时,刷新发生了。当填充了所有必须实际可见的元素时,虚拟化将显示并允许立即开始使用您的列表。
您的问题可能出在使用 ICollection 上。但这已经取决于您的代码隐藏要求。
发生这种情况是因为我正在使用 Mahapps.Metro
并且它使 ListView 不使用 VirtualizingStackPanel
使用Style="{DynamicResource VirtualisedMetroListView}
解决的问题
我有一个 ListView:
<ListView ItemsSource="{Binding Students, IsAsync=True}">
<ListView.View>
<GridView>
<!--Some view things -->
</GridView>
</ListView.View>
</ListView>
这是我的 ViewModel 中的 Students
:
public ICollection<Student> Students
{
get
{
return _students;
}
set
{
_students = value;
OnPropertyChanged(() => this.Students);
}
}
我正在更新 Students
这样的一些任务(当用户在另一个 TreeView 中选择某些内容时我正在更新 SourceState
):
private State _sourceState;
public State SourceState
{
get
{
return _sourceState;
}
set
{
_sourceState = value;
OnSourceStateChanged();
OnPropertyChanged(() => this.SourceState);
}
}
private void OnSourceStateChanged()
{
Task updateStudentFromSourceStateTask = new Task(() =>
{
Students = _stateService.GetAllStudents(_sourceState);
});
updateStudentFromSourceStateTask.Start();
}
问题是每当发生变化时 Students
,UI 会冻结,直到 ListView 在新的 Students
中显示数据,我不知道如何让 ListView 刷新它的数据在后台线程中(我已经尝试 IsAsync=True
绑定但没有帮助)
你必须使用 "virtualizing"。
<ListView ItemsSource="{Binding Students}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.Resources>
<DataTemplate DataType="...">
<Grid>
...
<ListView/>
编辑:
也许您的问题在于来源类型以及您如何评估它。在我确实使用它的特定情况下,我的来源声明如下:
public IEnumerable<MyElementType> Elements
{
get { return (IEnumerable<MyElementType>)GetValue(ElementsProperty); }
set { SetValue(ElementsProperty, value); }
}
public static readonly DependencyProperty ElementsProperty =
DependencyProperty.Register("Elements", typeof(IEnumerable<MyElementType>), typeof(MyUiUserControlType), new UIPropertyMetadata(null));
当我将新过滤器应用于更大的块时,刷新发生了。当填充了所有必须实际可见的元素时,虚拟化将显示并允许立即开始使用您的列表。 您的问题可能出在使用 ICollection 上。但这已经取决于您的代码隐藏要求。
发生这种情况是因为我正在使用 Mahapps.Metro
并且它使 ListView 不使用 VirtualizingStackPanel
使用Style="{DynamicResource VirtualisedMetroListView}
解决的问题