DataBinding and threading

G

Guest

I have an interesting scenario involving CF 2.0, data sources, threading and
the UI thread. I might be doing this completely wrong.

** The current state:

I have a datasource class that wraps a DataSet - which looks something like
this:

public class myDataSource : IListSource
{
DataTable dt = new DataTable();

public void Update()
{
...
dt.Rows.Add(...);
...
}

public IList IListSource.GetList()
{
return ((IListSource)dt).GetList();
}
}

and this class is bound to a DataGridView:
MyDataSource src = new MyDataSource();
this.dataGridView.DataSource = src;

void Button_Click(object sender, EventArgs e)
{
((MyDataSource)this.dataGridView.DataSource)src).Update();
}

Calling Update() works correctly in a Button_Click handler for example (as
long as it's called from the UI thread) and the databinding logic (through
IBindingList) works fine. When the button is clicked the DataTable changes
and the GridView updates itself automatically.

My problem is that I need to be able to call it on a timer (using a
different thread):

TimerCallback callback = new
TimerCallback(((MyDataSource)(this.dataGridView.DataSource)).Update);
System.Threading.Timer timer = new System.Threading.Timer(callback, null,
2*1000, Timeout.Infinite);

[Note: The example above is just academic, the callback will be calling the
update method through a global singleton instance of the MyDataSource]

When this version of Update() is called the DataTable gets updated but the
DataGridView is not updated/redrawn (probably because the databinding stuff
now happens on a different thread from the UI-thread). When the DataGridView
is invalidated it redraws itself with the newly added rows.

All the examples of this suggest using the Invoke method of the DataGridView
(or any other control in the UI-thread basically). But neither the
MyDataSource or the Timer knows about any UI-controls (which are in a
separate dll).

The MyDataSource.Update() method needs to be able to sync itself to the
UI-thread in order for the DataBinding to work correctly. But how does it go
around doing that? The MyDataSource could be bound to more than one control.

** Random thoughts:

Doing (new Form()).Invoke() doesn't seem to work.

Using a class that inherits IBindingList directly works across threads.
There might be a problem with the wrapping-datatable-class-solution itself.

The Update() method makes an HttpRequest call which returns a DataTable and
then does dt.Merge(newDataTable). This works fine when called on the
UI-thread.

Having a "public static Form mainForm" variable in the Program class to use
for Program.mainForm.Invoke is not a nice way of solving this. Also the dll
containing the myDataSource would be too coupled with the actual application.


** The problem (if anyone has a better way of achieving the desired result):

I have a WebService that returns a DataTable (DataSet with one DataTable
actually). This data is to be displayed in a DataGridView. The data needs to
be updated periodically.

What I'm trying to do is use the DataBinding mechanism in the .NET framework
to solve this for me. The idea was to bind the DataGridView to a DataTable
and then update the DataTable. The DataBinding mechanism was supposed to take
care of the rest and cause the DataGridView to update accordingly.

This completely brakes down when threads are involved as the DataBinding
stuff needs to happen on the UI-thread for the DataGridView to properly
redraw itself.

I thought this would be the most elegant way of doing this. Just do:
myDataGridView.DataSource = myDataSource;
and the rest would happen autmagically :)


Any comments or suggestions are welcome!

With regards,
 
G

Guest

Create an event in your Data class that notifies that an event occured that
requires an update. Wire up the event in your ui. When the event fires,
check your form for Form.InvokeRequired and if this is true, then invoke the
update on the form from the Form.Invoke method, otherwise call the update
method directly.

Rick D.
Contractor

Snorrk said:
I have an interesting scenario involving CF 2.0, data sources, threading and
the UI thread. I might be doing this completely wrong.

** The current state:

I have a datasource class that wraps a DataSet - which looks something like
this:

public class myDataSource : IListSource
{
DataTable dt = new DataTable();

public void Update()
{
...
dt.Rows.Add(...);
...
}

public IList IListSource.GetList()
{
return ((IListSource)dt).GetList();
}
}

and this class is bound to a DataGridView:
MyDataSource src = new MyDataSource();
this.dataGridView.DataSource = src;

void Button_Click(object sender, EventArgs e)
{
((MyDataSource)this.dataGridView.DataSource)src).Update();
}

Calling Update() works correctly in a Button_Click handler for example (as
long as it's called from the UI thread) and the databinding logic (through
IBindingList) works fine. When the button is clicked the DataTable changes
and the GridView updates itself automatically.

My problem is that I need to be able to call it on a timer (using a
different thread):

TimerCallback callback = new
TimerCallback(((MyDataSource)(this.dataGridView.DataSource)).Update);
System.Threading.Timer timer = new System.Threading.Timer(callback, null,
2*1000, Timeout.Infinite);

[Note: The example above is just academic, the callback will be calling the
update method through a global singleton instance of the MyDataSource]

When this version of Update() is called the DataTable gets updated but the
DataGridView is not updated/redrawn (probably because the databinding stuff
now happens on a different thread from the UI-thread). When the DataGridView
is invalidated it redraws itself with the newly added rows.

All the examples of this suggest using the Invoke method of the DataGridView
(or any other control in the UI-thread basically). But neither the
MyDataSource or the Timer knows about any UI-controls (which are in a
separate dll).

The MyDataSource.Update() method needs to be able to sync itself to the
UI-thread in order for the DataBinding to work correctly. But how does it go
around doing that? The MyDataSource could be bound to more than one control.

** Random thoughts:

Doing (new Form()).Invoke() doesn't seem to work.

