我有与DB一起使用的WPF应用程序。我有Connect to DB
按钮。当用户点击该按钮时,我想在应用程序尝试连接到数据库时显示一些进度动画。
这是我的按钮和进度动画:
<Button Grid.Column="2" Grid.ColumnSpan="2" Grid.Row="2" Name="btnLogin" HorizontalAlignment="Center" VerticalAlignment="Center" Click="btnLogin_Click" Margin="5">
<StackPanel Orientation="Horizontal">
<Image x:Name="btnLoginIcon" Source="pack://application:,,,/Content/Images/Connect_Icon.png" Width="20" Height="20"/>
<TextBlock x:Name="btnLoginText" Text="Connect" VerticalAlignment="Center" />
</StackPanel>
</Button>
<mui:ModernProgressRing Grid.Column="2" Grid.ColumnSpan="2" Grid.Row="2" x:Name="ConnectProgressRing" Width="40" Height="40" Visibility="Hidden" IsActive="True" Style="{StaticResource ThreeBounceProgressRingStyle}" />
这是我的背景工作代码:
private void btnLogin_Click(object sender, RoutedEventArgs e)
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWorkConnect);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerConnectCompleted);
ConnectProgressRing.Visibility = Visibility.Visible;
btnLogin.Visibility = Visibility.Hidden;
bw.RunWorkerAsync();
}
private void LoginToDB()
{
string ServerPath = Server.Text;
string Database = Schema.Text;
dbStr = "Server=" + ServerPath + ";Database=" + Database + ";Trusted_Connection=True;";
try
{
using (SqlConnection conn = new SqlConnection(dbStr))
{
conn.Open();
conn.Close();
}
}
catch (Exception ex)
{
if (ex.Message != null)
{
MessageBoxButton btn = MessageBoxButton.OK;
ModernDialog.ShowMessage(ex.Message, "Failure to connect", btn);
}
}
}
void bw_DoWorkConnect(object sender, DoWorkEventArgs e)
{
Dispatcher.Invoke(new Action(() => {
LoginToDB();
}), DispatcherPriority.ContextIdle);
}
void bw_RunWorkerConnectCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Dispatcher.Invoke(new Action(() =>
{
ConnectProgressRing.Visibility = Visibility.Hidden;
btnLogin.Visibility = Visibility.Visible;
}), DispatcherPriority.ContextIdle);
}
出于某种原因,当我单击“连接”按钮并且我的服务器和数据库错误时,我的窗口冻结,在收到错误消息之前我无法执行任何操作。您有什么想法我的LoginToDB()
为什么不在后台?
答案 0 :(得分:1)
我不确定bw_RunWorkerConnectCompleted
如何与所有内容联系,但WPF能够使用async / await模式。这允许执行在不同的线程上继续,并且UI线程被释放,因此您的窗口不会冻结/挂起。这是您重构的代码,以利用该模式。我再也不知道bw_RunWorkerConnectCompleted
所以我暂时删除了它。
private async Task LoginToDBAsync() // notice the async keyword in the method signature
{
string ServerPath = Server.Text;
string Database = Schema.Text;
dbStr = "Server=" + ServerPath + ";Database=" + Database + ";Trusted_Connection=True;";
try
{
using (SqlConnection conn = new SqlConnection(dbStr))
{
await conn.OpenAsync(); // will release the thread back to the WPF app and connection continues on new thread until it completes
conn.Close();
}
}
catch (Exception ex)
{
if (ex.Message != null)
{
MessageBoxButton btn = MessageBoxButton.OK;
ModernDialog.ShowMessage(ex.Message, "Failure to connect", btn);
}
}
}
// previous name was async void bw_DoWorkConnect(object sender, DoWorkEventArgs e)
async void btnLogin_Click(object sender, RoutedEventArgs e) // notice the async keyword in the method signature. Because its a WPF event callback it returns void and not Task like the above method
{
ConnectProgressRing.Visibility = Visibility.Visible;
btnLogin.Visibility = Visibility.Hidden;
await LoginToDBAsync(); // await db call
// ui code to be executed after the previous call completes.
// the continuation is executed on the UI thread
ConnectProgressRing.Visibility = Visibility.Hidden;
btnLogin.Visibility = Visibility.Visible;
}
答案 1 :(得分:1)
这是因为您的异步代码调用了对调度程序线程的调用
Dispatcher.Invoke(new Action(() => {
LoginToDB();
}), DispatcherPriority.ContextIdle);
调度程序将使用与UI相同的线程,您的UI将冻结。你需要删除Dispatcher.Invoke以允许你的代码异步运行,虽然我猜你已经把它放在那里因为LoginToDB引用了一些UI元素?
void bw_DoWorkConnect(object sender, DoWorkEventArgs e)
{
LoginToDB();
}
如果要更新UI,则不要在DoWork事件处理程序中捕获异常,并让RunWorkerCompleted事件处理程序检查RunWorkerCompletedEventArgs的错误以更新按钮和显示消息等。
private void LoginToDB()
{
string ServerPath = Server.Text;
string Database = Schema.Text;
dbStr = "Server=" + ServerPath + ";Database=" + Database + ";Trusted_Connection=True;";
using (SqlConnection conn = new SqlConnection(dbStr))
{
conn.Open();
conn.Close();
}
}
在RunWorkerCompleted事件处理程序中不需要Dispatcher.Invoke,因为该方法将在UI线程上执行。
void bw_RunWorkerConnectCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
MessageBoxButton btn = MessageBoxButton.OK;
ModernDialog.ShowMessage(e.Error.Message, "Failure to connect", btn);
return;
}
ConnectProgressRing.Visibility = Visibility.Hidden;
btnLogin.Visibility = Visibility.Visible;
}