Click here to Skip to main content
15,885,216 members
Articles / Programming Languages / C#

MVP: Updating View’s Binding Asynchronously with the SynchronizationContext Object

Rate me:
Please Sign up or sign in to vote.
2.43/5 (4 votes)
15 Mar 2010CPOL2 min read 22.8K   210   8   3
MVP: Updating View’s binding asynchronously with the SynchronizationContext object.

Introduction

I was tasked with a small WinForms application a while back which contained a long running calculation. The user would initiate the calculation by pressing a button and when the calculation was complete, the result would then be displayed in a textbox. I also wanted to build the application using different layers so that each layer could vary independently.

Details

I decided to implement this application using the Model-View-Presenter (MVP) pattern. I would set the long running process to run on a different thread (of course) and when the calculation was complete, I would have the presenter update the view. Sounds easy, right? Let’s see.

I used basic data binding to bind a textbox on the view which would display the results:

C#
txtTotal.DataBindings.Add("Text", this, "Total");

The Windows Form itself is the view (implements the IView interface), so this is actually a self binding.

The Problem

Now here's the issue. If the ‘Total’ variable was updated on the UI thread, the data binding worked fine and the result was displayed in the textbox. However because I wanted the long running task to be executed on a different thread, when I updated the view from the presenter in a different thread, I received the following error:

'Cross-thread operation not valid: Control 'txtTotal' accessed from a thread other than the thread it was created on'

Every developer has seen this error before and hates it with every 'fiber' of his/her being. No pun intended. Data binding in .NET does not allow updating of data from a different thread. So how can we solve this?

The Solution

There are quite a few solutions to this issue like the BackgroundWorker component. However the solution I chose involved the SynchronizationContext object (which a BackgroundWorker uses behind the scenes. Each thread has SynchronizationContext associated with it. You can use this context to execute code using a specified thread either using the Post (asynchronous) or Send (synchronous) methods.

To get a reference to the SynchronizationContext, you can use the following code:

C#
SynchronizationContext.Current;

I simply use the view’s SynchronizationContext object whenever I update any databinded property on the view from the presenter. Here is my code for the presenter:

C#
internal class Presenter : IPresenter
 {
   public Presenter(IView view)
   {
       this.View = view;

       //Keep a reference to the view's SynchronizationContext
       this.Context = this.View.Context;

       //Subscribe to the view's events
       this.View.OnLongRunningCalculationRequested +=
       new EventHandler(View_OnLongRunningCalculationRequested);
   }

   private IView View
   {
       get;
       set;
   }

   private SynchronizationContext Context
   {
       get;
       set;
   }

   private void View_OnLongRunningCalculationRequested(object sender, EventArgs e)
   {
       Thread thread = new Thread(RunLongRunningProcess);
       thread.Start();
   }

   private void RunLongRunningProcess()
   {
       //Simulate a long running process
       if (Context != null)
       {
           ///Perform intense or long running calculations here
           decimal data = 1000 + 10;


           //But update the view using the SynchronizationContext here
           Context.Post(delegate
                        {
                            //The Post method sends this message asynchronously
                            View.Total = data;

                        }, null);
       }
   }
 }

Notice I update the Total property of the view using its own SynchronizationContext. That means the actual delegate gets called from the UI thread. And now, the view's binding to the 'Total' property works as expected.

Have fun and send any comments my way.

History

  • 15th March, 2010: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Finance Industry
United States United States
Currently pursuing 'Programming Nirvana' (The ineffable ultimate in which one has attained disinterested wisdom and compassion as it relates to programming)

Respected Technologies
1. Confusor (https://confuser.codeplex.com/)
2. Power Threading (http://www.wintellect.com/Resources/visit-the-power-threading-library)
3. EDI Parsers (http://www.rdpcrystal.com)


Acknowledgements:

Microsoft Certified Technologist for WPF and .Net 3.5 (MCTS)
Microsoft Certified Technologist for WCF and .Net 3.5 (MCTS)
Microsoft Certified Application Developer for .Net (MCAD)
Microsoft Certified Systems Engineer (MCSE)
Microsoft Certified Professional (MCP)

Sun Certified Developer for Java 2 Platform (SCD)
Sun Certified Programmer for Java 2 Platform (SCP)
Sun Certified Web Component Developer (SCWCD)

CompTIA A+ Certified Professional

Registered Business School Teacher for Computer Programming and Computer Applications (2004)
(University of the State of New York Education Department)

Graduated from University At Stony Brook

Comments and Discussions

 
GeneralDropDown Pin
smolesen28-Nov-10 23:14
smolesen28-Nov-10 23:14 
Hi
I'm trying to populate a dropdown the same way, by binding MyCommandDropDown.DataSource to MyCommandList property:

MyCommandsDropDown.DataBindings.Add("DataSource", this, "MyCommandsList");


public IList<mycommand> MyCommandsList
{
get { return _mycommandslist; }
set
{
_mycommandslist = value;
RaisePropertyChangedEvent("MyCommandsList");
}
}

However I keep getting a 'Complex DataBinding accepts as a data source either an IList or an IListSource.'

Any suggestions to how I acomplish that?

TIA

Søren
GeneralWe saw this approach earlier Pin
Alexander Chernosvitov15-Mar-10 13:36
Alexander Chernosvitov15-Mar-10 13:36 
GeneralRe: We saw this approach earlier Pin
FatCatProgrammer22-Mar-10 4:00
FatCatProgrammer22-Mar-10 4:00 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.