I was wondering if somebody explain me what is wrong in my code.
I run timer in extra thread. In timer function I work with EF context. I see that timer function has worked 6 times and I especially set interval in 3 seconds and take only 100 rows but in my DB I see only one work.
So where is my error?
namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
private static int cnt = 0;
private Thread thread;
public Form1()
{
InitializeComponent();
}
private void StartThread()
{
var timer = new System.Timers.Timer();
timer.Elapsed += new System.Timers.ElapsedEventHandler(ProcessDb);
timer.Interval = 3000;
timer.Start();
}
public void ProcessDb(object sender, System.Timers.ElapsedEventArgs e)
{
cnt++;
ConnService serv = new ConnService();
serv.UpdateConnections(cnt);
}
private void button1_Click(object sender, EventArgs e)
{
thread = new Thread(StartThread);
thread.Start();
Thread.Sleep(20000);
}
}
public class MyqQueue
{
public static Stack<int> myStack = new Stack<int>();
public static Stack<int> myStack2 = new Stack<int>();
}
}
namespace WindowsFormsApplication2
{
class ConnService
{
public ConnService()
{
cnt++;
}
private static int cnt;
public void UpdateConnections(int second)
{
MyqQueue.myStack.Push(second);
DjEntities ctx = new DjEntities();
var entities = ctx.Connections.Take(100).Where(c => c.State == null);
foreach (var connection in entities)
{
connection.State = second;
}
if (second == 1)
Thread.Sleep(7000);
MyqQueue.myStack2.Push(second);
ctx.SaveChanges();
}
}
}
ctx.Connections.Take(100).Where(c => c.State == null)
Should be changed to
ctx.Connections.Where(c => c.State == null).Take(100)
Your first query translates to first take 100 without filtering and then apply filter.
Second query I have written will take items filtered and then take top 100.
Related
I am trying to add a batch of nodes to a TreeView asynchronously, in 50ms time intervals, but I am getting an exception that says the collection has been modified. How can I prevent this exception from occurring?
Collection was modified; enumeration operation may not execute.
public partial class Form1 : Form
{
private SynchronizationContext synchronizationContext;
private DateTime previousTime = DateTime.Now;
private List<int> _batch = new List<int>();
public Form1()
{
InitializeComponent();
}
private async void button1_Click(object sender, EventArgs e)
{
synchronizationContext = SynchronizationContext.Current;
await Task.Run(() =>
{
for (var i = 0; i <= 5000000; i++)
{
_batch.Add(i);
UpdateUI(i);
}
});
}
public void UpdateUI(int value)
{
var timeNow = DateTime.Now;
if ((DateTime.Now - previousTime).Milliseconds <= 50) return;
synchronizationContext.Post(new SendOrPostCallback(o =>
{
foreach (var item in _batch)
{
TreeNode newNode = new TreeNode($"{item}");
treeView1.Nodes.Add(newNode);
}
}), null);
_batch.Clear();
previousTime = timeNow;
}
}
List<T> is not thread safe. You are trying to modify the list while you are trying to read from it in your foreach.
There are a few ways to solve this.
Use ConcurrentQueue
Change:
private List<int> _batch = new List<int>();
To:
var _batch = new ConcurrentQueue<int>();
You can add items by calling:
_batch.Enqueue(1);
Then you can grab them all out and add them in a thread-safe manner:
while(_batch.TryDequeue(out var n))
{
// ...
}
Use List, but with Thread-synchronization Objects
Create a new object like so:
private readonly _listLock = new object();
Then add these helper methods:
private void AddToBatch(int value)
{
lock(_listLock)
{
_batch.Add(value);
}
}
private int[] GetAllItemsAndClear()
{
lock(_listLock)
{
try
{
return _batch.ToArray();
}
finally
{
_batch.Clear();
}
}
}
This ensures only one thread at a time is operating on the List object.
There are other ways to do this as well, but this is the gist of the problem. Your code won't be as fast because of the overhead of synchronizing data, but you won't crash.
I'm searching for this for several hours now and was not able to find proper solution. I'm c# beginner.
I have a winforms app with a ListBox and a class that does some work and should run forever on separate thread. I want to push MyDataStruct to ListBox each time its created in WorkerClass.Work.
Later on, several WorkerClass instances should run simultaneously and I will have combobox to pick which instance data to feed to ListBox . Is it better to have WorkerClas return only single MyDataStruct and keep their queue in Form1 class or have a queue in each WorkerClass and exchange the entire queue with Form1 every time it changes?
is my void QueueToLb good way to add queue data to ListBox ?
thank you for your support.
public partial class Form1 : Form
{
Queue<MyDataStruct> qList;
MyDataStruct myDataStruct;
private void RunTask()
{
//how do I make MyLongTask to update either qList or myDataStuct
Task.Run(() =>
{
MyLongTask(0, 1000);
});
}
private void MyLongTask(int low, int high)
{
WorkerClass wc = new WorkerClass();
wc.Work(low,high);
}
private void QueueToLb()
{
//is this good way to update listbox from queue?
List<MyDataStruct> lstMds = qList.Reverse<MyDataStruct>().ToList<MyDataStruct>();
List<string> lstStr = new List<string>();
foreach (MyDataStruct m in lstMds)
{
lstStr.Add(m.ToString());
}
listBox1.DataSource = lstStr;
}
}
public class WorkerClass
{
Queue<MyDataStruct> qList; //not sure if its better to keep the queue here or in Form1
public WorkerClass()
{
qList = new Queue<MyDataStruct>();
}
public void Work(int low, int high) //does some work forever
{
while (true)
{
if (qList.Count > 11) qList.Dequeue();
MyDataStruct mds = new MyDataStruct();
Random random = new Random();
mds.dt = DateTime.Now;
mds.num = random.Next(low, high);
qList.Enqueue(mds);
Thread.Sleep(1000);
}
}
}
public class MyDataStruct
{
public DateTime dt;
public int num;
public override string ToString()
{
StringBuilder s = new StringBuilder();
s.Append(num.ToString());
s.Append(" - ");
s.Append(dt.ToShortDateString());
return s.ToString();
}
}
OK I think I figured how to use BackgroundWorker on this, I'll be happy if someone could verify it is correct
public partial class Form1 : Form
{
Queue<MyDataStruct> qList;
BackgroundWorker bw = new BackgroundWorker();
public Form1()
{
InitializeComponent();
bw.WorkerReportsProgress = true;
bw.DoWork += new DoWorkEventHandler(Bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
}
private void Form1_Load(object sender, EventArgs e)
{
qList = new Queue<MyDataStruct>(12);
}
private void button1_Click(object sender, EventArgs e)
{
bw.RunWorkerAsync();
}
private void MyLongTask(int low = 0, int high = 1000)
{
WorkerClass wc = new WorkerClass(bw);
wc.Work(low,high);
}
private void BindToLbWithQueue()
{
MyDataStruct mds = new MyDataStruct();
Random random = new Random();
mds.dt = DateTime.Now;
mds.num = random.Next(0, 1000);
qList.Enqueue(mds);
QueueToLb();
}
private void QueueToLb()
{
//is this good way to update listbox from queue?
List<MyDataStruct> lstMds = qList.Reverse<MyDataStruct>().ToList<MyDataStruct>();
List<string> lstStr = new List<string>();
foreach (MyDataStruct m in lstMds)
{
lstStr.Add(m.ToString());
}
listBox1.DataSource = lstStr;
}
#region worker
private void Bw_DoWork(object sender, DoWorkEventArgs e)
{
MyLongTask();
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
qList = (Queue<MyDataStruct>)e.UserState;
QueueToLb();
}
#endregion
}
public class WorkerClass
{
Queue<MyDataStruct> qList; //not sure if its better to keep the queue here or in Form1
BackgroundWorker bw = null;
public WorkerClass(BackgroundWorker bw)
{
this.bw = bw;
qList = new Queue<MyDataStruct>();
}
public void Work(int low, int high) //does some work forever
{
while (true)
{
if (qList.Count > 11) qList.Dequeue();
MyDataStruct mds = new MyDataStruct();
Random random = new Random();
mds.dt = DateTime.Now;
mds.num = random.Next(low, high);
qList.Enqueue(mds);
bw.ReportProgress(0, qList);
Thread.Sleep(1000);
}
}
}
public class MyDataStruct
{
public DateTime dt;
public int num;
public override string ToString()
{
StringBuilder s = new StringBuilder();
s.Append(num.ToString());
s.Append(" - ");
s.Append(dt.ToShortDateString());
return s.ToString();
}
}
I am calculating prime numbers bw two numbers using following code
private static IEnumerable<int> GetPrimes(int from, int to)
{
for (int i = from; i <= to; i++)
{
bool isPrime = true;
int limit = (int)Math.Sqrt(i);
for (int j = 2; j <= limit; j++)
if (i % j == 0)
{
isPrime = false;
break;
}
if (isPrime)
{
yield return i;
}
}
}
And I want to update my list box without blocking my UI thread, with all the prime numbers using above code. The approch which I am using as following but this is not working out.
public MainWindow()
{
InitializeComponent();
_worker = new BackgroundWorker();
_worker.DoWork += _worker_DoWork;
this.DataContext = this;
}
private void _worker_DoWork(object sender, DoWorkEventArgs e)
{
PrimeNumbers = new ObservableCollection<int>();
foreach (var item in GetPrimes(1, 10000000))
{
Dispatcher.BeginInvoke(new Action<int>(Test), item);
}
}
private void Test(int obj)
{
PrimeNumbers.Add(obj);
}
public ObservableCollection<int> PrimeNumbers
{
get
{
return primeNumbers;
}
set
{
primeNumbers = value;
OnPropertyChanged("PrimeNumbers");
}
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
_worker.RunWorkerAsync();
}
but this approach freezes my UI. I want to have result continuously coming from the GetPrimes method and keep adding to my listboz
You are just posting too much. This code works as expected:
public partial class MainWindow
{
public ObservableCollection<int> PrimeNumbers { get; set; }
public MainWindow()
{
InitializeComponent();
PrimeNumbers = new ObservableCollection<int>();
}
private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
{
PrintPrimes(PrimeNumbers.Add, 1, 10000000, SynchronizationContext.Current);
}
private static void PrintPrimes(Action<int> action, int from, int to,
SynchronizationContext syncContext)
{
Task.Run(() =>
{
for (var i = from; i <= to; i++)
{
var isPrime = true;
var limit = (int) Math.Sqrt(i);
for (var j = 2; j <= limit; j++)
{
if (i%j == 0)
{
isPrime = false;
break;
}
}
if (isPrime)
{
syncContext.Post(state => action((int)state), i);
Thread.Sleep(1);
}
}
});
}
}
Consider avoiding old BackgroundWorker class. Also, instead of using a synchronization mechanism of your platform try to switch to platform independent SynchronizationContext.
Instead of sleeping a thread you can post your results in bunches:
public partial class MainWindow
{
public ObservableCollection<int> PrimeNumbers { get; set; }
public MainWindow()
{
InitializeComponent();
PrimeNumbers = new ObservableCollection<int>();
}
private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
{
PrintPrimes(items => items.ForEach(PrimeNumbers.Add),
1, 10000000, SynchronizationContext.Current);
}
private static void PrintPrimes(Action<List<int>> action, int from, int to,
SynchronizationContext syncContext)
{
Task.Run(() =>
{
var primesBuffer = new List<int>();
for (var i = from; i <= to; i++)
{
var isPrime = true;
var limit = (int) Math.Sqrt(i);
for (var j = 2; j <= limit; j++)
{
if (i%j == 0)
{
isPrime = false;
break;
}
}
if (isPrime)
{
primesBuffer.Add(i);
if (primesBuffer.Count >= 1000)
{
syncContext.Post(state => action((List<int>) state),
primesBuffer.ToList());
primesBuffer.Clear();
}
}
}
});
}
}
You can use Thread instead of Task.Run if you're stuck with older versions of a framework.
That code looks ok, you just forgot to start your background worker by calling BackgroundWorker.RunWorkerAsync.
PrimeNumbers = new ObservableCollection<int>();
This line needs to be outside your worker (or being invoked in the UI thread as well).
It seems like my UI was getting upadated very frequently so I have used a delay of 1 second on my background worker thread. It helped me to achieve my functionality
private void _worker_DoWork(object sender, DoWorkEventArgs e)
{
foreach (var item in GetPrimes(1, 1000000))
{
Thread.Sleep(1000);
Dispatcher.BeginInvoke(new Action<int>(Test), item);
}
}
I'm coding a class to move and copy files. I'm raising events when the current file progress and the total progress changes. When I test the code on my XP machine, it works fine, but when I run it on my Windows 7 64-Bit machine, the current progress doesn't update the UI correctly. The current progress ProgressBar only gets half way then starts on the next file which does the same. The total progress ProgressBar updates fine. Any ideas why this is happening?
EDIT: The Windows 7 machine is running a quad-core and the XP is running a dual-core. Not sure if that might be what's making a difference. I'm only a hobbyist so excuse my ignorance :)
EDIT: Code added (Background)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Threading;
using System.Timers;
using Timer = System.Timers.Timer;
namespace nGenSolutions.IO
{
public class FileTransporter
{
#region Delegates
public delegate void CurrentFileChangedEventHandler(string fileName);
public delegate void CurrentProgressChangedEventHandler(int percentComplete);
public delegate void CurrentWriteSpeedUpdatedEventHandler(long bytesPerSecond);
public delegate void TotalProgressChangedEventHandler(int percentComplete);
public delegate void TransportCompleteEventHandler(FileTransportResult result);
#endregion
private readonly List<string> _destinationFiles = new List<string>();
private readonly List<string> _sourceFiles = new List<string>();
private long _bytesCopiedSinceInterval;
private FileTransportResult _result;
private Timer _speedTimer;
private long _totalDataLength;
private BackgroundWorker _worker;
public bool TransportInProgress { get; private set; }
public event CurrentFileChangedEventHandler CurrentFileChanged;
public event CurrentProgressChangedEventHandler CurrentProgressChanged;
public event CurrentWriteSpeedUpdatedEventHandler CurrentWriteSpeedUpdated;
public event TotalProgressChangedEventHandler TotalProgressChanged;
public event TransportCompleteEventHandler TransportComplete;
public void AddFile(string sourceFile, string destinationFile)
{
if (!File.Exists(sourceFile))
throw new FileNotFoundException("The specified file does not exist!", sourceFile);
var fileInfo = new FileInfo(sourceFile);
_totalDataLength += fileInfo.Length;
_sourceFiles.Add(sourceFile);
_destinationFiles.Add(destinationFile);
}
public void BeginTransport()
{
// update the write speed every 3 seconds
_speedTimer = new Timer {Interval = 3000};
_speedTimer.Elapsed += SpeedTimerElapsed;
_worker = new BackgroundWorker();
_worker.DoWork += DoTransport;
_worker.RunWorkerCompleted += WorkerCompleted;
_worker.RunWorkerAsync();
_speedTimer.Start();
TransportInProgress = true;
}
private void SpeedTimerElapsed(object sender, ElapsedEventArgs e)
{
InvokeCurrentSpeedUpdated(_bytesCopiedSinceInterval);
_bytesCopiedSinceInterval = 0;
}
private void WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
TransportInProgress = false;
InvokeTransportComplete(_result);
}
public void CancelTransport(bool rollbackChanges)
{
if (TransportInProgress == false)
throw new InvalidOperationException("You tried to stop the transport before you started it!");
_result = FileTransportResult.Cancelled;
_worker.CancelAsync();
while (_worker.IsBusy)
{
// wait for worker to die an 'orrible death
}
// TODO: rollback changes if requested
}
private void DoTransport(object sender, DoWorkEventArgs e)
{
long totalBytesCopied = 0;
int totalPercentComplete = 0;
for (int i = 0; i < _sourceFiles.Count; i++)
{
string sourceFile = _sourceFiles[i];
string destinationFile = _destinationFiles[i];
long currentFileLength = new FileInfo(sourceFile).Length;
InvokeCurrentFileChanged(sourceFile);
using (var sourceStream = new FileStream(sourceFile, FileMode.Open, FileAccess.Read))
{
using (var destinationStream = new FileStream(destinationFile, FileMode.Create, FileAccess.Write))
{
using (var reader = new BinaryReader(sourceStream))
{
using (var writer = new BinaryWriter(destinationStream))
{
int lastPercentComplete = 0;
for (int j = 0; j < currentFileLength; j++)
{
writer.Write(reader.ReadByte());
totalBytesCopied += 1;
_bytesCopiedSinceInterval += 1;
int current = Convert.ToInt32((j/(double) currentFileLength)*100);
int total = Convert.ToInt32((totalBytesCopied/(double) _totalDataLength)*100);
// raise progress events every 3%
if (current%3 == 0)
{
// only raise the event if the progress has increased
if (current > lastPercentComplete)
{
lastPercentComplete = current;
InvokeCurrentProgressChanged(lastPercentComplete);
}
}
if (total%3 == 0)
{
// only raise the event if the progress has increased
if (total > totalPercentComplete)
{
totalPercentComplete = total;
InvokeTotalProgressChanged(totalPercentComplete);
}
}
}
}
InvokeCurrentProgressChanged(100);
}
}
}
}
InvokeTotalProgressChanged(100);
}
private void InvokeCurrentFileChanged(string fileName)
{
CurrentFileChangedEventHandler handler = CurrentFileChanged;
if (handler == null) return;
handler(fileName);
}
private void InvokeCurrentProgressChanged(int percentComplete)
{
CurrentProgressChangedEventHandler handler = CurrentProgressChanged;
if (handler == null) return;
handler(percentComplete);
}
private void InvokeCurrentSpeedUpdated(long bytesPerSecond)
{
CurrentWriteSpeedUpdatedEventHandler handler = CurrentWriteSpeedUpdated;
if (handler == null) return;
handler(bytesPerSecond);
}
private void InvokeTotalProgressChanged(int percentComplete)
{
TotalProgressChangedEventHandler handler = TotalProgressChanged;
if (handler == null) return;
handler(percentComplete);
}
private void InvokeTransportComplete(FileTransportResult result)
{
TransportCompleteEventHandler handler = TransportComplete;
if (handler == null) return;
handler(result);
}
}
}
EDIT: Code added (GUI)
using System;
using System.IO;
using System.Windows.Forms;
using ExtensionMethods;
using nGenSolutions.IO;
namespace TestApplication
{
public partial class ProgressForm : Form
{
public ProgressForm()
{
InitializeComponent();
}
private void ProgressForm_Load(object sender, EventArgs e)
{
var transporter = new FileTransporter();
foreach (string fileName in Directory.GetFiles("C:\\Temp\\"))
{
transporter.AddFile(fileName, "C:\\" + Path.GetFileName(fileName));
}
transporter.CurrentFileChanged += transporter_CurrentFileChanged;
transporter.CurrentProgressChanged += transporter_CurrentProgressChanged;
transporter.TotalProgressChanged += transporter_TotalProgressChanged;
transporter.CurrentWriteSpeedUpdated += transporter_CurrentWriteSpeedUpdated;
transporter.TransportComplete += transporter_TransportComplete;
transporter.BeginTransport();
}
void transporter_TransportComplete(FileTransportResult result)
{
Close();
}
void transporter_CurrentWriteSpeedUpdated(long bytesPerSecond)
{
double megaBytesPerSecond = (double)bytesPerSecond/1024000;
currentSpeedLabel.SafeInvoke(x=> x.Text = string.Format("Transfer speed: {0:0.0} MB/s", megaBytesPerSecond));
}
private void transporter_TotalProgressChanged(int percentComplete)
{
totalProgressBar.SafeInvoke(x => x.Value = percentComplete);
}
private void transporter_CurrentProgressChanged(int percentComplete)
{
currentProgressBar.SafeInvoke(x => x.Value = percentComplete);
}
private void transporter_CurrentFileChanged(string fileName)
{
this.SafeInvoke(x => x.Text = string.Format("Current file: {0}", fileName));
}
}
}
EDIT: SafeInvoke code added
public static void SafeInvoke<T>(this T #this, Action<T> action) where T : Control
{
if (#this.InvokeRequired)
{
#this.Invoke(action, new object[] {#this});
}
else
{
if (!#this.IsHandleCreated) return;
if (#this.IsDisposed)
throw new ObjectDisposedException("#this is disposed.");
action(#this);
}
}
Well, if transporter_CurrentProgressChanged gets correct values, the program works properly. You can try to add some minimal Thread.Sleep call to InvokeCurrentProgressChanged (maybe with 0 parameter) when progress value is 100%, to get UI chance to update itself, but in this case you reduce the program performance. It is possibly better to leave the program unchanged, since it works as expected, and main progress bar is updated.
I have a ListBox that is bound to a BindingList. The BindingList is built up when a third party application raises an event. I can see the BindingList being bound correctly... but nothing enters the ListBox. I have used the exact same logic with some of my own custom types and it usually works very well.
Form class
private Facade.ControlFacade _controlFacade;
public UavControlForm()
{
InitializeComponent();
_controlFacade = new UavController.Facade.ControlFacade();
UpdateEntityListBox();
}
private void UpdateEntityListBox()
{
lsbEntities.DataSource = _controlFacade.GetEntityTally();
lsbEntities.DisplayMember = "InstanceName";
}
Facade class
private Scenario _scenario;
public ControlFacade()
{
_scenario = new Scenario();
}
public BindingList<AgStkObject> GetEntityTally()
{
BindingList<AgStkObject> entityTally = _scenario.EntityTally;
return entityTally;
}
Scenario class
private static BindingList<IAgStkObject> _entityTally = new BindingList<AgStkObject>();
public Scenario()
{
if (UtilStk.CheckThatStkIsAvailable())
{
UtilStk.StkRoot.OnStkObjectAdded += new IAgStkObjectRootEvents_OnStkObjectAddedEventHandler(TallyScenarioObjects);
UtilStk.StkRoot.OnStkObjectDeleted += new IAgStkObjectRootEvents_OnStkObjectDeletedEventHandler(TallyScenarioObjects);
}
}
private void TallyScenarioObjects(object sender)
{
List<AgStkObject> tallyOfStkObjects = UtilStk.GetRunningTallyOfAllStkObjects();
List<string> stkObjectNames = UtilStk.GetInstanceNamesOfStkObjects(tallyOfStkObjects);
foreach (string stkObjectName in stkObjectNames)
{
if (!SearchFlightUavTallyByName(stkObjectName))
{
if (!SearchLoiterUavTallyByName(stkObjectName))
{
if (!SearchEntityTallyByName(stkObjectName))
{
int i = stkObjectNames.IndexOf(stkObjectName);
_entityTally.Add(tallyOfStkObjects[i]);
}
}
}
}
}
I can see the event fire from the third-party application - this adds an entity to _entityList as desired, but noothing is added to lsbEntities - why?
(jump right to the last example if you want to see it fixed etc)
Threads and "observer" patterns (such as the data-binding on winforms) are rarely good friends. You could try replacing your BindingList<T> usage with the ThreadedBindingList<T> code I used on a previous answer - but this combination of threads and UI is not an intentional use-case of winforms data-binding.
The listbox itself should support binding via list notification events (IBindingList / IBindingListView), as long as they arrive form the right thread. ThreadedBindingList<T> attempts to fix this by thread-switching on your behalf. Note that for this to work you must create the ThreadedBindingList<T> from the UI thread, after it has a sync-context, i.e. after it has started displaying forms.
To illustrate the point that listbox does respect list-change notifications (when dealing with a single thread):
using System;
using System.ComponentModel;
using System.Windows.Forms;
class Foo
{
public int Value { get; set; }
public Foo(int value) { Value = value; }
public override string ToString() { return Value.ToString(); }
}
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
using(var form = new Form())
using (var lst = new ListBox())
using (var timer = new Timer())
{
var data = new BindingList<Foo>();
form.Controls.Add(lst);
lst.DataSource = data;
timer.Interval = 1000;
int i = 0;
timer.Tick += delegate
{
data.Add(new Foo(i++));
};
lst.Dock = DockStyle.Fill;
form.Shown += delegate
{
timer.Start();
};
Application.Run(form);
}
}
}
and now with added threading / ThreadedBindingList<T> (it doesn't work with the regular BindingList<T>):
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
class Foo
{
public int Value { get; set; }
public Foo(int value) { Value = value; }
public override string ToString() { return Value.ToString(); }
}
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
using(var form = new Form())
using (var lst = new ListBox())
{
form.Controls.Add(lst);
lst.Dock = DockStyle.Fill;
form.Shown += delegate
{
BindingList<Foo> data = new ThreadedBindingList<Foo>();
lst.DataSource = data;
ThreadPool.QueueUserWorkItem(delegate
{
int i = 0;
while (true)
{
data.Add(new Foo(i++));
Thread.Sleep(1000);
}
});
};
Application.Run(form);
}
}
}
public class ThreadedBindingList<T> : BindingList<T>
{
private readonly SynchronizationContext ctx;
public ThreadedBindingList()
{
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)
{
if (ctx == null)
{
BaseListChanged(e);
}
else
{
ctx.Send(delegate
{
BaseListChanged(e);
}, null);
}
}
void BaseListChanged(ListChangedEventArgs e)
{
base.OnListChanged(e);
}
}