Hi there,

 

Although this post is very informative, it didn’t quite fulfill my needs.

I needed to start an asynchronous method, lazy loading some data, when a property bound to a WPF control was updated.

This is what I came up with:

 

public void UpdateInformation(string propertyValue)
{
	System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke(new Action(async () =>
	{
		_cancellationTokens.ForEach(c => c.Cancel());
		using (CancellationTokenSource cancellationToken = new CancellationTokenSource())
		{
			try
			{
				_cancellationTokens.Add(cancellationToken);
				var dataResponse = await service.LoadData(propertyValue, cancellationToken);
				...
			}
			finally
			{
				_cancellationTokens.Remove(cancellationToken);
			}
		}
	}));
}

As always, feel free to ask or comment.


WPF Controls has no ‘InvokeRequired’/’Invoke’ like WinForms, so making accessing a WPF Window/Control threadsafe can be accomplished by using the System.Windows.Threading.Dispatcher. Here are some examples. First a property: 

 

public int Progress
{
	get
	{
		if (!Dispatcher.CheckAccess())
		{
			Func<int> f = delegate () { return Progress; };
			return Dispatcher.Invoke(f);
		}

		...
	}
	set
	{
		if (!Dispatcher.CheckAccess())
		{
			Action<int> a = delegate (int progress) { Progress = progress; };
			Dispatcher.Invoke(a);
			return;
		}

		...
	}
}

 

  A void method

 

 

public void SetProgress(object obj, int progress)
{
	if (!Dispatcher.CheckAccess())
	{
		Action<object, int> a = new Action<object, int>(SetProgress);
		Dispatcher.Invoke(a, DispatcherPriority.Normal, obj, progress);
		return;
	}

	...
}

 

And lastly a method with a return value

 

 

public bool AllProcessesDone()
{
	if (!Dispatcher.CheckAccess())
	{
		Func<bool> f = new Func<bool>(AllProcessesDone);
		return Dispatcher.Invoke(f, DispatcherPriority.Normal);
	}

	...
}

 

As always, feel free to comment, or ask.

 


To ensure thread safety where Invoke is not implemented, can be done with a System.Windows.Threading.Dispatcher.

 

The Dispatcher should of course run on same thread as the thread safe instance, hence the Dispatcher instance should be created when the thread safe instance is constructed.

 

A little example is in order here, I guess.

Say that we have a object Foo accessed by object Bar, in Bar’ backgroundworker. This would cause a cross-thread exception

public class Foo
{
    private Object _fooBar;
    public Object FooBar
    {
        get { return _fooBar; }
        set { _fooBar = value; }
    }
    
    public Foo()
    {
    }
}

public class Bar
{
    private Foo _foo;
    public Foo Foo
    {
        get { return _foo; }
        set { _foo = value; }
    }
    
    public Bar()
    {
        this.Foo = new Foo();

        BackgroundWorker backgroundWorker = new BackgroundWorker();
        backgroundWorker.DoWork += backgroundWorker_DoWork;
    }

    void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        Foo.FooBar = NextFooBar();
    }

    private object NextFooBar()
    {
        return new Object();
    }
}

Since Foo don’t implement Invoke (or inherit from a object that does), one way to get about this would be to use a System.Windows.Threading.Dispatcher.

public class Foo
{
    private System.Windows.Threading.Dispatcher _dispatcher = null;
    private Object _fooBar;
    public Object FooBar
    {
        get { return _fooBar; }
        set { _dispatcher.Invoke(((Action<Object>))delegate(v) { internalSetFooBar(v); }, new object[] { value }); }
    }
    private void internalSetFooBar(object value)
    {
        _fooBar = value;
    }
    
    public Foo()
    {
        _dispatcher = System.Windows.Threading.Dispatcher.CurrentDispatcher;
    }
}

public class Bar
{
    private Foo _foo;
    public Foo Foo
    {
        get { return _foo; }
        set { _foo = value; }
    }
    
    public Bar()
    {
        this.Foo = new Foo();

        BackgroundWorker backgroundWorker = new BackgroundWorker();
        backgroundWorker.DoWork += backgroundWorker_DoWork;
    }

    void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
    {
        Foo.FooBar = NextFooBar();
    }

    private object NextFooBar()
    {
        return new Object();
    }
}

As always, feel free to comment, or ask.

 


InvalidOperationException: "Cross-thread operation not valid: Control '<name>' accessed from a thread other than the thread it was created on."

If you are getting this exception it means that your are trying to access a Control - running on the main thread of your application, say ThreadA - from another thread, ThreadB.

This other ThreadB could of-course be either a

System.Threading.Thread

 or a

System.ComponentModel.BackgroundWorker

or whatever.


The way to get around this, is by using invocation in the Control.


Here is a example if you don’t need a return value:

public void AddMessage(MessageType type, Message message)
{
    internalAddMessage(type, message);
}

private void internalAddMessage(MessageType type, Message message)
{
    if (this.InvokeRequired)
    {
        this.Invoke((MethodInvoker)delegate { internalAddMessage(type, message); });
        return;
    }
    //Do the 'Add Message' stuff you want to do
}

or if you need a return value from the Invoked method:

 

private DialogResult internalShowDialog()
{
    if (this.InvokeRequired)
    {
        Func<DialogResult> func = new Func<DialogResult>(internalShowDialog);
        return (DialogResult)this.Invoke(func);
    }
    else
    {
        return this.ShowDialog();
    }
}