BackgroundWorker.RunWorkerAsync()不会后台

时间:2016-03-28 15:04:09

标签: c# wpf .net-4.5 backgroundworker sqlconnection

我有与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()为什么不在后台?

2 个答案:

答案 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;
}