Using a class that inherits IBindingList directly works across threads.
There might be a problem with the wrapping-datatable-class-solution itself.

The Update() method makes an HttpRequest call which returns a DataTable and
then does dt.Merge(newDataTable). This works fine when called on the
UI-thread.

Having a "public static Form mainForm" variable in the Program class to use
for Program.mainForm.Invoke is not a nice way of solving this. Also the dll
containing the myDataSource would be too coupled with the actual application.


** The problem (if anyone has a better way of achieving the desired result):

I have a WebService that returns a DataTable (DataSet with one DataTable
actually). This data is to be displayed in a DataGridView. The data needs to
be updated periodically.

What I'm trying to do is use the DataBinding mechanism in the .NET framework
to solve this for me. The idea was to bind the DataGridView to a DataTable
and then update the DataTable. The DataBinding mechanism was supposed to take
care of the rest and cause the DataGridView to update accordingly.

This completely brakes down when threads are involved as the DataBinding
stuff needs to happen on the UI-thread for the DataGridView to properly
redraw itself.

I thought this would be the most elegant way of doing this. Just do:
myDataGridView.DataSource = myDataSource;
and the rest would happen autmagically :)


Any comments or suggestions are welcome!

With regards,
 
G

Guest

That is what I did first and it works quite nicely.

With the mess above I was trying to use the binding notification mechanism
built into the .NET framework to skip the event registration part. But it
really isn't as easy as I first thought..

dbgrick said:
Create an event in your Data class that notifies that an event occured that
requires an update. Wire up the event in your ui. When the event fires,
check your form for Form.InvokeRequired and if this is true, then invoke the
update on the form from the Form.Invoke method, otherwise call the update
method directly.

Rick D.
Contractor

Snorrk said:
I have an interesting scenario involving CF 2.0, data sources, threading and
the UI thread. I might be doing this completely wrong.

** The current state:

I have a datasource class that wraps a DataSet - which looks something like
this:

public class myDataSource : IListSource
{
DataTable dt = new DataTable();

public void Update()
{
...
dt.Rows.Add(...);
...
}

public IList IListSource.GetList()
{
return ((IListSource)dt).GetList();
}
}

and this class is bound to a DataGridView:
MyDataSource src = new MyDataSource();
this.dataGridView.DataSource = src;

void Button_Click(object sender, EventArgs e)
{
((MyDataSource)this.dataGridView.DataSource)src).Update();
}

Calling Update() works correctly in a Button_Click handler for example (as
long as it's called from the UI thread) and the databinding logic (through
IBindingList) works fine. When the button is clicked the DataTable changes
and the GridView updates itself automatically.

My problem is that I need to be able to call it on a timer (using a
different thread):

TimerCallback callback = new
TimerCallback(((MyDataSource)(this.dataGridView.DataSource)).Update);
System.Threading.Timer timer = new System.Threading.Timer(callback, null,
2*1000, Timeout.Infinite);

[Note: The example above is just academic, the callback will be calling the
update method through a global singleton instance of the MyDataSource]

When this version of Update() is called the DataTable gets updated but the
DataGridView is not updated/redrawn (probably because the databinding stuff
now happens on a different thread from the UI-thread). When the DataGridView
is invalidated it redraws itself with the newly added rows.

All the examples of this suggest using the Invoke method of the DataGridView
(or any other control in the UI-thread basically). But neither the
MyDataSource or the Timer knows about any UI-controls (which are in a
separate dll).

The MyDataSource.Update() method needs to be able to sync itself to the
UI-thread in order for the DataBinding to work correctly. But how does it go
around doing that? The MyDataSource could be bound to more than one control.

** Random thoughts:

Doing (new Form()).Invoke() doesn't seem to work.

Using a class that inherits IBindingList directly works across threads.
There might be a problem with the wrapping-datatable-class-solution itself.

The Update() method makes an HttpRequest call which returns a DataTable and
then does dt.Merge(newDataTable). This works fine when called on the
UI-thread.

Having a "public static Form mainForm" variable in the Program class to use
for Program.mainForm.Invoke is not a nice way of solving this. Also the dll
containing the myDataSource would be too coupled with the actual application.


** The problem (if anyone has a better way of achieving the desired result):

I have a WebService that returns a DataTable (DataSet with one DataTable
actually). This data is to be displayed in a DataGridView. The data needs to
be updated periodically.

What I'm trying to do is use the DataBinding mechanism in the .NET framework
to solve this for me. The idea was to bind the DataGridView to a DataTable
and then update the DataTable. The DataBinding mechanism was supposed to take
care of the rest and cause the DataGridView to update accordingly.

This completely brakes down when threads are involved as the DataBinding
stuff needs to happen on the UI-thread for the DataGridView to properly
redraw itself.

I thought this would be the most elegant way of doing this. Just do:
myDataGridView.DataSource = myDataSource;
and the rest would happen autmagically :)


Any comments or suggestions are welcome!

With regards,
 
G

Guest

Also - the notification mechanism allows for the datasource to tell the
consumer which rows/properties have changed and therefore the consumer (e.g.
DataGridView) doesn't need to recalculate and redraw everything.

In the event-driven scenario the consumer would have to rebind on every
update and on low-powered mobile devices that could get costly if the updates
are frequent.
 
G

Guest

You could create a custom event argument containing the changed data, create
a delegate using that event argument and then an event from that delegate.
This would allow your data class to notify the client of changes, and not
require the client to refresh the entire form.

Rick D.
Contractor
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top