Progressbar in WPF project

EccoBravo

Member
Joined
Jun 7, 2021
Messages
8
Programming Experience
1-3
Hello,
As a beginner of C#, I have troubles to code a WPF project with progress bar.

I created a WPF project with .cs file, .xaml file and .xaml.cs file.

In following link the code was explained in a simple kind:
(The asynchron worker philosophy in this link, is only in the code of .xaml file and .xaml.cs file explained.)

The problem is, in this example, the progressbar will be controlled in the xaml.cs file.

What is my question:
I will control the progressbar by the code of the .cs file, not from the .xaml.cs file:

How can I do this?

Many thanks
EB
 
More details please...

Like what have you tried?

What event/methods will be controlling your progress bar?

What type of pattern does your application require?

The common and most recommended is MVVM, but that may be over the top for what you are doing. As there are also other patterns available to you. Picking a model pattern depends on what your application requirements are.
 
Thank You for answer
Let me explain the problem by code. The code was linked in the TE Post.

My project consist of three parts,, the .xaml file, the .xaml.cs file and the most importaant .cs file.

.xaml file:
<Window x:Class="WpfTutorialSamples.Misc_controls.ProgressBarTaskOnUiThread"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ProgressBarTaskOnUiThread" Height="100" Width="300"
ContentRendered="Window_ContentRendered">
    <Grid Margin="20">
        <ProgressBar Minimum="0" Maximum="100" Name="pbStatus" />
    </Grid>
</Window>

.xaml.cs file:
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows;

namespace WpfTutorialSamples.Misc_controls
{
    public partial class ProgressBarTaskOnWorkerThread : Window
    {
        public ProgressBarTaskOnWorkerThread()
        {
            InitializeComponent();
        }

        private void Window_ContentRendered(object sender, EventArgs e)
        {
            BackgroundWorker worker = new BackgroundWorker();
            worker.WorkerReportsProgress = true;
            worker.DoWork += worker_DoWork;
            worker.ProgressChanged += worker_ProgressChanged;

            worker.RunWorkerAsync();
        }

        void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            for(int i = 0; i < 100; i++)   // this was not my intent.  Where ist the entry point for the .cs file control?
            {
                (sender as BackgroundWorker).ReportProgress(i);
                Thread.Sleep(100);
            }
        }

        void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            pbStatus.Value = e.ProgressPercentage;
        }
    }
}

.cs file:
// this is only draft code
. . .
    pbStatus.Minimum = 0;
    pbStatus.Maximum = zimax;
    window.ShowDialog();
    for(int zi = 0; zi < zimax; zi++)
    {
        //long code
        pbStatus.Value++;   // this should control ProgressBar
        Thread.Sleep(100);
    }
. . .

All the demos and tutorials control the ProgressBar by the .xaml.cd file (line (sender as BackgroundWorker).ReportProgress(i); )

I intent to control the ProgressBar by the pbStatus.Value++ line i.e. by the .cs file.

Many thanks
EB
 
Last edited:
The code was linked in the TE Post.
I don't read links. I read only what text is in the actual topic. I couldn't care what tutorial you followed. All I am interested in is your code, and the problem you have, and whatever your expectations are when using that code.

Basically, what you want to do by the looks of your current code is to use a backgroundworker to update your ui by reporting the progress made by the work you set your worker to do when calling worker.RunWorkerAsync(), instead of updating the ui from within xaml code?

I am rather busy at present, but promise I will look at this a little bit later on when I can find free time.
 
You are supposed to do your work inside the DoWork handler. It doesn't really matter where that method lives. It looks like in that tutorial they left the handler inside the .xaml.cs for the sake of simplicity.
 
You are supposed to do your work inside the DoWork handler. It doesn't really matter where that method lives. It looks like in that tutorial they left the handler inside the .xaml.cs for the sake of simplicity.
I think so too!
Thats my problem!
All tutorials post the same idea!
Thanks
EB
 
