How to do the processing and keep GUI refreshed using databinding?

Posted by macias on Stack Overflow See other posts from Stack Overflow or by macias
Published on 2010-12-23T21:24:01Z Indexed on 2010/12/23 21:54 UTC
Read the original article Hit count: 256

Filed under:
|
|
|

History of the problem

This is continuation of my previous question

How to start a thread to keep GUI refreshed?

but since Jon shed new light on the problem, I would have to completely rewrite original question, which would make that topic unreadable. So, new, very specific question.

The problem

Two pieces:

  • CPU hungry heavy-weight processing as a library (back-end)
  • WPF GUI with databinding which serves as monitor for the processing (front-end)

Current situation -- library sends so many notifications about data changes that despite it works within its own thread it completely jams WPF data binding mechanism, and in result not only monitoring the data does not work (it is not refreshed) but entire GUI is frozen while processing the data.

The aim -- well-designed, polished way to keep GUI up to date -- I am not saying it should display the data immediately (it can skip some changes even), but it cannot freeze while doing computation.

Example

This is simplified example, but it shows the problem.

XAML part:

    <StackPanel Orientation="Vertical">
        <Button Click="Button_Click">Start</Button>
        <TextBlock Text="{Binding Path=Counter}"/>
    </StackPanel>

C# part (please NOTE this is one piece code, but there are two sections of it):

public partial class MainWindow : Window,INotifyPropertyChanged
{
    // GUI part
    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        var thread = new Thread(doProcessing);
        thread.IsBackground = true;
        thread.Start();
    }

    // this is non-GUI part -- do not mess with GUI here
    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(string property_name)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(property_name));
    }

    long counter;
    public long Counter
    {
        get { return counter; }
        set
        {
            if (counter != value)
            {
                counter = value;
                OnPropertyChanged("Counter");
            }
        }
    }


    void doProcessing()
    {
        var tmp = 10000.0;

        for (Counter = 0; Counter < 10000000; ++Counter)
        {
            if (Counter % 2 == 0)
                tmp = Math.Sqrt(tmp);
            else
                tmp = Math.Pow(tmp, 2.0);
        }
    }
}

Known workarounds

(Please do not repost them as answers)

Those two first are based on Jon ideas:

  • pass GUI dispatcher to library and use it for sending notifications -- why it is ugly? because it could be no GUI at all
  • give up with data binding COMPLETELY (one widget with databinding is enough for jamming), and instead check from time to time data and update the GUI manually -- well, I didn't learn WPF just to give up with it now ;-)
  • and this is mine, it is ugly, but simplicity of it kills -- before sending notification freeze a thread -- Thread.Sleep(1) -- to let the potential receiver "breathe" -- it works, it is minimalistic, it is ugly though, and it ALWAYS slows down computation even if no GUI is there

So... I am all ears for real solutions, not some tricks.

© Stack Overflow or respective owner

Related posts about c#

Related posts about wpf