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: -
private LoginForm login = <retrieved from constructor param>
-
delegate void updateLabelTextDelegate(string newText);
-
private void updateLabelText(string newText)
-
{
-
if (this.login.label4.InvokeRequired)
-
{
-
// this is worker thread
-
updateLabelTextDelegate del = new updateLabelTextDelegate(updateLabelText);
-
this.login.label4.Invoke(del, new object[] { newText });
-
}
-
else
-
{
-
// this is UI thread
-
this.login.label4.Text = newText;
-
}
-
}
so how do i need to fix this???
Thanks in advance!
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: - class AsyncClient
-
{
-
public event EventHandler<EventArgs> TaskComplete;
-
protected virtual void OnTaskComplete()
-
{
-
if (TaskComplete != null)
-
TaskComplete(this, new EventArgs());
-
}
-
-
public void BeginTask()
-
{
-
Task.Factory.StartNew(TaskMethod);
-
}
-
-
private void TaskMethod()
-
{
-
Thread.Sleep(5000);
-
OnTaskComplete();
-
}
-
}
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: - public partial class Form1 : Form
-
{
-
private AsyncClient client;
-
Form2 f2;
-
public Form1()
-
{
-
InitializeComponent();
-
f2 = new Form2();
-
client = new AsyncClient();
-
client.TaskComplete += new EventHandler<EventArgs>(client_TaskComplete);
-
}
-
-
private void button1_Click(object sender, EventArgs e)
-
{
-
f2.Show();
-
f2.TaskLabel = "Working";
-
client.BeginTask();
-
}
-
-
void client_TaskComplete(object sender, EventArgs e)
-
{
-
f2.TaskLabel = "Finished";
-
}
-
-
-
}
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: - public partial class Form2 : Form
-
{
-
public string TaskLabel
-
{
-
get
-
{
-
return label1.Text;
-
}
-
set
-
{
-
if (InvokeRequired)
-
Invoke(new Action(() => TaskLabel = value));
-
else
-
label1.Text = value;
-
}
-
}
-
-
public Form2()
-
{
-
InitializeComponent();
-
}
-
}
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
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: - public Form main = new Form();
On the panel form ABOVE frmLogin() method, declare this: - frmLogin login = new frmLogin();
In your panel form's load event, put this: - login.main = this;
-
login.ShowDialog(); //Can be just login.Show() as well but ShowDialog is better.
Now wherever you want to communicate to 'panel' add this: - ((frmPanel)main).whateverPublicReference
On the panel form use: - login.whateverPublicReference
Hope this helps.
Sam.
ok thanks for the fast reply!
i'll try it!
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 :)
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. - public frmLogin login = new frmLogin();
On the panel form, add this code, similar to the other form.
(btw the client is called netClient) - public netClient client = new netClient();
on the client add the main reference. - public Form main = new Form();
Modify the code as follows: - login.main = this;
-
Hide();
-
login.ShowDialog();
-
Show();
When you call the client, use this code: - client.main = this;
-
client.run(); //Or similar
Now to access the login form from the client use: - ((frmPanel)main).login.whateverPublicReference
and to access the client from the login form use: - ((frmPanel)main).client.whateverPublicReference
Try that one.
Sam
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!
: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. -
{
-
this.Hide(); //Hides panel form
-
login.main = this;
-
client.main = this;
-
login.ShowDialog(); //Show login form
-
this.Show();
-
}
-
On login form's login button, put this code. -
{
-
((frmPanel)main).client.connect();
-
}
-
In the clients code at this when you want to send 'invalid username' back: - ((frmPanel)main).login.label.text = "Invalid Username";
When the client wants to set the panel's label to say hello, use this code: - ((frmPanel)main).label.text = "Hello";
The panels code should look like this. -
namespace ...
-
{
-
public class frmPanel : Form
-
{
-
-
//Copy from here
-
public netClient client = new netClient();
-
public frmLogin login = new frmLogin();
-
-
public frmPanel()
-
{
-
InitializeComponent();
-
}
-
-
private void frmPanel_Load(object sender, EventArgs e)
-
{
-
this.Hide(); //Hides panel form
-
login.main = this;
-
client.main = this;
-
login.ShowDialog(); //Show login form
-
this.Show();
-
}
-
-
//...rest of program
-
-
Try that.
Sam
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
----------------------------------------------------------------
@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.
thank you very much!
i will definately look at it and let you know if it helped!
@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. -
// reference using login.error = "string" from panel form.
-
// reference using ((frmPanel)main).login.error = "string" from client.
-
-
public string error
-
{
-
set
-
{
-
label4.Text = value;
-
}
-
}
-
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!
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!
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: - class AsyncClient
-
{
-
public event EventHandler<EventArgs> TaskComplete;
-
protected virtual void OnTaskComplete()
-
{
-
if (TaskComplete != null)
-
TaskComplete(this, new EventArgs());
-
}
-
-
public void BeginTask()
-
{
-
Task.Factory.StartNew(TaskMethod);
-
}
-
-
private void TaskMethod()
-
{
-
Thread.Sleep(5000);
-
OnTaskComplete();
-
}
-
}
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: - public partial class Form1 : Form
-
{
-
private AsyncClient client;
-
Form2 f2;
-
public Form1()
-
{
-
InitializeComponent();
-
f2 = new Form2();
-
client = new AsyncClient();
-
client.TaskComplete += new EventHandler<EventArgs>(client_TaskComplete);
-
}
-
-
private void button1_Click(object sender, EventArgs e)
-
{
-
f2.Show();
-
f2.TaskLabel = "Working";
-
client.BeginTask();
-
}
-
-
void client_TaskComplete(object sender, EventArgs e)
-
{
-
f2.TaskLabel = "Finished";
-
}
-
-
-
}
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: - public partial class Form2 : Form
-
{
-
public string TaskLabel
-
{
-
get
-
{
-
return label1.Text;
-
}
-
set
-
{
-
if (InvokeRequired)
-
Invoke(new Action(() => TaskLabel = value));
-
else
-
label1.Text = value;
-
}
-
}
-
-
public Form2()
-
{
-
InitializeComponent();
-
}
-
}
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.
Holy crap!
Thanks its working now!
I never heard of the Action class and that is exactly what i needed!
Thank you very much!
Sign in to post your reply or Sign up for a free account.
Similar topics
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...
|
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...
|
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...
|
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...
|
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...
|
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...
|
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...
|
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...
|
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...
|
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...
|
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,...
|
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...
|
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...
|
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...
|
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...
|
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...
|
by: adsilva |
last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
| |