473,466 Members | 1,514 Online
Bytes | Software Development & Data Engineering Community
Create Post

Home Posts Topics Members FAQ

how to communicate between several forms?

11 New Member
Hello,

i'm trying to create an application which exists of 2 forms:
a login form and a panel form.

Now the login form speaks for itself, when i press login i send a request to my server and the server returns some things..

I make use of a Form class which inits the form and a Client(Form login) class which do the networking.

Now when i insert a wrong pasword for example, i want it that it sets a text on the login panel to invalid password.

But the thing is that i get errors about cross threading when i set my text, EVEN if i use delegates to fix this.

But the problem is that i acces the text via an instance of the form class: this.loginForm.text = "invalid pass";

this code is in my Client class:
Expand|Select|Wrap|Line Numbers
  1.         private LoginForm login = <retrieved from constructor param>
  2.         delegate void updateLabelTextDelegate(string newText);
  3.         private void updateLabelText(string newText)
  4.         {
  5.             if (this.login.label4.InvokeRequired)
  6.             {
  7.                 // this is worker thread
  8.                 updateLabelTextDelegate del = new updateLabelTextDelegate(updateLabelText);
  9.                 this.login.label4.Invoke(del, new object[] { newText });
  10.             }
  11.             else
  12.             {
  13.                 // this is UI thread
  14.                 this.login.label4.Text = newText;
  15.             }
  16.         }
so how do i need to fix this???

Thanks in advance!
Jan 13 '11 #1

✓ answered by Curtis Rutland

Well, first of all, you absolutely don't need to directly access a label on a different form. That's poor design, and anyone who tells you to do that is wrong.

What you can do is expose a property or a method that will invoke itself if required. Here's a simplified example of what I think you're asking.

This is my "client" called AsyncClient. It exposes an event called "TaskComplete". When you call the BeginTask method, it starts a new thread, then sleeps that thread for 5 seconds, then triggers the event:

Expand|Select|Wrap|Line Numbers
  1.  class AsyncClient
  2.  {
  3.      public event EventHandler<EventArgs> TaskComplete;
  4.      protected virtual void OnTaskComplete()
  5.      {
  6.          if (TaskComplete != null)
  7.              TaskComplete(this, new EventArgs());
  8.      }
  9.  
  10.      public void BeginTask()
  11.      {
  12.          Task.Factory.StartNew(TaskMethod);
  13.      }
  14.  
  15.      private void TaskMethod()
  16.      {
  17.          Thread.Sleep(5000);
  18.          OnTaskComplete();
  19.      }
  20.  }
Form 1, when you click a button, launches Form2, updates it, and then starts a task. When the task is done, Form2 is updated again:

Expand|Select|Wrap|Line Numbers
  1. public partial class Form1 : Form
  2. {
  3.     private AsyncClient client;
  4.     Form2 f2;
  5.     public Form1()
  6.     {
  7.         InitializeComponent();
  8.         f2 = new Form2();
  9.         client = new AsyncClient();
  10.         client.TaskComplete += new EventHandler<EventArgs>(client_TaskComplete);
  11.     }
  12.  
  13.     private void button1_Click(object sender, EventArgs e)
  14.     {
  15.         f2.Show();
  16.         f2.TaskLabel = "Working";
  17.         client.BeginTask();
  18.     }
  19.  
  20.     void client_TaskComplete(object sender, EventArgs e)
  21.     {
  22.         f2.TaskLabel = "Finished";
  23.     }
  24.  
  25.  
  26. }
Now, Form2 is where the threading magic happens. Notice that instead of trying to directly update the label from other forms, I made a property called "TaskLabel". And in the set accessor for this property, I check to see if an invoke is required. If so, I invoke it using an Action (which is a predefined delegate type that takes no parameters and returns no values). And if not, I set the label to whatever value provided:

Expand|Select|Wrap|Line Numbers
  1. public partial class Form2 : Form
  2. {
  3.     public string TaskLabel
  4.     {
  5.         get
  6.         {
  7.             return label1.Text;
  8.         }
  9.         set
  10.         {
  11.             if (InvokeRequired)
  12.                 Invoke(new Action(() => TaskLabel = value));
  13.             else
  14.                 label1.Text = value;
  15.         }
  16.     }
  17.  
  18.     public Form2()
  19.     {
  20.         InitializeComponent();
  21.     }
  22. }
So when TaskLabel is updated, if it is done so from another thread, it's invoked properly.

There you go. Communication from form, to client, to event, to another form, with a cross thread reference to boot.

15 3493
Samuel Jones
48 New Member
Ok, the code you've got has blown my mind... way too confusing!!!