I don't read links. I read only what text is in the actual topic. I couldn't care what tutorial you followed. All I am interested in is your code, and the problem you have, and whatever your expectations are when using that code.

Basically, what you want to do by the looks of your current code is to use a backgroundworker to update your ui by reporting the progress made by the work you set your worker to do when calling worker.RunWorkerAsync(), instead of updating the ui from within xaml code?

I am rather busy at present, but promise I will look at this a little bit later on when I can find free time.
Thank!
I am looking forward to!
EB
 
Sorry, I am still tied up and don't have enough time to throw in a MVMV example. To keep it simple though, you are almost there. There are a few ways to update controls in WPF, thought the most recommended is using MVVM. You can also use the dispatcher.invoke() method and delegate back to the calling code that created the dispatched thread operation. I glanced back over your code, and I have wrote mine out similarly... in fact not much different. If you need help introducing a design pattern, I'm afraid one of the others on the board will have to assist you as I don't have a lot of time this week. But If I find spare time, I will post something up for you. Here's a gif of it working and updating :

rFmRhPh.gif

C#:
<Window
        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:WpfTestApp" x:Class="WpfTestApp.MainWindow"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="80*"/>
            <ColumnDefinition Width="235*"/>
            <ColumnDefinition Width="81*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="120*"/>
            <RowDefinition Height="185*"/>
            <RowDefinition Height="114*"/>
        </Grid.RowDefinitions>
        <ProgressBar x:Name="MyProgressBar" Grid.Column="1" HorizontalAlignment="Left" Height="45"
                     Margin="20,45,0,0" Grid.Row="1" VerticalAlignment="Top" Width="430" Initialized="MyProgressBar_Initialized"
                     Value="0">
        </ProgressBar>
        <Button x:Name="button" Content="Button" Grid.Column="1" HorizontalAlignment="Left" Margin="305,30,0,0" Grid.Row="2" VerticalAlignment="Top" Width="145" Height="30" Click="button_Click"/>

    </Grid>
</Window>
C#:
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
        private readonly BackgroundWorker BackgroundWorker = new();
        private void MyProgressBar_Initialized(object sender, EventArgs e)
        {
            BackgroundWorker.DoWork += BackgroundWorker_DoWork;
            BackgroundWorker.ProgressChanged += BackgroundWorker_ProgressChanged;
            BackgroundWorker.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;
            BackgroundWorker.WorkerSupportsCancellation = true;
            BackgroundWorker.WorkerReportsProgress = true;
        }

        private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            Debug.WriteLine("I finished my work sucessfully.");
        }

        private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) /* Fired from BackgroundWorker.ReportProgress() */
        {
            MyProgressBar.Value = e.ProgressPercentage;
            Debug.WriteLine($"Reporting progress at {e.ProgressPercentage}% completion.");
        }
        private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            int i = 0;
            while (i < 100)
            {
                i++;
                BackgroundWorker.ReportProgress(i);/* Fires the progress changed event */
                Thread.Sleep(100);
            }
        }

        private void button_Click(object sender, RoutedEventArgs e)
        {
            if (BackgroundWorker.IsBusy)
            {
                Debug.WriteLine("Already running, please wait for the current task to finish first.");
            }
            else
            {
                BackgroundWorker.RunWorkerAsync();
            }
        }
    }

When the progress bar initialised, we added the events for the backgroundworker :
C#:
        private void MyProgressBar_Initialized(object sender, EventArgs e)
        {
            BackgroundWorker.DoWork += BackgroundWorker_DoWork;
            BackgroundWorker.ProgressChanged += BackgroundWorker_ProgressChanged;
            BackgroundWorker.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;
            BackgroundWorker.WorkerSupportsCancellation = true;
            BackgroundWorker.WorkerReportsProgress = true;
        }
The background worker is defined at the partial scope of the MainWindow class :
C#:
       private readonly BackgroundWorker BackgroundWorker = new();

