3

I'm learning C# and what I need is to access control on a Form from other class (the same namespace).

I know there is a lot of posts on this topic here but didn't find complete solution 'for dumbs' so I write here what I figured out and please tell me - is this the correct way ?

Background: I have some 'debugging' form in my app and I need all other forms to be able to log their activity into this form. There is some ListBox control where all the logs from other forms are written. When I (or one of my tester-friends without Visual Studio) play around with app and something bad happens, I can look on that debug-form to see all detailed logs what happened just before that 'moment of error'.

My main form of the app (frmMain):

namespace myNamespace {

public partial class frmMain : Form {

private frmDebug debug;  // frmDebug is declared in other class
                         // we will hold reference to frmDebug form in 'debug'

public frmMain() {         // constructor of the main form 'frmMain'
  InitializeComponent();
  debug = new frmDebug();  // we create new instance of frmDebug immediately when
}                          // our main form is created (app started) and whole time
                           // of running this app we can access frmDebug from
                           // within frmMain through 'debug' variable


// clicking button 'btnLoggingTest', the log is written 
// in the 'frmDebug' form even if it is closed (not visible)
private void btnLoggingTest_Click(object sender, EventArgs e) {
  debug.Log("log this text for me please");
}

// Click handler of the button 'btnShowDebug' :
private void btnShowDebug_Click(object sender, EventArgs e) {
  debug.ShowDialog();    // here we can show frmDebug (in 'modal' style)
}                        // just to see what log-information is written there


} // frmMain class

} // namespace



And here is the code of class frmDebug itself : (there is only one Listbox placed on the form)

namespace myNamespace {
public partial class frmDebug : Form {

public frmDebug() {
  InitializeComponent();
}

public void Log(string txt) {    // this is the actual 'Log' function (or method)
  this.listBox1.Items.Add(txt);
  Application.DoEvents();        // if the logging takes place in some 
}                                // computing-intensive 'loop' or function,
                                 // (or in my case FTP login and upload process)
                                 // 'DoEvents' ensures every log appears immediately
                                 // after the 'Log()' was called. Otherwise all logs
                                 // would appear together at once, as soon as the 
                                 // computing-intensive 'loop' is finished

} // class frmDebug

} // namespace



I have a strange feeling in my stomach I'm doing it all wrong so please tell me how to do it properly :)
If it's OK, hope it helps somebody like me.

Thank you !

Enriqe
  • 507
  • 1
  • 5
  • 17
  • If you find yourself frequently accessing controls from forms they are not defined on, there's a good chance your UI architecture is not sound. I suggest you read up on MVC and MVVM (the Wikipedia articles are good intros) to learn about modern UI interaction patterns. – Eric J. Mar 22 '12 at 23:24

3 Answers3

3

Your application has probably a class called "Program". There you will find the code

var mainForm = new frmMain();
Application.Run(frmMain);

Create a static property for the debugging form in this class

public static frmDebug DebuggingForm { get; private set; }

Change the startup code like this

DebuggingForm = new frmDebug();
var mainForm = new frmMain();
Application.Run(frmMain);

From other classes you can access this form like this

Program.DebuggingForm.Log("log this text for me please");         
Program.DebuggingForm.Show();
Olivier Jacot-Descombes
  • 86,431
  • 10
  • 121
  • 160
2

I think you don't have to keep debugging form in memory. You can write logs to some object. E.g. static log:

public static Log
{
    private static List<string> _messages = new List<string>();

    public static Write(string message)
    {
        _messages.Add(message);
    }

    public static IEnumerable<string> Messages 
    { 
       get { return _messages; }
    }
}

You can add log messages from every point of your application via

Log.Write("log this text for me please");

If you need to view those messages just create and show debug form:

private void btnShowDebug_Click(object sender, EventArgs e) {
    using (frmDebug debug = new frmDebug())
                debug.ShowDialog();
}  

In debug form on load assign Log.Messages to your listbox.

Sergey Berezovskiy
  • 215,927
  • 33
  • 392
  • 421
0

A different approach would be to have an Event Sink that would act as a publish subscribe hub for your debug information, that way you don't get a dependency on the debug form all over the place, something like:

public class EventSink
{
    private static readonly IList<Action<string>> _listeners = new List<Action<string>>();

    public static void RegisterListener(Action<string> listener)
    {
        _listeners.Add(listener);
    }

    public static void RaiseEvent(string message)
    {
        foreach (var l in _listeners)
            l(message);
    }
}

in the constructor for your frmDebug you would do:

EventSink.RegisterListener(msg=>listBox1.Items.Add(msg));

and every time you need to add a message to the debug console you would do:

EventSink.RaiseEvent("this is a debug message");

This way you could then register new listeners to do different things, like send you an email when some specific event happens, etc. and you are not coupled with your debug form (decoupling is good:)

Jaime
  • 6,606
  • 1
  • 23
  • 41