Loading two forms simultaneously

A

A.Weinman

Hello all,

I have an application that has multiple forms, only 3 of which matter
for this issue. There is a main form that starts invisible and does
nothing more than start other forms and link them all together. I
also have a Login form and a View form, both started by the main
form. My goal is to have the Login form pop up as soon as the
application is started, and at the same time have the View form start
loading data, but stay invisible. I have tried this is a number of
ways, but the only one I have found that does not delay the appearance
of the Login form is to start the data-loading in a new thread from
the constructor of the View form. However, if the person logs in
before the data-loading has finished, I get an error, "Action being
performed on this control is being called from the wrong thread."
where I add a node to my tree that holds the information.

Is there a way I can check the completion of the thread before I
attempt to make the View form visible, or is there a better way to go
about this? Thanks in advance for your help.

~Andy
 
G

Guest

i cant understand why for data loading you need a form, also in case you
donign this, think on a controller which wil l sync all the calls together so
in case the login is not completed since the event arised by the data
completetion process is fired.

Using events (help to sync between the process) and MVC pattern will solve
the issue, oyou must think then async process may delay or interfere UI
stability unless you will concern the UI to suit to t he async threasds.

Sincerely
Yaron Karni
http://dotnetbible.blogspot.com/
 
W

william.w.oneill

However, if the person logs in
before the data-loading has finished, I get an error, "Action being
performed on this control is being called from the wrong thread."
where I add a node to my tree that holds the information.

You can only modify a control using the same thread used to construct
the control. See InvokeRequired() and Control.Invoke().
Is there a way I can check the completion of the thread before I
attempt to make the View form visible,

See Thread.Join() or thread synchronization objects such as
ManualResetEvent/AutoResetEvent.
or is there a better way to go
about this?

Yaron already mentioned this, but using the Model-View-Controller
(MVC) design pattern would help. You would construct the model (your
View form data) using a thread. Once that thread has completed, you
would bind the data to the UI.

Good luck!
 
P

Peter Duniho

On Aug 27, 6:20 am, (e-mail address removed) wrote:

[...]
or is there a better way to go
about this?

Yaron already mentioned this, but using the Model-View-Controller
(MVC) design pattern would help. You would construct the model (your
View form data) using a thread. Once that thread has completed, you
would bind the data to the UI.

Along those lines, you may find this recent thread useful:

http://groups.google.com/group/micr...csharp/browse_thread/thread/97406ffa8c9e035d/

In particular, it explains how you can implement a controller class
without making it a form that is always invisible (as it sounds like you
are doing here).

As far as the cross-thread thing goes, William's suggestion should
address that, whether you follow the exact MVC design pattern or not.
That is, just put the data initialization code on a thread, but use some
kind of notification to the main thread that owns your UI forms to allow
the completed initialization to be used.

I recommend the BackgroundWorker class for this. It already has built
in all the necessary behavior to do this cleanly. In particular, it
executes the work you want done on a background thread, and the event
that signals the completion of the background thread is executed on your
form's owning thread as required, without you having to explicitly use
Control.Invoke() or Control.BeginInvoke(). (Not that there's anything
wrong with those...it's just that the code can be simpler if you just
use the BackgroundWorker).

As an added bonus, the BackgroundWorker also provides an easy-to-use
"progress notification" paradigm, also via an event that is
automatically raised on the correct UI-owning thread.

You'll want to do this latter part (handling the cross-thread issue)
whether or not you fix up the controller aspect of your code.

Pete
 
A

A.Weinman

Loading the data in a separate thread and then binding it to my UI has
been mentioned more than once, but I am not sure it's possible for
what I'm trying to do. I have a treeview on my form, which I populate
with the last 20 schedules, and add each item produced on each of
those schedules as a child node to the given schedule. This data is
all stored on a database. I as of yet have not found a way to do this
other than a DataAdapter, pulling the data out with DataRows, and
adding each node individually. If there is a better way to do this or
you need more information to understand what I'm doing, please let me
know. Thank you.

~Andrew
 
P

Peter Duniho

Loading the data in a separate thread and then binding it to my UI has
been mentioned more than once, but I am not sure it's possible for
what I'm trying to do. I have a treeview on my form, which I populate
with the last 20 schedules, and add each item produced on each of
those schedules as a child node to the given schedule. This data is
all stored on a database. I as of yet have not found a way to do this
other than a DataAdapter, pulling the data out with DataRows, and
adding each node individually.

If it is impractical for you to create and maintain a copy of the data
in memory, and you need to use the TreeView control itself to maintain
the data (a questionable practice IMHO, but it's doable and not
necessarily a big problem), then you may well still need to use Invoke()
or (preferably) BeginInvoke() to populate the TreeView from a background
thread.

This doesn't mean that the BackgroundWorker class isn't useful in this
case; it just means that you still need to use Invoke/BeginInvoke().

The key is to have the background thread to do the actual database
queries, etc. and to have it use the invoke technique to call code that
will actually modify the TreeView instance by adding elements to it.

As a start, you probably will want to just write the code normally, but
wrap access to the TreeView with invoking. For example, if your code
would normally look like this:

treeView1.Nodes.Add(nodeData);

it would instead look like this:

treeView1.Invoke((MethodInvoker)delegate() {
treeView1.Nodes.Add(nodeData) } );

Actually, there are lots of ways to do this, but IMHO writing an
anonymous method in this case is the easiest and produces the clearest code.

There is some overhead associated with using Invoke(), and also using
Invoke() rather than BeginInvoke() causes the calling thread to block
until the invocation has been completed.

For performance reasons, you may want to only call Invoke() (or
BeginInvoke()) at particular points within the processing, to minimize
how many times you incur that overhead (for example, you might collect
the data as it's read into a temporary data structure, and then pass the
whole thing when you use Invoke() to some code that can take that large
chunk of data and update the UI all at once), and you may also want to
use BeginInvoke() so as to allow your background thread to continue
doing its work even though the UI may not have been completely updated yet.

But I would get it working in the simplest way first, and worry about
the performance aspect later.

Pete
 
A

A.Weinman

I wrapped my treeview1.add method in an invoke with an if statement,
and it works like a charm. Thanks for all your help.

~Andrew
 

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