Updated a Textbox from a separate class

  • Thread starter Thread starter Guest
  • Start date Start date
G

Guest

I know this is probably simple but I have a C# form and the class for the
form is called sbaSynch. I have a textbox name txtServerName.

I'm creating a class to manipulate XML functions so I added a class to
project and it's named XmlApi().

In the XmlAPI() class I have simple code as following
XmlAPI()
{
string str = "Some Text";
sbaSynch sbas = new sbaSynch();
sbas.txtServerName.Text = str;

}

I get no errors but the text box on the form doesn't display the text that
was assigned to it.
 
kfrost,

You can declare the control to be public, then, the instance will be
exposed from your form. However, this is pretty bad design. You should
expose a method which will allow you to pass the text into it, and then the
method will set the text (allowing you to not expose the control).

Hope this helps.
 
Hello Nicholas,

My problem everything I've ever written in the past has been console
applications and stuff and I've just written all my functions in one class.

While I was experimenting, I found that when I call a function in the
xmlapi, I could pass the textbox and that worked. I'm not sure that's like C
to where I'm passing a reference.

What you are saying, if the form class is sbaSynch() and from sbaSynch() I
make a call to a function in the XmlAPI() class, I should have a method in
the sbaSynch() class that can be called from the XmlAPI() class that would
update the textbox?

Would that be the most efficient way of doing this? Making calls back and
forth between classes?

Also, what are the potential problems of passing the textbox's to the class?

Below I listed the XmlAPI() class wrong.

It's XmlAPI()
{
GetXMLString(TextBox txtResults)
{
txtResults.Text = "some text"
}
}

In the form class there is a textbox named txtResults

I do the following
GetXMLString(txtResults);

This appears to work fine. Just not sure if there are inherit problems I'm
not aware of?
--
(e-mail address removed)

Network & Software Integration
www.n-sv.com


Nicholas Paldino said:
kfrost,

You can declare the control to be public, then, the instance will be
exposed from your form. However, this is pretty bad design. You should
expose a method which will allow you to pass the text into it, and then the
method will set the text (allowing you to not expose the control).

Hope this helps.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

kfrost said:
I know this is probably simple but I have a C# form and the class for the
form is called sbaSynch. I have a textbox name txtServerName.

I'm creating a class to manipulate XML functions so I added a class to
project and it's named XmlApi().

In the XmlAPI() class I have simple code as following
XmlAPI()
{
string str = "Some Text";
sbaSynch sbas = new sbaSynch();
sbas.txtServerName.Text = str;

}

I get no errors but the text box on the form doesn't display the text that
was assigned to it.


--
(e-mail address removed)

Network & Software Integration
www.n-sv.com
 
P.S. Nicolas, I've been ramping up on a lot of things, and I do have a
method in the sbaSynch() class trying to update the textbox's but it wasn't
working either. I've looked into creating a delegate but still wasn't
working either.

The only thing I've gotten to work is passing the textbox to the function
I'm calling in the separate class to get it to work consistently.

As mentioned, I don't know of any problems with doing this?
 
See inline why your code doesn't work.

Other problems with your code:
You shouldn't try to modify controls directly from classes that do some
computation or whatever. Try to return string, for example, and set the
properties of your controls after calling these methods.

class XmlAPI
{
public XmlApi() {}

public string GetSomeString(int someParameter)
{
return "Returned " + (someParameter + 10).ToString();
}
}

your form:

class SomeForm : System.Windows.Forms.Form
{
private TextBox tb;
private XmlAPI x = new XmlAPI();

public SomeForm()
{
tb = new TextBox();
this.Controls.Add(tb);

string result = x.GetSomeString(7);
tb.Text = result;
}
}


kfrost said:
I know this is probably simple but I have a C# form and the class for the
form is called sbaSynch. I have a textbox name txtServerName.

I'm creating a class to manipulate XML functions so I added a class to
project and it's named XmlApi().

