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
Are there few enough lines for the code to be posted here?
Kofi Sarfo
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
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
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);
}
}
}
replace:
SynchronizationContext.Current.Send(
with:
ctx.Send(
Marc
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); }
}