I want to bind the value property of a WPF ProgressBar to a dependency property that is updated during a long running process. If the long running process is called from the main thread then this blocks the UI (and hence the ProgressBar) from updating until the process completes - preventing the desired progress through the process being shown. The long running process also cannot be run by spinning a seperate thread because it is not possible to update a dependency property from a different thread to its owner (i.e. the thread on which it was created).
In the code below, when the button is clicked, the long running process runs and the progress bar jumps from 0% to 100% when it completes. Instead, I want to be able to click the button and have the progress bar show the progress through the long running progress (i.e. not just updating from 0% to 100% when the process finishes but showing a smooth progression).
MainWindow.xaml
<Window x:Class="ProgressBarTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<StackPanel>
<Button Width="200" Height="50" x:Name="btnRun" Click="btnRun_Click">Run Process</Button>
<ProgressBar Width="200" Height="20" x:Name="pbProgress" Minimum="0" Maximum="100" Value="{Binding Path=MyFoo.ProgressValue}"/>
</StackPanel>
</Window>
MainWindow.xaml.cs
using System.Windows;
using System.Threading;
namespace ProgressBarTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public Foo MyFoo { get; set; }
public MainWindow()
{
MyFoo = new Foo();
InitializeComponent();
}
private void btnRun_Click(object sender, RoutedEventArgs e)
{
btnRun.IsEnabled = false;
MyFoo.LongRunningProcess(); // Since this runs on same thread as UI, progress bar does not update until the long running process completes.
btnRun.IsEnabled = true;
}
}
public class Foo : DependencyObject
{
public static readonly DependencyProperty ProgressValueProperty = DependencyProperty.Register("ProgressValue", typeof(double), typeof(Foo));
public double ProgressValue
{
get { return (double)GetValue(ProgressValueProperty); }
set
{
SetValue(ProgressValueProperty, value);
}
}
public Foo()
{
ProgressValue = 0;
}
public void LongRunningProcess()
{
do
{
ProgressValue += 1;
Thread.Sleep(30);
}
while (ProgressValue < 100);
}
}
}
P.S. I know there is a way I can do this by passing the ProgressBar instance as an argument to the long running process so that it can update it directly through Dispatcher.Invoke, but that is not what I want. I want to have the progress bar update through binding to a dependency property.
Thanks
Sean