I start the worker from a button click, but you can use whatever event you want from whatever control you want to use. Providing the background worker is not already running a task, it will execute :
C#:
        private void button_Click(object sender, RoutedEventArgs e)
        {
            if (BackgroundWorker.IsBusy)
            {
                Debug.WriteLine("Already running, please wait for the current task to finish first.");
            }
            else
            {
                BackgroundWorker.RunWorkerAsync();
            }
        }
BackgroundWorker.RunWorkerAsync(); will kick off void BackgroundWorker_DoWork. Notice the ReportProgress takes the integer value for the percentage value of your running task. This then fires the void BackgroundWorker_ProgressChanged method :
C#:
        private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) /* Fired from BackgroundWorker.ReportProgress() */
        {
            MyProgressBar.Value = e.ProgressPercentage;
            Debug.WriteLine($"Reporting progress at {e.ProgressPercentage}% completion.");
        }
MyProgressBar.Value is the property of the value of our progress bar which is defined in our Xaml :
C#:
<ProgressBar x:Name="MyProgressBar" Grid.Column="1" HorizontalAlignment="Left" Height="45"
                     Margin="20,45,0,0" Grid.Row="1" VerticalAlignment="Top" Width="430" Initialized="MyProgressBar_Initialized"
                     Value="0">
        </ProgressBar>
Ok so I can spare another 5 minutes on this... If you want to add a model pattern, and bind your data. Note the closing tag for the progress bar control? Inside the closing tag, you can add a DataContext. It would look something similar to this :
C#:
            <ProgressBar.DataContext>
                <local:ProgressBarVM/>
            </ProgressBar.DataContext>
with the closing tag following it : </ProgressBar>
Then you can do something like this with your progressbar value property, binding it to your model :
C#:
Value="{Binding Path=ProgressBar_Value, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
.

A stereotypical model with the INotifyPropertyChanged interface would look something like this code below. It's wrote freehand without checking any docs, so double check on msdn that it is wrote correctly :

C#:
    public class ProgressBarVM : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private double progressBar_Value;
        public double ProgressBar_Value
        {
            get => progressBar_Value;
            set
            {
                progressBar_Value = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(ProgressBar_Value.ToString())); /* Call OnPropertyChanged when you change property value */
            }
        }
    }

Now that's as much time I can spare on this, but what you must do now is change this block of code to reflect using a model to interact with the above :
C#:
        private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) /* Fired from BackgroundWorker.ReportProgress() */
        {
            MyProgressBar.Value = e.ProgressPercentage;
            Debug.WriteLine($"Reporting progress at {e.ProgressPercentage}% completion.");
        }
You need to set the ProgressBar_Value inside ProgressBarVM model in place of where you are reading back the percentage. This is probably not perfect as up-until introducing a model, I have not tested this code. But the other guys and girls will be happy to help you if you need it. This should be enough to get you pointed in the right direction. I think using the MVVM is overkill for one control, however it's what is actually recommended. For simplicity, you can use the dispatcher I linked above and delegate the percentage back to your UI control. But MVVM nerds frown at this apprach.

Happy coding.
 
Last edited:
Thank you for the detailed answers!
It's already a tough one for a beginner like me.
I will do my best to dig in.
Maybe there is someone else who can give me further tips.
I had already heard something about MVVM, but have no idea what it is.
By the way, I've been trying to solve this problem for almost two weeks now and haven't gotten anywhere. That's why I turned to this forum. I am glad that people are trying to help me.
So, thanks to all of you!
EB
 
Last edited:
Two questions of understanding, surely the following code from Sheepings belongs in the .cs file and not in the .xaml.cs file, because this should be data related (not UI rlated)?

C#:
public class ProgressBarVM : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private double progressBar_Value;
        public double ProgressBar_Value
        {
            get => progressBar_Value;
            set
            {
                progressBar_Value = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(ProgressBar_Value.ToString())); /* Call OnPropertyChanged when you change property value */
            }
        }
    }

How do I put the following xaml code in the .xaml file?

C#:
            <ProgressBar.DataContext>
                <local:ProgressBarVM/>
            </ProgressBar.DataContext>