In the XmlAPI() class I have simple code as following
XmlAPI()
{
string str = "Some Text";

sbaSynch sbas = new sbaSynch(); //you are creating new instance of
form here. You can't expect your application's form's controls to be
modified because you are accessing this instance in the next line - if you
want to modify your own form, pass it to this method as parameter.


sbas.txtServerName.Text = str; //you are modifying the local
instance's txtServerName's Text property
 
Hello Lebesgue,

Thank you for the response. Keep in mind, I already have a textBox designed
on the form. The code you show creates another textbox to just store a value.

The textboxes I'm working with are visible on the form and I need to change
their values. I tried creating a function in my form class and passing the
string back from the function in the XmlAPI class. No exceptions occurred
with it but the end results that the textbox never showed the changes.

What I've found to work reliably is in your example below, I have XmlApi
class function accept a TextBox. public string GetSomeString(TextBox
txtResults)

So now it looks like.
class XmlAPI
{
public XmlApi(){}
public string GetSomeString(TextBox txtResult)
{
txtResult.Text = "some text";
}
}

class SomeForm : System.Windows.Forms.Form
{
private XmlAPI x = new XmlAPI();

x(txtResults);
}

Note, the txtResults textbox is defined on the form.

What are the problems with doing it this way?
 
So now it looks like.
class XmlAPI
{
public XmlApi(){}
public string GetSomeString(TextBox txtResult)
{
txtResult.Text = "some text";
}
}

class SomeForm : System.Windows.Forms.Form
{
private XmlAPI x = new XmlAPI();

x(txtResults);
}

Note, the txtResults textbox is defined on the form.

What are the problems with doing it this way?


As I have written in earlier post, it is not considered a good practice to
do so. Application logic code should not operate directly on controls.

You seem to completely not understand my earlier post. I am sorry, I can't
help you any further.
 
Thank you Lebesgue,

I appreciate the input but just to clarify for anybody else contemplating on
responding, I understand what you said, I'm just saying that I tried that
before your post and it doesn't work the way I need it to. The
txtResults.Text is not displaying on the form. I've kicked the code of in a
background thread and then tried to use a delegate to call the function but
still, the text does not update once I get into the code in the remote class.

Also, what I was asking is what is considered "bad" about passing the object
to a function in a separate class?

The control is still private and I would think this would be along the same
lines of passing a pointer to an object in C.

I'm looking for a little more than it's just bad practice, I would like to
know the reasoning as to why. This is a simple application without too much
complexity and basically it's just a utility to copy records from one DB to
another and I'm just updating the text box with the record info that is being
copied after each copy.


With that being said, thank you for your input.
 
Dear kfrost,
your problem with not updating the contents of your TextBox is here:

XmlAPI()
{
string str = "Some Text";
sbaSynch sbas = new sbaSynch(); <<<<------------------------- HERE,
read my first post
sbas.txtServerName.Text = str;
....

Also, what I was asking is what is considered "bad" about passing the object
to a function in a separate class?

Nothing is considered bad about passing an object to a method of a class.
The control is still private and I would think this would be along the same
lines of passing a pointer to an object in C.

Classes are reference types in C#, so they are passed to methods by
reference. The same as using pointer in C.
I'm looking for a little more than it's just bad practice, I would like to
know the reasoning as to why.

If you don't feel it's dumb for a "XmlAPI" class to operate on TextBoxes
instead of returning strings, do it as you please.
 
Lebesgue,

Thank you again, and again I get that part. I've left out there are
actually two classes in the code because I expected there would be a simple
way of doing it and the thread wouldn't go down this avenue. I agree with
what you said if my code were running in the Form() class but hardly any of
it does. I also tried creating a function within Form() class in which
could be called from the other classes and pass the strings that needed to be
written to the text box and update the txtResults control that way but it
doesn't work either.

At any rate without debating it anymore, I am looking for a way to update a
text box defined in the form class, while inside of another class. I
understand what you've posted and I appreciate the input but that's not what
I'm trying to do.

The form calls one utility() class which contains interfaces for Small
Business accounting where the majority of the code for the app is run. Also,
I need to the code from within that class to interface with an xml file. So
I'm creating the xmlAPI class so I can reuse the code.

So I'm not doing work in the form class to make a call like you describe to
get a string returned back there. It goes like this.

Form() class immediately makes a call to a method named RunQuoteView()
within the ListingQuotes() class. Then I have a need to make calls to the
xmlApi(). Some of the code in each of these classes I need to update the
txtResults.Text which is a control initialized in the Form() class.

Having said that, I'm trying to understand how to update a textbox control
created in the form class, from other classes in an application.

I thought this would be fairly common. Up until now the only way I've found
it to work reliably is what I've posted. What you've posted isn't relative
here and I'm certainly not going to take modular code and copy it all into
the form class just so I can update a text box.

I don't consider it "dumb" just because somebody says it is, so if it's dumb
I would very much appreciate the reasoning as to why it's dumb so I can make
the decision for myself because I need to do it some way.

Also, if this isn't the way you go about accomplishing what I'm try to do,
then how else would you do it? (Again, what you've posted I understand but
it's not relevate for my scenario.)

Thanks again for your time.
 
kfrost said:
I am looking for a way to update a text box defined
in the form class, while inside of another class.

Suppose your class is called XmlThing, the work it does is performed
by a public method DoTheWork, and the result of that work is stored in
a private field (member variable) of the XmlThing class. I'd then
expose this value through a property - "Value", for instance, such
that the XmlThing class doesn't need to *set* anything, merely make
the value available.

The code in the form might look like this:

XmlThing x = new XmlThing();
x.DoTheWork();
theTextBox.Text = x.Value;
I would very much appreciate the reasoning as to
why it's dumb

An XML API has nothing to do with Windows Forms or text boxes. The
point of using classes in programming is modularisation - keeping
separate things apart. Therefore if you can use a simple native type
(such as string) rather than enforcing a dependency on a complex class
like TextBox, your code will be more easily reusable in situations
where there is no form or text box (e.g. on the Web, or in a console
application).

P.
 
Paul,

Thank you very much for the detailed response. I understand the modularity
concept and that's why I do everything in the classes. If resuability is the
main concern, in this situation I may leave what i have, take the example you
listed and see if I can come up with a way to cut out passing the textbox
down the road.

One question I do have with what you posted is this. Say the code you
posted is in a class called ListQuotes() and looks like this. Also, the
textbox is defined in a the form class. So it looks like this.

public class form
{
private System.Windows.Forms.TextBox theTextBox;

private void InitializeComponent()
{
this.theTextBox = new System.Windows.Forms.TextBox();
}

private void form_Load(object sender, System.EventArgs e)
{
using(ListQuote LQ = new ListQuote())
{
LQ.RunThing():
}
}

public class ListQuotes
{
private RunThing()
{
XMLThing x = new XmlThing();
x.DoTheWork();

}
}

public class XmlThing ()
{
string value;

private void DoTheWork()
{
// Say here we copy records from one database to another
// and I want to update theTextBox with info regarding the records
// that were copied. I know I could create a string and append to it
// similar to what you mentioned. However, I need to update theTextBox
// either here or after the x.DoTheWork call from the
ListQuotes.RunThing()
// above. It has to been in one place or the other and that's why I'm
asking
// how to access theTextBox from either of these classes.
}
}

I think the scenario pretty much describes what I have. You see the
theTextBox and all the controls I need to pull values from and update are in
the Form class.

However, all the code I work with runs in either the methods of
theListQuotes or XmlThing classes. However, say TextBox needs to be updated
as soon as DoWork function is called also, I'm never going to be back within
the Form class until all the work in the ListQuotes and XmlThing methods are
complete. At this point it's too late to populate the text boxes. Also, I
want this info written as soon as it's done because the user can watch what's
going on as it happens.

Again, I thank you for taking the time to explain. I really appreciate it.
Worst case I can deal with changing the method definition is that's the
worst thing I'll have to deal with in the future.

Thanks again!
 
As I said, the XmlThing should not know or care that its result is
going into a text box, because some day *it might not be*. XmlThing
should just provide an output, and let the caller (in this case, the
form) do what it wants with that output.

Perhaps I'm missing something, but it sounds like you just need to
return a value:

public class MyForm : Form
{
private TextBox theTextBox;

// ... InitializeComponent ... blah blah ...

private void Form_Load(object sender, System.EventArgs e)
{
ListQuote lq = new ListQuote();
theTextBox.Text = lq.RunThing();
}
}

public class ListQuote
{
public string RunThing()
{
// get the value from the database
string s = "some value";
return s;
}
}

P.
 
The thing that's being missed here is that all the form is there for is to
get a servername from the user and it has the txtResults display box which I
want to use to display what's going on within the application

So the line you have in the Form_Load() function
theTextBox.Text = lq.RunThing();

That won't work because it wouldn't update theTextBox.Text until all the
code in RunThing() has finished. There's probably 100+ lines of code in
RunThing() that I'm not listing here for simplicity sake.

I know it's my fault for not doing a better job of explaining, but in short.
At say lines 5, 35, 46, 56 I need to update theTextBox.Text while inside the
RunThing() method

It can't wait until the exection returns back to Form_Load in MyForm.

One thing I have tried is running RunThing in a background thread and
created a delegate function in MyForm. Then make a call from the RunThing()
back to this delegate when I need to populate theTextBox.Text. I step
through the code and in the debugger it appears to work but the
theTextBox.Text never updates on the form itself.
 
kfrost said:
So the line you have in the Form_Load() function
theTextBox.Text = lq.RunThing();
That won't work because it wouldn't update
theTextBox.Text until all the code in RunThing()
has finished.

Right - well, if there's a point in the code at which you could stop
and update the text box, then you should be able to rewrite it so that
you have some persistent state (how much work you've done so far) and
the form could then call it in a loop.

However, threading is usually a better idea when you're doing
intensive work and want to update the user interface at the same time.
You say you've tried this and it didn't seem to work. Are you using
Control.Invoke for the delegate? If you access controls created on one
thread from a different thread, the results are unpredictable and
usually wrong.

This page might help:
http://www.yoda.arachsys.com/csharp/threads/

P.
 
The invoke I'm trying to use is probably what's wrong. I had done something
similiar to this within a thread a year a so ago and just copied the code I
used but for something else. Didn't spend a lot of time on it. Was hoping
there was a simple way. I'm learning the SBA sdk and have a prototype app
I'm trying to get to a customer. I was just wanting to populate the text box
while it was running so they don't think the app is hung. (Know there are
other things to do.) But then i got curious about this.

Here's what i have for the delegate.
public delegate void DisplayStringDelegate(string strText);

public void DisplayString(string strText)
{
if(InvokeRequired)
{
BeginInvoke(new DisplayStringDelegate(DisplayString), new object[]
{strText});
return;
}
txtResults.Text += strText;
}

Like I mentioned before this is within the form class and it runs but the
txtResults.Text doesn't update. I'm not that familiar with this code at this
time. I'll look at Control.Invoke you mentioned and see if I can find an
example for it.

Thanks again for all your help!!!!!
 
Hello Paul,

Not sure you're still monitoring this thread, but if so. I step through and
paid attention to the code I posted in the last post and InvokeRequired was
never true so it missed the BeginInvoke.

So I removed the if statement, and for some reason it excepts out with the
error, "Cannot call Invoke or InvokeAsynch on a control until the window
handle has been created."

I googled that but found all kinds of stuff, nothing too relevant. I then
started looking at Invoke and BeginInvoke samples and they were pretty much
what I had.

The only thing I see different is i'm in making the call from a class
outside of where the delegate was defined.

Any ideas on this?

Thanks.
--
(e-mail address removed)

Network & Software Integration
www.n-sv.com


kfrost said:
The invoke I'm trying to use is probably what's wrong. I had done something
similiar to this within a thread a year a so ago and just copied the code I
used but for something else. Didn't spend a lot of time on it. Was hoping
there was a simple way. I'm learning the SBA sdk and have a prototype app
I'm trying to get to a customer. I was just wanting to populate the text box
while it was running so they don't think the app is hung. (Know there are
other things to do.) But then i got curious about this.

Here's what i have for the delegate.
public delegate void DisplayStringDelegate(string strText);

public void DisplayString(string strText)
{
if(InvokeRequired)
{
BeginInvoke(new DisplayStringDelegate(DisplayString), new object[]
{strText});
return;
}
txtResults.Text += strText;
}

Like I mentioned before this is within the form class and it runs but the
txtResults.Text doesn't update. I'm not that familiar with this code at this
time. I'll look at Control.Invoke you mentioned and see if I can find an
example for it.

Thanks again for all your help!!!!!
--
(e-mail address removed)

Network & Software Integration
www.n-sv.com


Paul E Collins said:
Right - well, if there's a point in the code at which you could stop
and update the text box, then you should be able to rewrite it so that
you have some persistent state (how much work you've done so far) and
the form could then call it in a loop.

However, threading is usually a better idea when you're doing
intensive work and want to update the user interface at the same time.
You say you've tried this and it didn't seem to work. Are you using
Control.Invoke for the delegate? If you access controls created on one
thread from a different thread, the results are unpredictable and
usually wrong.

This page might help:
http://www.yoda.arachsys.com/csharp/threads/

P.
 
InvokeRequired was never true so it missed the BeginInvoke.
InvokeRequired would be true if you get the value of this property from
other than UI thread. If you didn't spawn any other threads, it has no
reason to be true.

So I removed the if statement, and for some reason it excepts out with the
error, "Cannot call Invoke or InvokeAsynch on a control until the window
handle has been created."

I'd bet it's because you are calling this from the constructor of your form
or, what is more probable, you are calling it on your blind instance of form
(SBASynch = new SbaSynch(); in your XmlAPI) which has no handle until it is
shown.
 
Lebesque,

Thank you. I think you've hit it on your last set of comments. That is
what I'm doing because I need to call the delegate from from a class inside
say the XmlApi. With my limited knowlege of delegates and multithreading,
that was the one way I could come up with so far.

To save anyone responding, I'll lay out this scenario for some hopeful
input. Lebesgue, sorry for switching things up on you here, but I'm going to
use the names I am in the app so I hopefully don't miss anything like I did
in the beginning of this thread. (I'm going to cut out a lot of irrelevent
code but the same naming structure.)

namespace SBASynch
{
public class Form1 : System.Windows.Forms.Form
private System.Windows.Forms.TextBox txtResults;
private System.Windows.Forms.Button btnSynch;

public delegate void DisplayStringDelegate(string strText);

this.txtResults = new System.Windows.Forms.TextBox();
this.btnSynch = new System.Windows.Forms.btnSynch();

private void btn_Click(object sender, System.EventArgs e)
{
Thread WorkThread = new Thread(new ThreadStart(CopyInvoices));
WorkThread.IsBackground = true;
WorkThread.Start():
}

private void CopyInvoices()
{
// I left out the 2 text boxes initialization code above to be concise.
string serverName = txtServerName.Text;
string databaseName = txtDBName.Text;

try
{
using (ListingQuotes getQuotes = new ListingQuotes(serverName,
clientName, databaseName)
{
getQuotes.RunQuoteView();
}
catch(Exception ex)
{

}

public void DisplayString(string strText)
{
BeginInvoke(new DisplayStringDelegate(DisplayString), new object[]
{strText});
txtResults.Text += strText + "\r\n";
}
}

class ListingQuotes : IDisposable
{

Public void RunQuoteView()
{
Form1 cForm1 = new Form1();

cForm1.DisplayString("Delegate Called");
}
}


Keep in mind that the ListingQuotes is a class from some SDK examples and in
the RunQuoteView(), most of the code in the application is ran.

Multiple times in this codes at different points I need to update controls
immediately such as the textbox which is initialized in the Form1 class.

The code you see in the Form1 btnSynch.Click most of the code that runs int
the form class and execution will not return to there until the app is
finished. That's what led to my original post. Now the only thing I've
found to work is pass the txtResult object to RunQuotesNow().

But I've kind of got stuck on trying to figure out how to update the textbox
from Form1 as you and Paul have suggested. So that's what got me on this
delegation. So again, from what you stated, the way I'm calling it is the
problem but how else would you go about doing this?

Thanks again for your input!
 
Dear kfrost,
from what you've written in your last post I understand you need to update
your TextBox during execution of some xml processing. Correct me if I'm
wrong please. You were also asking how to accomplish this if you don't want
to pass a reference to that TextBox to the class that does the processing.
One possible solution is using events.

Please don't take this example absolutely strict, it just shows how to do it
in principle, not exactly in your case.
Please try to modify it to suit your concrete needs, it shows just how to
"do events" not exactly how you should do it. Refer to [1] or [2] if you
need more help on events and delegates.

[1] http://www.c-sharpcorner.com/Code/2003/Nov/EventsinNet.asp
[2] http://www.google.com

public delegate void NotificationHandler(object sender,
NotificationEventArgs e);

public class NotificationEventArgs : System.EventArgs
{
private string message;
public string Message { get {return message;} }
public NotificationEventArgs(string message)
{
this.message = message;
}
}

public class ClassWithNotifications
{
public event NotificationHandler Notification;

public void DoSomeProcessing()
{
//now you get some data that you need to show in the TextBox.
OnNotification("We have some data: " + data.ToString());
//do some other processing
OnNotification("Now the processing is over");
}

private void OnNotification(string message)
{
if (Notification != null)
{
Notification(this, new NotificationEventArgs(message);
}
}
}

usage:

on your form, let's say class MainForm

class MainForm
{
private ClassWithNotifications c;

public MainForm()
{
InitializeComponentsIfYouWish();
c = new ClassWithNotifications();
c.Notification += new
NotificationHandler(this.ShowNotificationInTextBox);
}

private void someButton_Click(object sender, EventArgs e)
{
c.DoSomeProcessing();
}

private void ShowNotificationInTextBox(object sender,
NotificationEventArgs e)
{
this.textBoxWhichIsSupposedToShowNotifications.Text = e.Message;
}
}


kfrost said:
Lebesque,

Thank you. I think you've hit it on your last set of comments. That is
what I'm doing because I need to call the delegate from from a class inside
say the XmlApi. With my limited knowlege of delegates and multithreading,
that was the one way I could come up with so far.

To save anyone responding, I'll lay out this scenario for some hopeful
input. Lebesgue, sorry for switching things up on you here, but I'm going to
use the names I am in the app so I hopefully don't miss anything like I did
in the beginning of this thread. (I'm going to cut out a lot of irrelevent
code but the same naming structure.)

namespace SBASynch
{
public class Form1 : System.Windows.Forms.Form
private System.Windows.Forms.TextBox txtResults;
private System.Windows.Forms.Button btnSynch;

public delegate void DisplayStringDelegate(string strText);

this.txtResults = new System.Windows.Forms.TextBox();
this.btnSynch = new System.Windows.Forms.btnSynch();

private void btn_Click(object sender, System.EventArgs e)
{
Thread WorkThread = new Thread(new ThreadStart(CopyInvoices));
WorkThread.IsBackground = true;
WorkThread.Start():
}

private void CopyInvoices()
{
// I left out the 2 text boxes initialization code above to be concise.
string serverName = txtServerName.Text;
string databaseName = txtDBName.Text;

try
{
using (ListingQuotes getQuotes = new ListingQuotes(serverName,
clientName, databaseName)
{
getQuotes.RunQuoteView();
}
catch(Exception ex)
{

}

public void DisplayString(string strText)
{
BeginInvoke(new DisplayStringDelegate(DisplayString), new object[]
{strText});
txtResults.Text += strText + "\r\n";
}
}

class ListingQuotes : IDisposable
{

Public void RunQuoteView()
{
Form1 cForm1 = new Form1();

cForm1.DisplayString("Delegate Called");
}
}


Keep in mind that the ListingQuotes is a class from some SDK examples and in
the RunQuoteView(), most of the code in the application is ran.

Multiple times in this codes at different points I need to update controls
immediately such as the textbox which is initialized in the Form1 class.

The code you see in the Form1 btnSynch.Click most of the code that runs int
the form class and execution will not return to there until the app is
finished. That's what led to my original post. Now the only thing I've
found to work is pass the txtResult object to RunQuotesNow().

But I've kind of got stuck on trying to figure out how to update the textbox
from Form1 as you and Paul have suggested. So that's what got me on this
delegation. So again, from what you stated, the way I'm calling it is the
problem but how else would you go about doing this?

Thanks again for your input!
 

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

Back
Top