locked
Can't get ProgressBar to work RRS feed

  • Question

  • I have a Test class which contains various static algorithms (static procedures/methods) one of which is called someAlgorithm().  Inside this algorithm I would like to declare a simple progress bar and update that progress bar as the algorithm progresses through its computations.  Seems simple enough, but I can't get the progress bar to work:

    My Test.cs file looks as follows:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Controls;      //ProgressBar

    namespace Mottle
    {
        class Test
        {
            private delegate void UpdateProgressBarDelegate(System.Windows.DependencyProperty dp, Object value);

            public static void someAlgorithm()
            {
                MottleProgressBar myProgressBar = new MottleProgressBar();
                UpdateProgressBarDelegate updatePbDelegate = new UpdateProgressBarDelegate(ApennineProgressBar.SetValue);
                myProgressBar.Show();

                for (double i = 0; i < 100; ++i)
                {
                    myProgressBar.Dispatcher.Invoke(updatePbDelegate,
                            System.Windows.Threading.DispatcherPriority.Background,
                            new object[] { ProgressBar.ValueProperty, i });
                }
            }
        }
    }

    and my MottleProgressBar.xaml file looks as follows:

    <Window x:Class="Mottle.MottleProgressBar"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:Mottle"
            mc:Ignorable="d"
            Title="Calculating..." Height="80" Width="300" ShowInTaskbar="False" ResizeMode="NoResize">
        <Grid Margin="20,10,20,12">
            <ProgressBar Minimum="0" Maximum="100" Name="Status" />
        </Grid>
    </Window>

    Why doesn't the progress bar update?


    MarcinMR

    Friday, September 25, 2015 4:03 PM

Answers

  • Looks to me like there are several problems there.

    Your progressbar you are changing is a control you just new up in your test class rather than the one in the window. So you're never possibly going to be able to see a change.

    This progressbar:

                MottleProgressBar myProgressBar = new MottleProgressBar();
                 UpdateProgressBarDelegate updatePbDelegate = new UpdateProgressBarDelegate(ApennineProgressBar.SetValue);
                 myProgressBar.Show();

    Is not this one:

            <ProgressBar Minimum="0" Maximum="100" Name="Status" />
    

    It's just an object you instantiated.

    You didn't .Add it to any control in a window so you can't see it.

    .

    Even if you tried to use the progressbar in the window then it's a private member so you wouldn't be able to mess with it from some other class.

    If you resolved those then you're in a tight loop there so all you'll actually see is it jumps straight to 100.

    You will also find that this is a problematic approach.

    I recommend you reconsider your linear progressbar and switch to using a spinner/throbber which shows is indeterminate progress.

    That way you will avoid the issue where your progress jumps about.

    That'll be your next problem.

    You can see such a control in the sample for this:

    http://social.technet.microsoft.com/wiki/contents/articles/28209.wpf-entity-framework-mvvm-walk-through-1.aspx

    Notice that the property which controls showing ( and hence the spinning ) of the throbber (ThrobberVisible) is changed on the UI thread. Change it on a background thread and you would need dispatcher invokeasync to get back to the UI thread. Change notification will fail if you don't.

            protected async override void GetData()
            {
                db.Configuration.LazyLoadingEnabled= true;
                ThrobberVisible = Visibility.Visible;
                ObservableCollection<CustomerVM> _customers = new ObservableCollection<CustomerVM>();
                var customers = await (from c in db.Customers
                                        orderby c.CustomerName
                                        select c).ToListAsync();
                foreach (Customer cust in customers)
                {
                    _customers.Add(new CustomerVM { IsNew = false, TheEntity = cust });
                }
                Customers = _customers;
                RaisePropertyChanged("Customers");
                ThrobberVisible = Visibility.Collapsed;
            }


    Saturday, September 26, 2015 3:37 PM
  • You forgot to post the implementation of the ApennineProgressBar.SetValue method. It is this method that doesn't work if the ProgressBar doesn't get updated. This code works as expected:

    class Test
        {
            public static void someAlgorithm()
            {
                MottleProgressBar myProgressBar = new MottleProgressBar();
                myProgressBar.Show();
    
                for (double i = 0; i < 100; ++i)
                {
                    myProgressBar.Dispatcher.Invoke(new Action(()=> { myProgressBar.Status.Value = i; }),
                            System.Windows.Threading.DispatcherPriority.Background);
                }
            }
        }
    

    By the way I cannot see any previous replies in this thread - there seems to be some sync issue going on: https://social.msdn.microsoft.com/Forums/en-US/85683cb2-19c6-468a-b897-939ae8692454/threads-dont-show-all-replies?forum=reportabug - so please forgive me if I am unknowingly repeating something here.

    Hope that helps.

    Please remember to close your threads by marking helpful posts as answer and then start a new thread if you have a new question. Please don't ask several questions in the same thread.

    Sunday, September 27, 2015 9:24 AM

