locked
Problems with ProgressBar Binding when Value is non zero RRS feed

  • Question

  • I'm developing a Windows 8.1 Store App, and am using the ProgressBar control to display download progress by binding the Value and Maximum properties to a model object, and updating the Value during download.

    This works fine, as long as the ProgressBar is initially bound to the model when the Value is zero (nothing downloaded). But if  the ProgressBar is initially bound to a model object with a Value more than zero, the ProgressBar does not update when the Value is updated.

    I've confirmed this behaviour in a test app. I've copied the code below.

    Is there something obvious I should or shouldn't be doing?

    Thanks.

    XAML:

    <Page
        x:Class="ProgressBarApp.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:ProgressBarApp"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        d:DataContext="{d:DesignInstance local:ProgressObj, IsDesignTimeCreatable=True}"
        
        mc:Ignorable="d">
    
        <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
            <ProgressBar Height="25" Width="300"
                                     Value="{Binding Progress}"
                                     Maximum="{Binding Max}"
                                     Visibility="Visible"/>
            <Button Content="Progress" HorizontalAlignment="Left" Margin="424,431,0,0" VerticalAlignment="Top" Width="120" Click="ProgressClick"/>
        </Grid>
    </Page>

    Page Code and Model code:

    public sealed partial class MainPage
    {
        private bool _run;
    
        public MainPage()
        {
            //This works OK
            //Progress = new ProgressObj {Max = 1024, Progress = 0};
                
            //This fails to update when Progress is updated
            Progress = new ProgressObj { Max = 1024, Progress = 100 };
    
            InitializeComponent();
            DataContext = Progress;
        }
    
        public ProgressObj Progress
        {
            get;
            private set;
        }
    
    
        private async void ProgressClick(object sender, RoutedEventArgs e)
        {
            //Progess
            _run = !_run;
            if (_run)
                await RunProgress();
        }
    
        private async Task RunProgress()
        {
            do
            {
                //Increment by a random amount within a range
                int incr = new Random().Next(1, Progress.Max/100);
                Progress.Progress += incr;
    
                //Add a wait in so we can see progress
                await Task.Run(
                    async () => await Task.Delay(TimeSpan.FromMilliseconds(10)));
                    
                Debug.WriteLine("Update Progress:{0}", Progress.Progress);
            } while (Progress.Max > Progress.Progress && _run);
        }
    }
    
    public class ProgressObj : INotifyPropertyChanged
    {
        private int _max;
        private int _progress;
    
        public int Progress
        {
            get { return _progress; }
            set
            {
                _progress = value;
                OnPropertyChanged("Progress");
            }
        }
    
        public int Max
        {
            get { return _max; }
            set
            {
                _max = value;
                OnPropertyChanged("Max");
            }
        }
    
        protected void OnPropertyChanged(string propertyName = null)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    }

    Friday, November 29, 2013 2:14 AM

Answers

  • I understand what you are saying, but oddly though it seems this does not work like we are thinking it should where coercion is acting for us.  We can force it, by assigning the value directly like mentioned.. or extend ProgressBar with a dependency property that does handle it with a PropertyChangedCallback so that the binding source does properly notify the target (value) for us.

    • Marked as answer by Sean Clifford Saturday, January 4, 2014 11:07 PM
    Friday, November 29, 2013 10:39 AM

All replies

  • I believe if you are going to be changing the value, you need to set it to determinate by flagging IsIndeterminate = "false";

    Friday, November 29, 2013 3:46 AM
  • Thanks for your reply tsw, but the default value for IsIndeterminate is already false, so adding it to the XAML doesn't change the behaviour of the ProgressBar.
    Friday, November 29, 2013 3:56 AM
  • ok, so I setup your sample and found that I had to subscribe to the event handler and assign the updated property to the value of the instanced progressbar.

    give the progress bar a name:

    <ProgressBar x:Name="myProgress" Height="25" Width="300"
                                     Value="{Binding Progress}"
                                     Maximum="{Binding Max}"
                                     Visibility="Visible"/>

    and in your code-behind, add a handler in the constructor:

    Progress.PropertyChanged += Progress_PropertyChanged;

    and method:

            void Progress_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
            {
                myProgress.Value = Progress.Progress;
            }

    ... seems to be working for me now.

    Friday, November 29, 2013 5:53 AM
  • for me though, I think I would have looked at creating a template control based on progressbar, and added a dependency property with a PropertyChangedCallback(OnPropertyChanged) handler.  For me, it is a little easier and I don't have to remember to deal with unsubscribing from the handler if I must... its just built in that way.

    You can still bind to your data context and setup a INotifyPropertyChanged class to deal with data updates... especially if the context is coming from say... SQLite or some other source.

    Friday, November 29, 2013 6:02 AM
  • Thanks for your replies.

    I'm not able to test your approach until I get back to work on Monday, but isn't it just a workaround for the Binding on the Value property breaking for some reason?

    Friday, November 29, 2013 8:35 AM
  • I understand what you are saying, but oddly though it seems this does not work like we are thinking it should where coercion is acting for us.  We can force it, by assigning the value directly like mentioned.. or extend ProgressBar with a dependency property that does handle it with a PropertyChangedCallback so that the binding source does properly notify the target (value) for us.

    • Marked as answer by Sean Clifford Saturday, January 4, 2014 11:07 PM
    Friday, November 29, 2013 10:39 AM
  • I'm still hoping that this behaviour isn't a bug in the ProgressBar control that I'll need to work around - and that I'm just missing some obscure setting or configuration.

    If I do end up needing to work around the Value Binding - I'll likely use your approach of handling the binding manually (if I do, I'll accept your answer), but for now I'll hold out for a better answer.

    Sunday, December 1, 2013 8:37 PM
  • Same problem here.  Nothing can change the values after the initial binding.

    Hong

    Monday, December 30, 2013 3:23 AM
  • Yes, changing the values while binding appears to be broken.
    I ended up using tsw's suggestion of removing the binding and changing the progress bar value manually through the PropertyChanged events.
    Saturday, January 4, 2014 11:11 PM