Click here to Skip to main content
15,867,686 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I have a screen which used to check connection with multiple devices by COM/USB Port as the below picture
https://i.stack.imgur.com/CAjK0.png[^]

When user click button Check Device if checking a device connection is finished, icon good or not good will display next to combobox and progress bar will update value based on total of devices. For example, there are 4 devices then progress bar will update 25%, 50%, 75%, 100% after checking.

What I have tried:

I tried to use PropertyChanged to update value but it's seem not working. After clicking button, the progress bar is visible but value is always 100. I also implement INotifyPropertyChanged in the cs file. Please help me with this!
Here is my code:

XAML:
XML
<ProgressBar x:Name="pgProcessing" Visibility="Collapsed" Minimum="0" Maximum="100" Value ="{Binding Percent, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>


Code behind:
C#
private double _percent;
public double Percent
    {
        get { return _percent; }
        set { _percent = value; OnPropertyChanged("Percent"); }
    }

public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string name)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }

private void BtnCheck_Click(object sender, RoutedEventArgs e)
    {
        pgProcessing.Visibility = Visibility.Visible;
        _percent = 0;
        this.Percent = _percent;

        // Do check connection 1st device
        // End

        _percent = (100 * currentDevice) / TOTAL_DEVICE;
        this.Percent = _percent; //50%

        // Do check connection 2nd device
        // End

        _percent = (100 * currentDevice) / TOTAL_DEVICE;
        this.Percent = _percent; //100%
    }


My constructor:
C#
public HealthCheck()
        {
            InitializeComponent();
            this.DataContext = this;
        }
Posted
Updated 16-Feb-23 5:02am
v2
Comments
Graeme_Grant 16-Feb-23 2:57am    
Are you processing with async/await?
huynhminh97 16-Feb-23 2:59am    
@Graeme_Grant, i'm not using async/await

Change this:
C#
_percent = (100 * currentDevice) / TOTAL_DEVICE;
this.Percent = _percent; //100%

To this:
C#
this.Percent = (100 * currentDevice) / TOTAL_DEVICE;


UPDATE #1

As you're binding to the code behind, you do not need INotifyPropertyChanged. Give the control a name and update directly.

Also, you're working directly on the UI thread, so there is no time for the UI to update.

UPDATE #2
Here is a working solution:
XML
<Window x:Class="WpfProgress.MainWindow"
        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:WpfProgress"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Button Content="RunTask" 
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Click="ButtonBase_OnClick"/>
        <ProgressBar x:Name="MyProgressBar"
                     Minimum="0" Maximum="100"
                     Grid.Row="1"
                     HorizontalAlignment="Center"
                     VerticalAlignment="Center"
                     Width="200"
                     Height="30"/>
    </Grid>
</Window>

... and the code-behind:
C#
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        var task = StartProcess();
    }

    private async Task StartProcess()
    {
        // do work here...
        for (int i = 0; i < 100; i++)
        {
            await Task.Delay(100).ConfigureAwait(false);

            // update the ProgressBar
            var progress = i;
            DispatcherHelper.Execute(() => MyProgressBar.Value = progress);
        }
    }
}

And the DispatcherHelper class to marshall back to the UI thread:
C#
public static class DispatcherHelper
{
    public static void Execute(Action action)
    {
        if (Application.Current is null || Application.Current.Dispatcher is null)
            return;

        // Marshall to Main Thread
        Application.Current.Dispatcher.BeginInvoke( DispatcherPriority.Background, action);
    }
}
 
Share this answer
 
v4
Comments
huynhminh97 16-Feb-23 3:10am    
I remove INotifyPropertyChanged out but progress bar still not update value.

Also, you're working directly on the UI thread, so there is no time for the UI to update.
=> So could you show me how to fix this
Graeme_Grant 16-Feb-23 3:20am    
see update
Andre Oosthuizen 16-Feb-23 5:50am    
In detail, nice! +5
Graeme_Grant 16-Feb-23 6:15am    
Thanks ...
huynhminh97 16-Feb-23 21:32pm    
@Graeme_Grant That's so great. Code is so easy to understand. Thank you so much.

Graeme’s solution is excellent. I would just like to add that an alternative approach is to use the generic Progress<T> class. That class captures the UI thread and removes the need for thread marshalling. See Reporting Progress From Async Tasks.


C#
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
    var task = StartProcess();
}

protected async Task StartProcess()
{

    var progress = new Progress<int>(percent =>
    {
        MyProgressBar.Value = percent;
    });
    await Task.Run(() => DoWork(progress));
}
public void DoWork(IProgress<int> progress)
{
    for (int i = 0; i <= 100; i += 25)

    {
        //simulate work
        Thread.Sleep(250);
        progress?.Report(i);
    }
}
 
Share this answer
 
v3
Comments
huynhminh97 16-Feb-23 21:35pm    
@George Swan Thanks for your answer
George Swan 17-Feb-23 1:13am    
You are most welcome.
Move your long running code from the UI thread to a second thread - the BackgroundWorker Class (System.ComponentModel) | Microsoft Learn[^] is a good candidate as it's easy to use and provides progress reporting back to the UI thread.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900