Now I will read up on the topic of MVVM.

Kindest Thanks and regards
EB

PS:
Here I think, I found a pretty good MVVM explanation (in german).
 
Last edited:
Respectfully, the code from Sheeping does not adhere to the MVVM pattern.

Model => Your database, or WCF data source, dataset, collection, etc...
View => The XAML of your view, using databindings to your ViewModel
ViewModel => A simple class that contains the data you want to display on your form. This class must implement INotifyPropertyChanged.

Basically, all you need to do is create a property on your ViewModel, have that property's setter call OnPropertyChanged, bind your progressbar's Value property to your viewmodel property, and then just update the viewmodel property. You should not ever, under ANY circumstance, have to interact with the view directly in WPF. You just bind the view, update the viewmodel, and everything else is taken care of by the framework.

Also, WPF is made to work with MVVM, you should immediately cease any attempt to use WPF without MVVM. It's not gonna work right. It's not made to.

XML:
<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication1" x:Class="MainWindow"
    Title="MainWindow" Height="350" Width="525">

    <Window.DataContext>
        <local:MyViewModel/>
    </Window.DataContext>

    <Grid>
        <ProgressBar  Minimum="0" Maximum="100" Value="{Binding PercentDone}" />
    </Grid>
</Window>

C#:
public class MyViewModel : INotifyPropertyChanged
{
    #region INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string name = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
    #endregion
  
    private decimal _percentDone = 0m; 
    public decimal PercentDone
    {
        get => _percentDone;
        set
        {
            _percentDone = value;
            OnPropertyChanged();
        }
    }
  
    public MyViewModel()
    {
      
    }
  
  
    public void DoSomething()
    {
        // Do whatever you need to do here, and increment the property, which calls the setter,
        // and the INotifyPropertyChanged interface takes care of telling your view to update itself.
            
        PercentDone += 1;
    } 

}
 
Last edited:
You should not ever, under ANY circumstance, have to interact with the view directly in WPF.

If you are following the "recommended" design principle of MVVM, then yes you are correct. But if you are not, and are doing something simple like making a progress bar loading screen where the percentage is delegated safely. Then it's Ok. I really do consider the use of mvvm for something this simple to be quite excessive and unnecessary. Also, lets not pretend like mvvm is the only pattern which can be used in wpf, because its not. It was microsoft who made the push for the use of mvvm in wpf. There are actually many other alternative patterns which can also be used. Such as dependency injection. command just to name a few...

The observer pattern I was writing in notepad++ was unfinished and untested as I already stated above. So respectfully, it does adhere to mvvm, except it was simply an unfinished and unfurnished example.

Respectfully, the code from Sheeping does not adhere to the MVVM pattern.

I already explained that It wasn't meant to follow the MVVM pattern. The background worker delegates the percentage through its events. So it can be safely set without jamming up the UI.
Only towards the end of writing that post, did I write out the observer Interface (INotifyPropertyChanged), but I didn't include some bits and bobs, as I said, I didn't have time to write a full blown mvvm example for the op, nor was I using visual studio to help. There is always one :cool:
But MVVM nerds frown at this approach.

You should note; the architecture project type does not dictate the pattern you need to use. The pattern you need to use should be dictated by your applications requirements. Mvvm was never intended to be used on only one control. It was meant to be used on complex applications with multiple binding rules for a multitude of controls spanning one or many models. When everyone else was still writing win forms apps, i was writing in wpf. And so, I too used to use the mvvm pattern quite a lot, but its not always a requirement, and people should know that alternate patterns are optional in wpf.

surely the following code from Sheepings belongs in the .cs

Yes.

How do I put the following xaml code in the .xaml file?

Already explained that to you. And haven't time to reexplain it.

Now I will read up on the topic of MVVM.

There is no shortage of mvvm tutorials. It's often overkill to use for something as simple as what you are doing.
 
@Herman, I am unable to PM you privately, which is what I wanted to do here. But I don't have access to start pm's since I stepped down from the staff board...