But there is a simple solution.

I'm assuming that your 'panel' form is the main form and the 'login' form is just like a dialog that pops up?

If so, follow these steps.
login form is called frmLogin
panel form is called frmPanel

In the login form's code, declare this variable:
Expand|Select|Wrap|Line Numbers
  1. public Form main = new Form();
On the panel form ABOVE frmLogin() method, declare this:
Expand|Select|Wrap|Line Numbers
  1. frmLogin login = new frmLogin();
In your panel form's load event, put this:
Expand|Select|Wrap|Line Numbers
  1. login.main = this;
  2. login.ShowDialog();  //Can be just login.Show() as well but ShowDialog is better.
Now wherever you want to communicate to 'panel' add this:
Expand|Select|Wrap|Line Numbers
  1. ((frmPanel)main).whateverPublicReference
On the panel form use:
Expand|Select|Wrap|Line Numbers
  1. login.whateverPublicReference
Hope this helps.

Sam.
Jan 13 '11 #2
john patohn
11 New Member
ok thanks for the fast reply!

i'll try it!
Jan 13 '11 #3
john patohn
11 New Member
Hmm it seems that this isn't what i want i think:

i've 3 classes atm
one login form class
one panel form class
one client class for networking(this is not a form!)

so when the application starts up, i want it to show only my login form, i use the client class for networking, and when i have a wrong username/pass i want the text to be set at login class from the client class.. so i need an SAFE instance of the login class in my client class, but as stated above what i have now doesn't work.

you are not using the client class in any step?

thanks for the reply though :)
Jan 13 '11 #4
Samuel Jones
48 New Member
I didn't notice you had a third class!! :D

method still works though.

Make the following modifications with my code.

put 'public' into this line.

Expand|Select|Wrap|Line Numbers
  1. public frmLogin login = new frmLogin();
On the panel form, add this code, similar to the other form.

(btw the client is called netClient)

Expand|Select|Wrap|Line Numbers
  1. public netClient client = new netClient();
on the client add the main reference.
Expand|Select|Wrap|Line Numbers
  1. public Form main = new Form();
Modify the code as follows:
Expand|Select|Wrap|Line Numbers
  1. login.main = this;
  2. Hide();
  3. login.ShowDialog();
  4. Show();
When you call the client, use this code:
Expand|Select|Wrap|Line Numbers
  1. client.main = this;
  2. client.run(); //Or similar
Now to access the login form from the client use:
Expand|Select|Wrap|Line Numbers
  1. ((frmPanel)main).login.whateverPublicReference
and to access the client from the login form use:
Expand|Select|Wrap|Line Numbers
  1. ((frmPanel)main).client.whateverPublicReference
Try that one.

