加载数据时 ProgressBar 没有动画;使用 await 时加载不会等待
ProgressBar is not animating when data is loading; and loading doesn't wait when using await
在我的应用程序中,需要从数据库加载数据。在此期间,会显示一个启动画面。我想要一个进度条动画,所以将其添加到初始屏幕 xaml;
<ProgressBar Margin="5" DockPanel.Dock="Top" IsIndeterminate="True" />
这是有效的,但动画基本上在每次长时间检索数据时停止。我的负载数据看起来像这样;
private void loadData()
{
getData1FromDB();
displayData1_OnScreen();
getData2FromDB();
displayData2_OnScreen();
getData3FromDB();
displayData3_OnScreen();
}
所以当 getData1FromDB()
为 运行 时,进度条动画停止。认为问题可以这样解决;
private async void loadData()
{
await Task.Run(() => getData1FromDB());
displayData1_OnScreen();
getData2FromDB();
await Task.Run(() => displayData2_OnScreen());
getData3FromDB();
await Task.Run(() => displayData3_OnScreen());
}
这导致了另一个问题。虽然 getData1FromDB()
是 运行,但程序进入了 displayData1_OnScreen
。由于 getData1FromDB()
未完成,因此未显示任何数据。
如何在不重写整个加载过程的情况下解决这个问题?
编辑:根据评论;
没有别的是在调用 displayData1_OnScreen
老实说我也很惊讶。我唯一能想到的是 loadData()
是从构造函数中调用的。
老实说不,我不确定这就是问题所在。我正在尝试进行更多调试以查看@BradleyDotNET 提出后实际发生了什么。
关于getData1FromDB
的问题有点难打。基本上它会根据参数集构建一个 sqlCMD 字符串,然后像这样运行它;
DataTable dt = new DataTable();
string ConString = ConnectionString();
using (MySqlConnection con = new MySqlConnection(ConString))
{
MySqlDataAdapter da = new MySqlDataAdapter();
MySqlCommand cmd = new MySqlCommand(sqlCMD, con);
con.Open();
da.SelectCommand = cmd;
da.Fill(dt);
con.Close();
}
return dt;
您不应该使用 async void,事件处理程序除外:http://channel9.msdn.com/Series/Three-Essential-Tips-for-Async/Tip-1-Async-void-is-for-top-level-event-handlers-only
wpf中有动画,即使UI线程被阻塞,也可以在显卡上运行,但它们非常有限,我不会依赖它们
这段代码怎么样?
private async Task loadData()
{
await Task.Run(getData1FromDB);
displayData1_OnScreen();
await Task.Run(getData2FromDB);
displayData2_OnScreen();
await Task.Run(getData3FromDB);
displayData3_OnScreen();
}
就其价值而言,即使 loadData() 在构造函数中运行,以下重现尝试似乎也能满足您的要求 -
CS:
using System;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
namespace WpfAsyncAwaitProgressbar
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new TestViewModel();
}
}
public class TestViewModel : INotifyPropertyChanged
{
public TestViewModel()
{
Text1 = Text2 = Text3 = "Loading ...";
loadData();
}
public string Text1 { get; private set; }
public string Text2 { get; private set; }
public string Text3 { get; private set; }
public Visibility ProgressbarVisibility { get; private set; }
private async void loadData()
{
setBusyMode(true);
await Task.Run(() => getData1FromDB());
await Task.Run(() => getData2FromDB());
await Task.Run(() => getData3FromDB());
setBusyMode(false);
}
private void getData1FromDB()
{
Thread.Sleep(3000);
Text1 = "Data Result 1";
raisePropertyChanged(() => Text1);
}
private void getData2FromDB()
{
Thread.Sleep(2000);
Text2 = "Data Result 2";
raisePropertyChanged(() => Text2);
}
private void getData3FromDB()
{
Thread.Sleep(1000);
Text3 = "Data Result 3";
raisePropertyChanged(() => Text3);
}
private void setBusyMode(bool isBusy)
{
ProgressbarVisibility = isBusy ? Visibility.Visible : Visibility.Hidden;
raisePropertyChanged(() => ProgressbarVisibility);
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void raisePropertyChanged<TProperty>(Expression<Func<TProperty>> projection)
{
var memberExpression = (MemberExpression)projection.Body;
raisePropertyChanged(memberExpression.Member.Name);
}
private void raisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion INotifyPropertyChanged
}
}
XAML:
<Window x:Class="WpfAsyncAwaitProgressbar.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" SizeToContent="WidthAndHeight">
<StackPanel>
<TextBlock Text="{Binding Text1}"/>
<TextBlock Text="{Binding Text2}"/>
<TextBlock Text="{Binding Text3}"/>
<ProgressBar Visibility="{Binding ProgressbarVisibility}"
IsIndeterminate="True"
Height="10"/>
</StackPanel>
</Window>
在我的应用程序中,需要从数据库加载数据。在此期间,会显示一个启动画面。我想要一个进度条动画,所以将其添加到初始屏幕 xaml;
<ProgressBar Margin="5" DockPanel.Dock="Top" IsIndeterminate="True" />
这是有效的,但动画基本上在每次长时间检索数据时停止。我的负载数据看起来像这样;
private void loadData()
{
getData1FromDB();
displayData1_OnScreen();
getData2FromDB();
displayData2_OnScreen();
getData3FromDB();
displayData3_OnScreen();
}
所以当 getData1FromDB()
为 运行 时,进度条动画停止。认为问题可以这样解决;
private async void loadData()
{
await Task.Run(() => getData1FromDB());
displayData1_OnScreen();
getData2FromDB();
await Task.Run(() => displayData2_OnScreen());
getData3FromDB();
await Task.Run(() => displayData3_OnScreen());
}
这导致了另一个问题。虽然 getData1FromDB()
是 运行,但程序进入了 displayData1_OnScreen
。由于 getData1FromDB()
未完成,因此未显示任何数据。
如何在不重写整个加载过程的情况下解决这个问题?
编辑:根据评论;
没有别的是在调用 displayData1_OnScreen
老实说我也很惊讶。我唯一能想到的是 loadData()
是从构造函数中调用的。
老实说不,我不确定这就是问题所在。我正在尝试进行更多调试以查看@BradleyDotNET 提出后实际发生了什么。
关于getData1FromDB
的问题有点难打。基本上它会根据参数集构建一个 sqlCMD 字符串,然后像这样运行它;
DataTable dt = new DataTable();
string ConString = ConnectionString();
using (MySqlConnection con = new MySqlConnection(ConString))
{
MySqlDataAdapter da = new MySqlDataAdapter();
MySqlCommand cmd = new MySqlCommand(sqlCMD, con);
con.Open();
da.SelectCommand = cmd;
da.Fill(dt);
con.Close();
}
return dt;
您不应该使用 async void,事件处理程序除外:http://channel9.msdn.com/Series/Three-Essential-Tips-for-Async/Tip-1-Async-void-is-for-top-level-event-handlers-only
wpf中有动画,即使UI线程被阻塞,也可以在显卡上运行,但它们非常有限,我不会依赖它们
这段代码怎么样?
private async Task loadData()
{
await Task.Run(getData1FromDB);
displayData1_OnScreen();
await Task.Run(getData2FromDB);
displayData2_OnScreen();
await Task.Run(getData3FromDB);
displayData3_OnScreen();
}
就其价值而言,即使 loadData() 在构造函数中运行,以下重现尝试似乎也能满足您的要求 -
CS:
using System;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
namespace WpfAsyncAwaitProgressbar
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new TestViewModel();
}
}
public class TestViewModel : INotifyPropertyChanged
{
public TestViewModel()
{
Text1 = Text2 = Text3 = "Loading ...";
loadData();
}
public string Text1 { get; private set; }
public string Text2 { get; private set; }
public string Text3 { get; private set; }
public Visibility ProgressbarVisibility { get; private set; }
private async void loadData()
{
setBusyMode(true);
await Task.Run(() => getData1FromDB());
await Task.Run(() => getData2FromDB());
await Task.Run(() => getData3FromDB());
setBusyMode(false);
}
private void getData1FromDB()
{
Thread.Sleep(3000);
Text1 = "Data Result 1";
raisePropertyChanged(() => Text1);
}
private void getData2FromDB()
{
Thread.Sleep(2000);
Text2 = "Data Result 2";
raisePropertyChanged(() => Text2);
}
private void getData3FromDB()
{
Thread.Sleep(1000);
Text3 = "Data Result 3";
raisePropertyChanged(() => Text3);
}
private void setBusyMode(bool isBusy)
{
ProgressbarVisibility = isBusy ? Visibility.Visible : Visibility.Hidden;
raisePropertyChanged(() => ProgressbarVisibility);
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void raisePropertyChanged<TProperty>(Expression<Func<TProperty>> projection)
{
var memberExpression = (MemberExpression)projection.Body;
raisePropertyChanged(memberExpression.Member.Name);
}
private void raisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion INotifyPropertyChanged
}
}
XAML:
<Window x:Class="WpfAsyncAwaitProgressbar.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" SizeToContent="WidthAndHeight">
<StackPanel>
<TextBlock Text="{Binding Text1}"/>
<TextBlock Text="{Binding Text2}"/>
<TextBlock Text="{Binding Text3}"/>
<ProgressBar Visibility="{Binding ProgressbarVisibility}"
IsIndeterminate="True"
Height="10"/>
</StackPanel>
</Window>