StatusBar Text/Background updates don't work reliably

73 Views Asked by At

New to WPF, trying to figure out why my style changes for a StatusBar aren't working reliably. Code seems very straightforward:

private void OnTryP4Login(object sender, RoutedEventArgs e)
    {
        statusText.Text = string.Format("Connecting to {0} as '{1}'.",
                                         textBoxUri.Text, textBoxUser.Text); // <-- Doesn't work
        statusBar.Background = Brushes.Yellow; // <-- Doesn't work

        if (m_Manager.P4Login(textBoxUri.Text, textBoxUser.Text,
            textBoxClientSpec.Text, passwordBox.Password))
        {
            statusText.Text = "SUCCESS!"; // <-- Doesn't work
            statusBar.Background = Brushes.Green; // <-- Doesn't work
            Thread.Sleep(2000);
            this.Close();
        }
        else
        {
            statusBar.Background = Brushes.Red; // <-- THIS works
            statusText.Text = "Error connecting to Perforce server!"; // <-- THIS works
            buttonConnect.Visibility = Visibility.Collapsed;
            buttonOK.Visibility = Visibility.Visible;
        }
    }

Can somebody show me the error in my ways?

The relevant XAML:

<Window x:Class="WpfClient.P4LoginDialog"
    [...]
    Icon="feat.ico">
<DockPanel>
    <Grid DockPanel.Dock="Top" Height="158">
    [...]
    <Button x:Name="buttonConnect" Content="Connect" Click="OnTryP4Login" HorizontalAlignment="Left" Margin="274,130,0,-18" VerticalAlignment="Top" Width="75"/>

    </Grid>
    <StatusBar x:Name="statusBar" Height="25" Background="#888" DockPanel.Dock="Bottom" HorizontalAlignment="Stretch" >
        <TextBlock x:Name="statusText"
                Width="Auto" 
                Height="Auto" 
                Foreground="#fff" 
                Text="Connect to P4 for live integration" 
                HorizontalAlignment="Right"
                TextAlignment="Right"
                />
    </StatusBar>
</DockPanel>

Update: Thanks to these suggestions, I've been able to get the yellow text to work by doing the below. Still no luck with the green Success message. I got a whole bunch of reading on async to do to fully understand the execution flow and what the suggestions fully mean (last time I did anything like this, BackgroundWorkers were all the rage). I appreciate any other hints/explanations.

private async void OnTryP4Login(object sender, RoutedEventArgs e)
    {
        statusText.Text = string.Format("Connecting to {0} as '{1}'.",
                          textBoxUri.Text, textBoxUser.Text); // <-- Works now
        statusBar.Background = Brushes.DarkGoldenrod; // <-- Works now

        Task<bool> getConnection = m_Manager.P4Login(textBoxUri.Text, textBoxUser.Text, textBoxClientSpec.Text, passwordBox.Password);
        bool connection = await getConnection;

        if (connection)
        {
            statusText.Text = "SUCCESS!"; // <-- Still doesn't work (stays DarkGoldenrod)
            statusBar.Background = Brushes.Green; // <-- Still doesn't work (stays DarkGoldenrod)
            Thread.Sleep(2000);
            this.Close();
        }
        else
        {
            statusBar.Background = Brushes.Red; // <-- Still works
            statusText.Text = "Error connecting to Perforce server!"; // <-- Still works
            buttonConnect.Visibility = Visibility.Collapsed;
            buttonOK.Visibility = Visibility.Visible;
        }
    }

public async Task<bool> P4Login(string uri, string user, string clientSpec, string password)
    {
        await Task.Delay(100);
        return m_PI.Connect(uri, user, clientSpec, password);
    }
2

There are 2 best solutions below

0
On BEST ANSWER

In a single-threaded application, updates to controls are not going to have an effect until your event handler is done and the screen can be updated. Your Thread.Sleep(2000); blocks up the whole UI.

Make the method async and replace the sleep with await Task.Delay( 2000 ). This will allow the normal event loop to take over during those two seconds and the UI will get updated.

Edit: Your "Connecting" and yellow background still aren't going to work because (I assume) m_Manager.P4Login() is a synchronous function. If you can't make it async, do this right before calling it:

await Dispatcher.Yield( DispatcherPriority.ApplicationIdle );

This lets the event loop briefly take over and process all outstanding events (including your text and background color update) before your code continues.

0
On

I suppose that the whole code runs in the GUI thread and prevents WPF from updating the GUI.

Try this one:

private async void OnTryP4Login(object sender, RoutedEventArgs e)
{
    statusText.Text = string.Format("Connecting to {0} as '{1}'.",
                                     textBoxUri.Text, textBoxUser.Text); // <-- Doesn't work
    statusBar.Background = Brushes.Yellow; // <-- Doesn't work

    await Task.Delay(100);

    if (m_Manager.P4Login(textBoxUri.Text, textBoxUser.Text,
        textBoxClientSpec.Text, passwordBox.Password))
    {
        statusText.Text = "SUCCESS!"; // <-- Doesn't work
        statusBar.Background = Brushes.Green; // <-- Doesn't work
        Thread.Sleep(2000);
        this.Close();
    }
    else
    {
        statusBar.Background = Brushes.Red; // <-- THIS works
        statusText.Text = "Error connecting to Perforce server!"; // <-- THIS works
        buttonConnect.Visibility = Visibility.Collapsed;
        buttonOK.Visibility = Visibility.Visible;
    }
}

If that worked, you should probably run the actual login asyncron like that:

var loginSucceeded = await Task.Run(() => m_Manager.P4Login(textBoxUri.Text, textBoxUser.Text,
        textBoxClientSpec.Text, passwordBox.Password));

instead of using Task.Delay(100).