7
votes

I'm trying to databind the value property of a ProgressBar in WPF. I have a button set up to increment the databound int property for the Value of the ProgressBar. When I push the button it should make the ProgressBar's Value count up from 1 to 100. However... it doesn't seem to be working and I am not sure what I am doing wrong. Here is my XAML...

<Window x:Class="ProgressBarExample2.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="250" Width="400" Background="WhiteSmoke">
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
    <Button Name="goButton" Height="30" Width="50" Margin="0,10,0,50" Click="goButton_Click">GO!</Button>
    <ProgressBar Name="progressBar" Width="300" Height="30" Value="{Binding Percent, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>

and here is my code behind...

public partial class MainWindow : Window, INotifyPropertyChanged
{
    #region INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;
    protected void NotifyPropertyChange(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    #endregion

    private int percent = 0;
    public int Percent
    {
        get { return this.percent; }
        set 
        {
            this.percent = value;
            NotifyPropertyChange("Percent");
        }
    }

    public MainWindow()
    {
        InitializeComponent();
    }


    private void goButton_Click(object sender, RoutedEventArgs e)
    {
        for (Percent = 0; Percent <= 100; Percent++)
        {
            Thread.Sleep(50);
        }
    }
}
3
you can perhaps try adding a timer- on button click start the timer. In timer callback increment the Percent- with proper data-binding this should just work. Timer callbacks are in background thread so your main thread is free to update the progress barSarang

3 Answers

7
votes

The Thread.Sleep is blocking the UI thread and stopping the animation of the progress bar.

You can use the following to pause execution without blocking the UI thread. Replace your Thread.Sleep(50) call with Wait(50)

/// <summary>
/// Stop execution for a specific amount of time without blocking the UI
/// </summary>
/// <param name="interval">The time to wait in milliseconds</param>
public static void Wait(int interval)
{
    ExecuteWait(() => Thread.Sleep(interval));
}

public static void ExecuteWait(Action action)
{
    var waitFrame = new DispatcherFrame();

    // Use callback to "pop" dispatcher frame
    IAsyncResult op = action.BeginInvoke(dummy => waitFrame.Continue = false, null);

    // this method will block here but window messages are pumped
    Dispatcher.PushFrame(waitFrame);

    // this method may throw if the action threw. caller's responsibility to handle.
    action.EndInvoke(op);
}
1
votes

There are other solutions without the databinding. You can declare a delegate

private delegate void UpdateProgressBarDelegate(System.Windows.DependencyProperty dp, Object value);

and use it in the click event of the button

private void goButton_Click(object sender, RoutedEventArgs e)
        {
             //Configure the ProgressBar
            progressBar.Minimum = 0;
            progressBar.Maximum = 100;
            progressBar.Value = 0;

            //Stores the value of the ProgressBar
            double value = 0;

            //Create a new instance of our ProgressBar Delegate that points
            //  to the ProgressBar's SetValue method.
            UpdateProgressBarDelegate updatePbDelegate = new UpdateProgressBarDelegate(progressBar.SetValue);

            //Tight Loop:  Loop until the ProgressBar.Value reaches the max
            do
            {
                value += 1;

                /*Update the Value of the ProgressBar:
                  1)  Pass the "updatePbDelegate" delegate that points to the ProgressBar1.SetValue method
                  2)  Set the DispatcherPriority to "Background"
                  3)  Pass an Object() Array containing the property to update (ProgressBar.ValueProperty) and the new value */
                Dispatcher.Invoke(updatePbDelegate,
                    System.Windows.Threading.DispatcherPriority.Background,
                    new object[] { ProgressBar.ValueProperty, value });

            }
            while (progressBar.Value != progressBar.Maximum);
        }
1
votes

There is no code (posted) that sets the DataContext of the Window (or StackPanel).

To be sure about the cause, watch the Output window for Binding errors.


And in addition,

private void goButton_Click(object sender, RoutedEventArgs e)
{
    for (Percent = 0; Percent <= 100; Percent++)
    {
        Thread.Sleep(50);
    }
 }

This blocks message processing so your app will be 'unresponsive' for 5 seconds. No input processing and no screen updates will take place. A busy loop is simply not good in an event-driven GUI.

Move this code to a Backgroundworker or use a Timer.