I have WPF application that works with DB. I have Connect to DB
button. When user clicks that button I want to show some progress animation while app is trying to connect to DB.
Here's my button and progress animation:
<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}" />
Here's my code for background work:
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);
}
For some reason when I click Connect button and I have wrong Server and DB my window freezes and I can't do anything until I got error message back. Do you have any ideas why is my LoginToDB()
doesn't go in background?
I am not sure how bw_RunWorkerConnectCompleted
ties in with everything but WPF has the ability to use the async/await pattern. This allows execution to continue on a different thread and the UI thread gets released so your windows does not freeze/hang. Here is your code refactored to make use of that pattern. Again, I have no idea about bw_RunWorkerConnectCompleted
so I removed it for now.
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;
}