Let me please try to reason with you here without side tracking the topic again. Ok? Nobody is making you stay here. If you are unhappy then you can sign-out, as can I. I would rather see you and other people hang about the forum and chime-in on posted questions personally. I like to see people helping others. Your post was welcomed, but it was your remark I was trying to correct. I don't know who you are or what your deal is. You came onto this board from the shadows claiming I said something I didn't.

Look, you appear to have experience in c sharp, and if you ask me, that is what this board needs. It needs more people who will get involved answering topics and specifically so. But that's best done without accusing people of saying something they never did. Should you choose to stay on the forums, then you will need to read my posts fully in the future. I am very specific for a reason and I almost never make mistakes. And If I do, I would prefer any mistakes I do make to be pointed out to me, as they have in the past. The reason I am explicit in explaining posts (often in detail), is because people misinterpret textual contexts really easily on the inet, and I am well aware of this. Often due to language barriers, but that's not what happened here, and we both know it.

I don't believe in being rude nor do I like resulting to name calling, as I actually despise it. And for that I will apologise. Sorry.

I've been on this board a long time, and I get on well with the staff and all the members, and if I ever permanently leave this website, I won't be joining another one for the same reasons as what you said in your deleted posts. I understand that more than anyone, and I have no intentions to make things awkward for you. Over the years, I have gone from administering boards like this to being a participant just like you. I've seen it all, and I can relate to what you said about why you don't post on forums and I wasn't trying to get at you in my reply. I am generally blunt, and to the point when pointing out irregularities either in code or in posts, and my bluntness should not be construed as ignorance.

I believe in treating people as I would like to be treated. (Nice and respectful). That changed when you began to melt my head with your accusation. :) It's clear where the mistake resides : Progressbar in WPF project when you said my code didn't adhere to mvvm, and I said it wasn't meant to in the first line of my example. I was pointing out your misunderstanding, and it blew up from there. You made a false allegation against me, as I never set-out to provide an example using the observable pattern in detail or even at all. Lets put it behind us. Ok? I am normally calm and down to earth, and very easy to get along with except when dealing with stupidity. Or if it's dam soring hot day, and then It doesn't take much to trigger me and turn me into the devils syndicate doppelgänger. lol

I am very very seldom on this side of the fence watching someone wanting to leave a site because of replies I have made. Over the years, I observed people leaving boards because of other people, and almost never because of posts I've made myself. This is why I am specific, and this is why you are getting a lengthily reply, in the hope that you understand the point being made here.

If I posted a wrong example, I would have been corrected on it by one of the others. But they didn't correct me because they likely read all that I wrote in full. Obviously they didn't upvote it or approve it as an answer, because they read that I had said it was incomplete in bold at the bottom of my reply. And while the op was using a background worker which also safely delegates its percentage values back, it was actually safe to update the UI. Given the simplicity of what the op wanted, and since they are only learning, I wasn't planning on over complicating it. As most experienced devs have trouble understanding the observer pattern, and for that reason, I wanted to keep it simple for the op. Being honest, I probably should have left it out of my reply anyway as it was irrelevant to the posted question our op wanted answers too which I actually answered regardless of the approach. My main aim was to make the OP understand the BackGroundWorker.

Hope to see more of you on the site. I'm not holding spite, so you have no reason to leave. Are we good?
 
Hello Mr. Sheepings,
I am amazed and appalled at what a terrible row my little question has sparked. Never in my life did I complain about your answer, on the contrary, I thought it was very nice how you took care of my question. Thank you very much for that!
But if my further questions have come across a bit funny, then it is trained to my lack of professional understanding. As I wrote, I am not a computer scientist, just an amateur self-taught! With my follow-up questions I have never insinuated anything to you but I have only difficulties in understanding myself. To the clarification I am very grateful!
But if I did not fit with my question I will say goodbye to the forum. My path of knowledge will not be easier.
Then I would like to apologize finally for this not intended misunderstanding!
With kind regards
EccoBravo
 
Back
Top Bottom