Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Cross-Thread Form Binding - Can it be done?

2,817 views
Skip to first unread message

jehuga...@gmail.com

unread,
Feb 1, 2008, 11:33:57 PM2/1/08
to
Hello:

I have a business object that reports the updated status of a download
taking place in another thread. I wanted to simplify my code by having
the interface bind to my business object directly. My business object
implements INotifyPropertyChanged.

I get a cross-thread error since the download is taking place in
another thread. What is *the* way for handling this type of issue. If
it makes any difference, I am binding a DataGridView.

Thanks,
Travis

tzarpho

unread,
Feb 2, 2008, 12:54:33 PM2/2/08
to
Cross-thread form binding? It's not a sound idea. The usual way to
deal with this is to use Invoke and CallBacks. There's a good example
at: http://www.codeproject.com/KB/miscctrl/progressdialog.aspx

Are there few enough lines for the code to be posted here?

Kofi Sarfo

jehuga...@gmail.com

unread,
Feb 2, 2008, 1:04:38 PM2/2/08
to

I had already written my code to use Invoke, and all my research
points back that way. I just feel like there should be a way of
designing the UI using a business object.

If there was a way to do that without the interface trying to bind to
my class, that would be excellent. It wouldn't be hard reverting back.

Thanks,
Travis

Marc Gravell

unread,
Feb 2, 2008, 5:50:57 PM2/2/08
to
I have previously posted a ThreadedBinding class that will work for single
bindings (TextBox etc)
[http://groups.google.com/group/microsoft.public.dotnet.languages.csharp/browse_thread/thread/69d671cd57a2c7ab/2f078656d6f1ee1f]

However, it won't work for grids. Perhaps your business object would have to
use the sync-context?

I'll see if I can knock an example together...

Marc


Marc Gravell

unread,
Feb 2, 2008, 6:12:08 PM2/2/08
to
Managed it by educating BindingList<T> about sync-context; a "keeper" I
reckon ;-p

Most of this code is demo; it is just the list that you need...

Marc

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
public abstract class EntityBase : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;

protected virtual bool UpdateField<T>(ref T field, T value, string
propertyName) {
if (EqualityComparer<T>.Default.Equals(field, value)) return
false; // no change
field = value;
if (!string.IsNullOrEmpty(propertyName))
OnPropertyChanged(propertyName);
return true;
}
protected virtual void OnPropertyChanged(string propertyName) {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}

public class SomeEntity : EntityBase
{
public SomeEntity() { }
public SomeEntity(string forename, string surname, DateTime
dateOfBirth)
{
Forename = forename;
Surname = surname;
DateOfBirth = dateOfBirth;
}
private string forename, surname;
private DateTime dateOfBirth;

public string Forename {
get { return forename; }
set { UpdateField(ref forename, value, "Forename"); }
}
public string Surname {
get { return surname; }
set { UpdateField(ref surname, value, "Surname"); }
}
public DateTime DateOfBirth {
get { return dateOfBirth; }
set { UpdateField(ref dateOfBirth, value, "DateOfBirth"); }
}
}

static class Program
{
[STAThread]
static void Main()
{
ThreadedBindingList<SomeEntity> data = new
ThreadedBindingList<SomeEntity>();
data.Add(new SomeEntity());
data.Add(new SomeEntity());
data.Add(new SomeEntity());
Application.EnableVisualStyles();
using(Form f = new Form())
using (DataGridView dgv = new DataGridView())
{
dgv.Dock = DockStyle.Fill;
dgv.DataSource = data;
f.Controls.Add(dgv);
f.Load += delegate {

ThreadPool.QueueUserWorkItem(delegate
{
StartRandomEditing(data);
});
};
Application.Run(f);
}
}
static void StartRandomEditing(IList<SomeEntity> data)
{
Random rand = new Random();
while (true)
{
Thread.Sleep(1000);
int index = rand.Next(data.Count);
SomeEntity item = data[index];
switch (rand.Next(3))
{
case 0:
item.Forename += "#";
break;
case 1:
item.Surname += "#";
break;
case 2:
item.DateOfBirth += TimeSpan.FromDays(1);
break;
}
}
}
}

public class ThreadedBindingList<T> : BindingList<T>
{
protected override void OnAddingNew(AddingNewEventArgs e)
{
SynchronizationContext ctx = SynchronizationContext.Current;
if (ctx == null)
{
BaseAddingNew(e);
}
else
{
SynchronizationContext.Current.Send(delegate
{
BaseAddingNew(e);
}, null);
}
}
void BaseAddingNew(AddingNewEventArgs e)
{
base.OnAddingNew(e);
}
protected override void OnListChanged(ListChangedEventArgs e)
{
SynchronizationContext ctx = SynchronizationContext.Current;
if (ctx == null)
{
BaseListChanged(e);
}
else
{
SynchronizationContext.Current.Send(delegate
{
BaseListChanged(e);
}, null);
}
}
void BaseListChanged(ListChangedEventArgs e)
{
base.OnListChanged(e);
}
}
}


Marc Gravell

unread,
Feb 2, 2008, 6:45:14 PM2/2/08
to
Oops; minor optimisation (that I prep'd for, then forgot...):

replace:
SynchronizationContext.Current.Send(
with:
ctx.Send(

Marc


gregschmitz

unread,
Sep 26, 2008, 4:28:04 PM9/26/08
to
Because the thread calling the ThreadedBindingList methods may not be the thread that created the list, you will want to move the SyncronizationContext object out of the methods and into a class variable. This way, the sync context will always be the one that created the object:

public class ThreadedBindingList<T> : BindingList<T>
{

SynchronizationContext ctx = SynchronizationContext.Current;


protected override void OnAddingNew(AddingNewEventArgs e)
{

//SynchronizationContext ctx = SynchronizationContext.Current;


if (ctx == null)
{ BaseAddingNew(e); }
else

{ ctx.Send(delegate { BaseAddingNew(e); }, null); }
}

void BaseAddingNew(AddingNewEventArgs e)
{ base.OnAddingNew(e); }

protected override void OnListChanged(ListChangedEventArgs e)
{

//SynchronizationContext ctx = SynchronizationContext.Current;


if (ctx == null)
{ BaseListChanged(e); }
else

{ ctx.Send(delegate { BaseListChanged(e); }, null); }
}

void BaseListChanged(ListChangedEventArgs e)
{ base.OnListChanged(e); }
}

0 new messages