Sam
Jan 13 '11 #5
john patohn
11 New Member
thanks again, but i am confused now :(

can u please say what i need to put in my login class to get a reference to client(this is not a form!), so i can call at my connect button in the login form:

client.main = this;
this.client.connect();

and what i need to put in my panel class to get a reference to my client ?
and what is frmPanel ?
and do i need todo Application.run(Panel()); ?

so this is what i want:
-user starts application,
-login form pops up,
-user fills in details and press connect button, that does client.connect() and the response is for example invalid username, then on the login form should appear login.label = "invalid username",
-once he succesfully logged in, the login form dissappear and the panel form shows up,
-the client is receiving packets still and now it needs to update something of the panel form panel.label = "hello";

thanks!
Jan 13 '11 #6
Samuel Jones
48 New Member
:D

ok, apologies for the confusion.

frmPanel is your panel form.
frmLogin is your login form.
netClient is your client CLASS.

Add the code from my previous posts.

Add the following code to frmPanel's load event.
Expand|Select|Wrap|Line Numbers
  1. {
  2.     this.Hide();  //Hides panel form
  3.     login.main = this;
  4.     client.main = this;
  5.     login.ShowDialog();  //Show login form
  6.     this.Show();
  7. }
  8.  
On login form's login button, put this code.
Expand|Select|Wrap|Line Numbers
  1. {
  2.     ((frmPanel)main).client.connect();
  3. }
  4.  
In the clients code at this when you want to send 'invalid username' back:
Expand|Select|Wrap|Line Numbers
  1. ((frmPanel)main).login.label.text = "Invalid Username";
When the client wants to set the panel's label to say hello, use this code:
Expand|Select|Wrap|Line Numbers
  1. ((frmPanel)main).label.text = "Hello";

The panels code should look like this.

Expand|Select|Wrap|Line Numbers
  1. namespace ...
  2. {
  3.    public class frmPanel : Form
  4.    {
  5.  
  6.        //Copy from here
  7.        public netClient client = new netClient();
  8.        public frmLogin login = new frmLogin();
  9.  
  10.        public frmPanel()
  11.        {
  12.            InitializeComponent();
  13.        }
  14.  
  15.        private void frmPanel_Load(object sender, EventArgs e)
  16.         {
  17.             this.Hide();  //Hides panel form
  18.             login.main = this;
  19.             client.main = this;
  20.             login.ShowDialog();  //Show login form
  21.             this.Show();
  22.         }
  23.  
  24. //...rest of program
  25.  
  26.  
Try that.

Sam
Jan 13 '11 #7
john patohn
11 New Member
Ok that helped me alot, but now i see i got another problem.. :D

i need to access the textbox from my login in my client class to get the name and pass.. how would i do that? :)

thanks for the replies i really appreciate it!

-----------------------------------------------------------------
i fixed that but all this code did NOT help :(
this is what i used: ((Form3)main).login.label4.Text = "Invalid Username";
it still says about cross threads invalid access on label
----------------------------------------------------------------
Jan 13 '11 #8
Curtis Rutland
3,256 Recognized Expert Specialist
@Samuel, that's really not the best advice. What you're advising is tightly coupling multiple forms and classes. What if you have to change one? You have to start making changes all over the place.

A better pattern is to allow forms to raise events, and others to subscribe. To borrow an analogy, in a bingo parlor, the guy pulling the balls pulls one, reads it, and shouts it out, while all listeners check their boards to see if they have a match. He doesn't go to each of them and check for them, and they don't each have to go to him to check. It's broadcast, and each person responds in their own way, and neither cares what the other does.

To that point, I've recently written a tutorial to cover this exact point. Please have a read:

http://bytes.com/topic/c-sharp/insig...ny-other-forms

It shows the two best mechanisms for form interaction.
Jan 13 '11 #9
john patohn
11 New Member
thank you very much!
i will definately look at it and let you know if it helped!
Jan 13 '11 #10
Samuel Jones
48 New Member
@Curtis: I have happily been proven wrong. :D

This will help heaps in my current program and this user's program!

The one thing i can't see in your solution is how you would talk to the parent form? (in your example the Default Form and in John's problem the panel form)

I would think a conjunction of methods would work maybe?

@john: I'm stumped, i have no knowledge of such an error. maybe if you provide us with the full error script i could try to coax it out.

Try to solve your errors using Curtis's property method. ie.
Expand|Select|Wrap|Line Numbers
  1. // reference using login.error = "string" from panel form.
  2. // reference using ((frmPanel)main).login.error = "string" from client.
  3.  
  4. public string error
  5. {
  6.    set
  7.    {
  8.        label4.Text = value;
  9.    }
  10. }
  11.  
Jan 14 '11 #11
john patohn
11 New Member
I've been a little busy, but im stuck again with the tutorial which was posted above...

the reason why i get corss thread exception: invalid access is because (i think atleast) i call the change from another thread, because i use asynchronous programming, and so i use a method OnReceive, which is triggered by another thread, so not the main thread where my form was created..

i tried adding the things with event system, but i still get the same exception, so events aren't thread safe neither.. which is normal

so any ideas how to solve this safely?


thanks in advance!
Jan 14 '11 #12
Curtis Rutland
3,256 Recognized Expert Specialist
This should help:

http://msdn.microsoft.com/en-us/libr...v=VS.100).aspx

Basically, you have to invoke the call instead of doing it directly.
Jan 14 '11 #13
john patohn
11 New Member
Thanks for the reply, but i tried that already (look first post..)

but the problem is that i access a label from another form.. so i need to safely access that class also.. atleast i think because it didn't work when i used delegates.

Im not sure about the design of multiple forms, stupid microsoft needs everything about forms handled on the main thread... which i have problems with..

if someone has any tutorial on how to make multiple forms and communicate between them..
THIS IS NOT FORM <-> FORM
more like
FORM <-event- CLIENT(NO FORM) -event-> OTHERFORM
the Client class handles the network therefor i don't have a form for that!

maybe my design is poor, but im still new at this.. please help me!

thanks!
Jan 14 '11 #14
Curtis Rutland
3,256 Recognized Expert Specialist
Well, first of all, you absolutely don't need to directly access a label on a different form. That's poor design, and anyone who tells you to do that is wrong.

What you can do is expose a property or a method that will invoke itself if required. Here's a simplified example of what I think you're asking.

This is my "client" called AsyncClient. It exposes an event called "TaskComplete". When you call the BeginTask method, it starts a new thread, then sleeps that thread for 5 seconds, then triggers the event:

Expand|Select|Wrap|Line Numbers
  1.  class AsyncClient
  2.  {
  3.      public event EventHandler<EventArgs> TaskComplete;
  4.      protected virtual void OnTaskComplete()
  5.      {
  6.          if (TaskComplete != null)
  7.              TaskComplete(this, new EventArgs());
  8.      }
  9.  
  10.      public void BeginTask()
  11.      {
  12.          Task.Factory.StartNew(TaskMethod);
  13.      }
  14.  
  15.      private void TaskMethod()
  16.      {
  17.          Thread.Sleep(5000);
  18.          OnTaskComplete();
  19.      }
  20.  }
Form 1, when you click a button, launches Form2, updates it, and then starts a task. When the task is done, Form2 is updated again:

Expand|Select|Wrap|Line Numbers
  1. public partial class Form1 : Form
  2. {
  3.     private AsyncClient client;
  4.     Form2 f2;
  5.     public Form1()
  6.     {
  7.         InitializeComponent();
  8.         f2 = new Form2();
  9.         client = new AsyncClient();
  10.         client.TaskComplete += new EventHandler<EventArgs>(client_TaskComplete);
  11.     }
  12.  
  13.     private void button1_Click(object sender, EventArgs e)
  14.     {
  15.         f2.Show();
  16.         f2.TaskLabel = "Working";
  17.         client.BeginTask();
  18.     }
  19.  
  20.     void client_TaskComplete(object sender, EventArgs e)
  21.     {
  22.         f2.TaskLabel = "Finished";
  23.     }
  24.  
  25.  
  26. }
Now, Form2 is where the threading magic happens. Notice that instead of trying to directly update the label from other forms, I made a property called "TaskLabel". And in the set accessor for this property, I check to see if an invoke is required. If so, I invoke it using an Action (which is a predefined delegate type that takes no parameters and returns no values). And if not, I set the label to whatever value provided:

Expand|Select|Wrap|Line Numbers
  1. public partial class Form2 : Form
  2. {
  3.     public string TaskLabel
  4.     {
  5.         get
  6.         {
  7.             return label1.Text;
  8.         }
  9.         set
  10.         {
  11.             if (InvokeRequired)
  12.                 Invoke(new Action(() => TaskLabel = value));
  13.             else
  14.                 label1.Text = value;
  15.         }
  16.     }
  17.  
  18.     public Form2()
  19.     {
  20.         InitializeComponent();
  21.     }
  22. }
So when TaskLabel is updated, if it is done so from another thread, it's invoked properly.

There you go. Communication from form, to client, to event, to another form, with a cross thread reference to boot.
Jan 14 '11 #15
john patohn
11 New Member
Holy crap!

Thanks its working now!
I never heard of the Action class and that is exactly what i needed!

Thank you very much!
Jan 15 '11 #16

Sign in to post your reply or Sign up for a free account.

Similar topics

1
by: Chris Beach | last post by:
Hi, I have a JSP page with several forms on it. Some of these forms are generated dynamically, and each of them submits some information to a database. Handling one form is easy, as I can...
1
by: Coy Howe | last post by:
What is the best way to enter a new record and have a common ID # or Social Security #, for example, populate several tables at the same time? I have a main table with an auto number and a dozen...
1
by: Romao | last post by:
Hi, None of the C#/ADO documentation I was able to read refers what are the best practices/implementations for this "kind of" problem, I would like to get help/opinions please, let me explain...
0
by: Kristian Frost | last post by:
Hi, I'm just getting started with VB.Net, and I'm having trouble getting the routing around of some of the data straight in my mind, which has led me to the following problem. Basically, I'm...
4
by: f3dde | last post by:
Hiya, I am working on something that has several VB forms. Now I like to make all those forms into 1 form. Is it possible to make some sort of menu, just like in MS Access to have multiple forms...
11
by: isoquin | last post by:
Really, i've looked for the answer, but I must be missing some syntax somewhere... I have a popup calendar which is to be used across several forms. Each form will need the calendar to...
2
by: BBLP | last post by:
Hi, I'm sure this is a really simple thing to do but I'm fairly new to Access... I used the Wizard to create a form linked to another - this is fine and the linked form is filtered by the Primary...
6
by: John | last post by:
Hi I am trying to create (not open) several forms in the background threads using the code given below at the end. 1. Am I doing it correctly? 2. How can I get handle sot these forms in the...
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
1
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...
0
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and...
0
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.