views:

333

answers:

3

I've got a routine that grabs a list of all images in a directory, then runs an MD5 digest on all of them. Since this takes a while to do, I pop up a window with a progress bar. The progress bar is updated by a lambda that I pass in to the long-running routine.

The first problem was that the progress window was never updated (which is normal in WPF I guess). Since WPF lacks a Refresh() command I fixed this with a call to Dispatcher.Invoke(). Now the progress bar is updated for a while, then the window stops being updated. The long-running work does eventually finish and the windows go back to normal.

I have already tried a BackgroundWorker and quickly became frustrated by a threading issue related to an event triggered by the long-running process. So if that's really the best solution and I just need to learn the paradigm better, please say so.

But I'd be really much happier with the approach I've got here, except that it stops updating after a bit (for example, in a folder with 1000 files, it might update for 50-100 files, then "hang"). The UI does not need to be responsive during this activity, except to report on progress.

Anyway, here's the code. First the progress window itself:

public partial class ProgressWindow : Window
{
    public ProgressWindow(string title, string supertext, string subtext)
    {
        InitializeComponent();
        this.Title = title;
        this.SuperText.Text = supertext;
        this.SubText.Text = subtext;
    }

    internal void UpdateProgress(int count, int total)
    {
        this.ProgressBar.Maximum = Convert.ToDouble(total);
        this.ProgressBar.Value = Convert.ToDouble(count);
        this.SubText.Text = String.Format("{0} of {1} finished", count, total);
        this.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate);
    }

    private static Action EmptyDelegate = delegate() { };
}


<Window x:Class="Pixort.ProgressWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Pixort Progress" Height="128" Width="256" WindowStartupLocation="CenterOwner" WindowStyle="SingleBorderWindow" ResizeMode="NoResize">
    <DockPanel>
        <TextBlock DockPanel.Dock="Top" x:Name="SuperText" TextAlignment="Left" Padding="6"></TextBlock>
        <TextBlock DockPanel.Dock="Bottom" x:Name="SubText" TextAlignment="Right" Padding="6"></TextBlock>
        <ProgressBar x:Name="ProgressBar" Height="24" Margin="6"/>
    </DockPanel>
</Window>

The long running method (in Gallery.cs):

public void ImportFolder(string folderPath, Action<int, int> progressUpdate)
{
    string[] files = this.FileIO.GetFiles(folderPath);

    for (int i = 0; i < files.Length; i++)
    {
        // do stuff with the file
        if (null != progressUpdate)
        {
            progressUpdate.Invoke(i + 1, files.Length);
        }
    }
}

Which is called thusly:

 ProgressWindow progress = new ProgressWindow("Import Folder Progress", String.Format("Importing {0}", folder), String.Empty);
 progress.Show();
 this.Gallery.ImportFolder(folder, ((c, t) => progress.UpdateProgress(c, t)));
 progress.Close();
A: 

Mate do simple WPF programming by using DataBinding. Refer MVVM design pattern explaining same.

Bind progressbar value property with some source property defined in DataContext class. And update source property in dispatcher invoked method.

WPF engine will take care of rest.

You have currently written code wothout any binding...

Rakesh Gunijan
Do you know where I can find a working example of this? I tried a lot of different things to get this to work without adding layers of indirection to the code. Every time the bar was not updated at all.
mcl
Hi, I have posted code. Check it.
Rakesh Gunijan
+1  A: 

Turns out this is related to the DispatcherPriority in UpdateProgress. Changing DispatcherPriority.Render to something lower, in my case DispatcherPriority.Background did the trick.

Henk's answer lead me to believe that if the message pump is overwhelmed, that it needed help sorting out what to do when. Changing priority seems to be just the ticket.

mcl
A: 
Rakesh Gunijan