WPF DependencyProperty throws InvalidOperationException

I am trying to set a dependency property which is updated by a WCF callback thread. There is a ProgressBar on MainWindow.xaml that is bound to this property:

MainWindow.xaml

<ProgressBar Name="ProgressBar" Value="{Binding Progress, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />

MainWindow has an instance of DemoModule, which is defined as:

DemoModule.xaml.cs

/// <summary>
///     Interaction logic for DemoModule.xaml
/// </summary>
public partial class DemoModule : UserControl, INotifyPropertyChanged
{
    public static readonly DependencyProperty ProgressProperty = DependencyProperty.Register("Progress", typeof(int), typeof(DemoModule));        
    public event ProgressEventHandler ProgressChanged;
    public event PropertyChangedEventHandler PropertyChanged;

    public int Progress
    {
        get { return (int)GetValue(ProgressProperty); }
        set { SetValue(ProgressProperty, value); }  // setter throws InvalidOperationException "The calling thread cannot access this object because a different thread owns it"
    }

    /// <summary>
    ///     Initializes a new instance of the <see cref="DemoModule" /> class.
    /// </summary>
    public DemoModule()
    {
        InitializeComponent();
        ProgressChanged += OnProgressChanged;
    }

    /// <summary>
    /// Called when [progress changed].
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="args">The <see cref="ProgressChangedEventArgs" /> instance containing the event data.</param>
    public void OnProgressChanged(object sender, ProgressChangedEventArgs args)
    {
        Debug.WriteLine("Current Thread: {0}", Thread.CurrentThread.ManagedThreadId);
        Debug.WriteLine("Current Dispatcher Thread: {0}", Application.Current.Dispatcher.Thread.ManagedThreadId);

        if (ProgressChanged == null) return;

        Debug.WriteLine("ProgressChangedEventArgs.Current: " + args.Current);
        Progress = Convert.ToInt32(args.Current * 100);
        OnPropertyChanged("Progress");
    }

    /// <summary>
    /// Called when [property changed].
    /// </summary>
    /// <param name="propertyName">Name of the property.</param>
    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        Trace.WriteLine("Property " + propertyName + " changed. Value = " + Progress);
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }

}

The Progress.set() is throwing an exception because of the thread affinity. How can I fix this?

Update 1

This is allegedly thread safe, but has no effect:

public int Progress
{
    get
    {
        return Dispatcher.Invoke((() => (int)GetValue(ProgressProperty))); 
    }
    set
    {
        Dispatcher.BeginInvoke((Action)(() => SetValue(ProgressProperty, value)));
    }
 }

Update 2

My DemoModule.xaml.cs has a reference to a client library which implements the WCF callback method OnUpdateProgress:

InstallerAgentServiceClient.cs

public void OnUpdateProgress(double progress)
        {
            //Debug.WriteLine("Progress: " + progress*100 + "%");
            var args = new ProgressChangedEventArgs(progress, 1, "Installing");
            _installModule.OnProgressChanged(this, args);
        }

The _installModule object above is the instance of DemoModule.

Update 3

After removing the [CallBackBehavior] attribute from the WCF client library, there no longer seems to be thread synchronization issues. I can update the progress bar in the MainWindow as follows:

DemoModule.xaml.cs

public void OnProgressChanged(object sender, ProgressChangedEventArgs args)
{
    Progress = Convert.ToInt32(args.Current * 100);
    var progressBar = Application.Current.MainWindow.FindName("ProgressBar") as ProgressBar;
    if (progressBar != null)
        progressBar.Value = Progress;
}

Answers


I recommand using the IProgress interface. Works like a charm for me and is pretty easy to use. In your progressbarVM add

public double Actualprogress
{
get { return (double)GetValue(ActualprogressProperty); }
set { SetValue(ActualprogressProperty, value); }
}
public static readonly DependencyProperty ActualprogressProperty =
DependencyProperty.Register("Actualprogress", typeof(double), typeof(ProgressBar), 
new PropertyMetadata(0.0));

then call your method as an asyn task using await like so :

var progress = new Progress<double>(progressPercent => 
progressBarVM.progressBar.Actualprogress = progressPercent);
Parser parser = new Parser();
ModelVMResult result = await Task.Run(() => parser.Parse(filename,progress));

then in your method "parse" just do:

float threshold = 0.0f;
for (int i = 0; i < count; i++)
{
if (i >= threshold)
{ progress.Report(prog += 1); threshold += count / 100.0f; }
this.Readline(reader, i);
}

Of course you need to bind your xaml progressbar.Value to ProgressbarVM.Actualprogress. Then your progressbar will update and your app will still be responsive during the process.


You need to update your DepedencyProperty via the UI Thread. Use:

Application.Current.Dispatcher.BeginInvoke(Action)

Or:

Application.Current.Dispatcher.Invoke(Action)

Need Your Help

Why is this highscore glitching?

ios objective-c sprite-kit

For some reason, the highscore on the game over page is not properly displayed. After a round of play, both the score and highscore are displayed on the game over menu. The score is displayed fine,...

Emacs shell scripts - how to put initial options into the script?

shell emacs command-line elisp options

Inspired by Stack&nbsp;Overflow question Idomatic batch processing of text in Emacs? I tried out an Emacs shell script with the following headline: