在我的 WPF 项目中,我需要将大量 SQL 数据传输到数据网格。自然是等的时间长了。我使用了 MVVM 模式。
为了使 UI 更具响应性,我使用了后台工作者。但传输完成后,屏幕再次冻结,并且 UI 更新。我希望在加载数据时更新 UI。有关于这个主题的主题,我理解这是关于使用
ProgressChanged
事件,但我无法弄清楚。所以我准备了一个简单的项目来询问。
这是我的 XAML 标记:
<Window x:Class="BackRoundWorkerTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BackRoundWorkerTest"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<local:BackroundWorkerVM x:Key="vm"/>
</Window.Resources>
<Grid DataContext="{StaticResource vm}">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="40"/>
</Grid.RowDefinitions>
<DataGrid ItemsSource="{Binding Persons, UpdateSourceTrigger=PropertyChanged}"
AutoGenerateColumns="True">
</DataGrid>
<Button Grid.Row="1"
Command="{Binding DoWorkCommand}"/>
</Grid>
</Window>
这是视图模型:
public class BackgroundWorkerVM : INotifyPropertyChanged
{
private BackgroundWorker worker;
// it calls the PopulateList() method.
public DoWorkCommand DoWorkCommand { get; set; }
public BackgroundWorkerVM()
{
// it calls the PopulateList() method.
DoWorkCommand = new DoWorkCommand(this);
worker = new BackgroundWorker();
worker.DoWork += Worker_DoWork;
worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
worker.WorkerReportsProgress = true;
}
private ObservableCollection<Person> persons = new ObservableCollection<Person>();
public ObservableCollection<Person> Persons
{
get { return persons; }
set
{
persons = value;
OnPropertyChanged();
}
}
public void PopulateList()
{
worker.RunWorkerAsync();
}
private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
}
private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
}
private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
App.Current.Dispatcher.Invoke((Action)delegate
{
for (int i = 0; i < 100; i++)
{
Persons.Add(new Person { PersonName = "john", Surname = "Smith", ResultChanged = true });
Thread.Sleep(100);
Persons.Add(new Person { PersonName = "jack", Surname = "Smith", ResultChanged = false });
}
});
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
这是模型类:
public class Person
{
public string PersonName { get; set; }
public string Surname { get; set; }
public bool ResultChanged { get; set; }
}
您仍然在主应用程序线程上进行所有等待
App.Current.Dispatcher.Invoke(...)
。因此,当 Invoke(...)
中调用的方法正在运行时,主应用程序线程无法更新 GUI。
可能的选择之一是在池线程中准备集合,然后替换整个集合:
public class BackgroundWorkerVM : INotifyPropertyChanged
{
public RelayCommand DoWorkCommand { get; }
public BackgroundWorkerVM()
{
DoWorkCommand = new(PopulateList);
}
private ObservableCollection<Person> _persons = new ObservableCollection<Person>();
public ObservableCollection<Person> Persons
{
get { return _persons; }
set
{
_persons = value;
OnPropertyChanged();
}
}
public async void PopulateList()
{
var list = await Task.Run(Worker_DoWork);
Persons = list;
}
private ObservableCollection<Person> Worker_DoWork()
{
ObservableCollection<Person> list = new ObservableCollection<Person>();
for (int i = 0; i < 100; i++)
{
list.Add(new Person { PersonName = "john", Surname = "Smith", ResultChanged = true });
Thread.Sleep(100);
list.Add(new Person { PersonName = "jack", Surname = "Smith", ResultChanged = false });
}
return list;
}
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string? name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}