All replies

  • Looks to me like there are several problems there.

    Your progressbar you are changing is a control you just new up in your test class rather than the one in the window. So you're never possibly going to be able to see a change.

    This progressbar:

                MottleProgressBar myProgressBar = new MottleProgressBar();
                 UpdateProgressBarDelegate updatePbDelegate = new UpdateProgressBarDelegate(ApennineProgressBar.SetValue);
                 myProgressBar.Show();

    Is not this one:

            <ProgressBar Minimum="0" Maximum="100" Name="Status" />
    

    It's just an object you instantiated.

    You didn't .Add it to any control in a window so you can't see it.

    .

    Even if you tried to use the progressbar in the window then it's a private member so you wouldn't be able to mess with it from some other class.

    If you resolved those then you're in a tight loop there so all you'll actually see is it jumps straight to 100.

    You will also find that this is a problematic approach.

    I recommend you reconsider your linear progressbar and switch to using a spinner/throbber which shows is indeterminate progress.

    That way you will avoid the issue where your progress jumps about.

    That'll be your next problem.

    You can see such a control in the sample for this:

    http://social.technet.microsoft.com/wiki/contents/articles/28209.wpf-entity-framework-mvvm-walk-through-1.aspx

    Notice that the property which controls showing ( and hence the spinning ) of the throbber (ThrobberVisible) is changed on the UI thread. Change it on a background thread and you would need dispatcher invokeasync to get back to the UI thread. Change notification will fail if you don't.

            protected async override void GetData()
            {
                db.Configuration.LazyLoadingEnabled= true;
                ThrobberVisible = Visibility.Visible;
                ObservableCollection<CustomerVM> _customers = new ObservableCollection<CustomerVM>();
                var customers = await (from c in db.Customers
                                        orderby c.CustomerName
                                        select c).ToListAsync();
                foreach (Customer cust in customers)
                {
                    _customers.Add(new CustomerVM { IsNew = false, TheEntity = cust });
                }
                Customers = _customers;
                RaisePropertyChanged("Customers");
                ThrobberVisible = Visibility.Collapsed;
            }


    Saturday, September 26, 2015 3:37 PM
  • You forgot to post the implementation of the ApennineProgressBar.SetValue method. It is this method that doesn't work if the ProgressBar doesn't get updated. This code works as expected:

    class Test
        {
            public static void someAlgorithm()
            {
                MottleProgressBar myProgressBar = new MottleProgressBar();
                myProgressBar.Show();
    
                for (double i = 0; i < 100; ++i)
                {
                    myProgressBar.Dispatcher.Invoke(new Action(()=> { myProgressBar.Status.Value = i; }),
                            System.Windows.Threading.DispatcherPriority.Background);
                }
            }
        }
    

    By the way I cannot see any previous replies in this thread - there seems to be some sync issue going on: https://social.msdn.microsoft.com/Forums/en-US/85683cb2-19c6-468a-b897-939ae8692454/threads-dont-show-all-replies?forum=reportabug - so please forgive me if I am unknowingly repeating something here.

    Hope that helps.

    Please remember to close your threads by marking helpful posts as answer and then start a new thread if you have a new question. Please don't ask several questions in the same thread.

    Sunday, September 27, 2015 9:24 AM
  • Andy, thanks for the reply, but I got lost in you last paragraph

    "Notice that the property which controls showing ( and hence the spinning ) of the throbber (ThrobberVisible) is changed on the UI thread. Change it on a background thread and you would need dispatcher invokeasync to get back to the UI thread. Change notification will fail if you don't."

    I'm not that far along in my C#, wpf yet, however I do have to complain: I spent two days looking at the implementation of the progressbar, both in books and online, and nowhere did it go into the concept that you mention (The one I quoted above).  Where the heck are you supposed to read up on these things?

    As for Magnus, I tried your solution, and it worked for me.  I hope I can get some hints about where to read up on the issues Andy mentioned, but for now, your advice did the trick.  I attach my files for those who will have a similar issue in the future:

    Static Test.someAlgorithm() method which displays a progress bar to show progress of algorithm:

                             

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Controls;      //ProgressBar
    using System.Threading;
    using System.ComponentModel;


    namespace Mottle
    {
        class Test
        {
            private delegate void UpdateProgressBarDelegate(System.Windows.DependencyProperty dp, Object value);

            public static void someAlgorithm()
            {
                MottleProgressBar myProgressBar = new MottleProgressBar();
                UpdateProgressBarDelegate updatePbDelegate = new UpdateProgressBarDelegate(myProgressBar.Status.SetValue);
                myProgressBar.Show();

                for (double i = 0; i < 100; ++i)
                {


                    //myProgressBar.Status.Dispatcher.BeginInvoke(updatePbDelegate,
                    //System.Windows.Threading.DispatcherPriority.Background,
                    //new object[] { ProgressBar.ValueProperty, i });
                    Thread.Sleep(50);
                    myProgressBar.Dispatcher.Invoke(new Action(()=> { myProgressBar.Status.Value = i; }),
                        System.Windows.Threading.DispatcherPriority.Background);
                }
            }

            public static void doWork(object sender, DoWorkEventArgs e)
            {
                someAlgorithm();
            }
        }

    and my MottleProgressBar is implemented as follows: 

    <Window x:Class="Mottle.MottleProgressBar"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:Mottle"
            mc:Ignorable="d"
            Title="Calculating..." Height="80" Width="300" ShowInTaskbar="False" ResizeMode="NoResize">
        <Grid Margin="20,10,20,12">
            <ProgressBar Minimum="0" Maximum="100" Name="Status" IsIndeterminate="False"/>
        </Grid>
    </Window>

    thanks to the both of you.


    MarcinMR

    Monday, September 28, 2015 3:56 PM
  • Just a quick question, the following line:

    myProgressBar.Dispatcher.Invoke(new Action(()=> { myProgressBar.Status.Value = i; }),
                        System.Windows.Threading.DispatcherPriority.Background);

    obtains the core dispatcher (what exactly this is, I haven't been able to find) for my window "myProgressBar" and executes on the thread the dispatcher is associated with (the UI thread I assume?) the specified action (updating my progressbar Status' value).  

    But why does this work? No Asynchronous thread exists to update the UI? Why does this update the UI as my algorithm progresses while simply calling myProgressBar.Status.Value does not?

    Thanks


    MarcinMR


    • Edited by MarcinMR Monday, September 28, 2015 4:31 PM
    Monday, September 28, 2015 4:30 PM
  • A throbber is a fairly common term for one of those wait spinning things which are fairly commonly used in apps to denote they are busy.

    https://en.wikipedia.org/wiki/Throbber

    The throbber in that linked solution is a usercontrol rather than a standard control. There is no documentation you could find on it since I wrote it. I rather assumed you'd download the solution and look at it.

    The solution is built to the mvvm pattern.

    Almost everyone uses mvvm for xaml orientated development.

    .

    ThrobberVisible is a property of a base viewmodel (CrudVMBase) it is bound to the Visibility property of Throbber:

    <support:Throbber x:Name="Throbber" Visibility="{Binding ThrobberVisible}"/>

    When it is changed, to true then the thing containing the arc which will be spun round is given focus.

            public Throbber()
            {
                InitializeComponent();
                this.IsVisibleChanged += new DependencyPropertyChangedEventHandler(VisibleChanged);
            }
    
            void VisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
            {
                if ((bool)e.NewValue == true)
                {
                    Dispatcher.BeginInvoke(
                    DispatcherPriority.ContextIdle,
                    new Action(delegate()
                    {
                        this.ArcContainer.Focus();
                    }));
                }
            }
        }

    This makes sure no other control has focus but the main effect is it starts the storyboard which spins the arc.

    	<UserControl.Triggers>
            <EventTrigger RoutedEvent="UIElement.GotFocus" SourceName="ArcContainer">
                <BeginStoryboard Storyboard="{StaticResource SpinIt}"/>
            </EventTrigger>
        </UserControl.Triggers>

    The reason for the popularity of a wait control which doesn't specify the proportion of progress so far is the problems you have if you indicate percentages.

    What happens is you end up with a progress which jumps to some setting as you do one thing, sits forever.... then jumps to complete. Or some variation.

    That's because it's often impossible to predict what actual time some process will take and you don't have some convenient 1% now... 2%... 3% point. You have a chunk of code then some other chunk of code.


    Monday, September 28, 2015 5:09 PM
  • Don't use Invoke it's blocking.

    Use InvokeAsync ( or begininvoke ).

    Not that this is very important at this stage but you may as well get used to best practice now.

    .

    When I google "WPF Dispatcher" top of my hits is:

    https://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396

    To expand on that.

    The dispatcher is essentially a route into what the ui thread is to do.

    Like a sort of conveyor belt where you throw a parcel on the end, the parcel being an instruction to do something.

    The parcels on the belt are passed to a processor which does them.

    Except you can tell it to do this parcel right now, next or when it has nothing else to do ( context idle ).

    The latter is often best.


    Monday, September 28, 2015 5:20 PM
  • >>Just a quick question, the following line:...obtains the core dispatcher (what exactly this is, I haven't been able to find) for my window "myProgressBar" and executes on the thread the dispatcher is associated with (the UI thread I assume?) the specified action (updating my progressbar Status' value). 

    Yes, it executes the action delegate on the dispatcher thread. You are required to use the dispatcher to add the delegate to the dispatcher queue if you are running the someAlgorithm() method from a background thread because you can only access UI elements on the same thread on which they were originally created (and this is the UI/dispatcher thread).

    If you are running the someAlgorithm() on the UI thread this code should work as well, without the use of the Dispatcher:

    class Test
        {
            public static void someAlgorithm()
            {
                MottleProgressBar myProgressBar = new MottleProgressBar();
                myProgressBar.Show();
    
                for (double i = 0; i < 100; ++i)
                {
                    myProgressBar.Status.Value = i;
                }
            }
        }
    


    Hope that helps.

    Please remember to close your threads by marking helpful posts as answer and then start a new thread if you have a new question. Please don't ask several questions in the same thread.

    Tuesday, September 29, 2015 2:45 PM