Updated a Textbox from a separate class

  • Thread starter Thread starter Guest
  • Start date Start date
Yes you understand correctly. Also, WOW, thank you very much for taking the
time to put togther a detailed response. I'll take it and go through it and
check out the link you posted as well.

I've got a decent c# book I'll use as a reference but again thank you so
much for the detailed response!!!

kris
 
Hello Lebesgue,

I just wanted to let you know what you posted worked. I'm don't fully
understand just yet what all I did but I got it to work.

Thanks again for your help!

--
(e-mail address removed)

Network & Software Integration
www.n-sv.com


Lebesgue said:
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!
 
Here are some choices (mostly bad choices, but choices none-the-less):

1. Publicly expose the textboxes on the form. I am not keen on this idea.
2. Create a method to update the textbox. This is better, but has its
limitations.
3. Pass the textbox into the method by ref and allow it to be updated. This
can tightly couple the form to your class.
4. Create a delegate when you update the text and link to a method on your
form (handle an "event"). You can use this method to update the textbox.

NOTE: In a properly tiered application, the UI sends information into the
business layer and receives back an answer (or handles events raised
(delegates)). The UI works with the user, so it is the driver. If you have
the business layer updating the UI directly, you are reversing the flow. This
generally ends with a business layer that is intertwined with the UI and hard
to separate (yuck). You should think in terms of black boxes (when I hand you
this, you do something and return this) and services (much like black box,
but the messages passed are generally larger). In both cases, you try not to
link your UI and your business components. If you do, you might as well leave
the code in the UI.

--
Gregory A. Beamer
MVP; MCP: +I, SE, SD, DBA

***************************
Think Outside the Box!
***************************
 
Hello Greg,

Thank you for the insight. All the code runs in a module that was already
created for small business accounting. I didn't see the need to take
everything in that class and copy it to the UI form just to update the text
box.

Two of your suggestions I've gotten to work.
1) Passing the textbox into the other class method and that worked fine but
I got feedback that this wasn't "smart".

2)Lebesgue's event code I also got to work. He has a delegate and a event
handler and it seems to work fairly well. I did run into a place in my
function that didn't update the textbox but it wasn't that important so I
haven't done anything else.

What I'm curious to know from you though is, are there any major problems
with passing a ref to a control like a textbox into a method in a separate
class?

To me a ref is kind of like a pointer in C and I would have thought that
would have been best most efficient way because I would have thought you're
just passing the address of the textbox to update instead of the whole class.

Of these two which would be your preference and why?

Thanks.

kris
 
This has been an interesting reading about this problem. I understand the
position that Kfrost is in. I can only see that everyone is posting what is
a good technique. Then how would you design something like this to avoid
having the class directly changing the UI. Lets use an example. I have on a
form a listbox. I have a class that will take 2 numbers and multiply them
and then take the result and divide by 2. The listbox will display the
multiplication result and then will display the division result. Like a Log.
what is the correct approach to designing for this situation. Would it be
the delegate approach number 4? from cowboy
 
One possible correct approach would be to have a logger class which would be
accessible from your math library and let that class log to the ListBox.

abstract class Logger
{
public abstract void Log(string message);

public static Logger Null
{
get {return new NullLogger();}
}

private class NullLogger : Logger
{
public override void Log(string message)
{
}
}
}

class ListBoxLogger : Logger
{
private ListBox box;
public ListBoxLogger(ListBox box)
{
this.box = box;
}
public override void Log(string message)
{
this.box.Items.Add(message);
}
}

class Math
{
private Logger logger = Logger.Null;
public Logger Logger
{
get {return logger;}
set {logger = value;}
}

public float DoComputation(float a, float b)
{
float c = a * b;
logger.Log("immediate result a * b = " + c);
float d = c / 2;
logger.Log("result of computation is " + d);
return d;
}
}

class MyForm : Form
{
//...
ListBox box = new ListBox();
Math math = new Math;

//some handler
{
math.Logger = new ListBoxLogger(box);
}

//take inputs
{
float foo = math.DoComputation(1f, 2f);
}
}
 
correct me if I am wrong. isnt this still changing the list directly from a
class. which is not the best technique
 
You are wrong, it's definitely not "not the best technique". I am
manipulating the ListBox directly from a highly specialized class, done for
ListBox updating, nothing more. Business logic doesn't have a clue about
ListBox being updated, it calls logger.Log(string) (you see, no ListBox in
action)
 
Thanks for explaining!

Lebesgue said:
You are wrong, it's definitely not "not the best technique". I am
manipulating the ListBox directly from a highly specialized class, done for
ListBox updating, nothing more. Business logic doesn't have a clue about
ListBox being updated, it calls logger.Log(string) (you see, no ListBox in
action)
 

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