I’ve made a class Logging which has the event Logging.Send() triggers an event that delivers a string message to any subscribed classes.
In this case I’m using this method Logging.Send(string message, ELogType type); which triggers the event Logs , which are then listened to by my main Form, which appends the message to a RichTextBox that acts as my text log depending on the message’s type.
My main issue is that this Logging.Send method only sends its message to the log when called in my main Form class. If I call it in another class, for the sake of the example let’s call it Controller, it triggers the event but the subscribing Form does not seem to receive it. What did do wrong?
Here’s an example of my issue:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
public enum ELogType { Primary, Simple, Extra, Readout, Debug }
public class Logging
{
public Logging() { }
public event EventHandler<LoggerMessageArgs> Log;
public bool PostPrimaryLogs = true,
PostSimpleLogs = true,
PostExtraLogs = false,
PostReadoutLogs = false,
PostDebugLogs = false;
public void Send(string message)
{
Send(message, ELogType.Primary);
}
public void Send(string primaryMessage, string simpleMessage)
{
Send(new[] { primaryMessage, simpleMessage }, new[] { ELogType.Primary, ELogType.Simple });
}
public void Send(string message, ELogType type)
{
Console.WriteLine("sendtest"); //to check that the send event was happening.
if ((type == ELogType.Primary && PostPrimaryLogs == true) //is there some way to avoid this giant if statement here?
|| (type == ELogType.Simple && PostSimpleLogs == true)
|| (type == ELogType.Extra && PostExtraLogs == true)
|| (type == ELogType.Readout && PostReadoutLogs == true)
|| (type == ELogType.Debug && PostDebugLogs == true))
Log?.Invoke(this, new LoggerMessageArgs(message, type));
}
public void Send(string[] messages, ELogType[] types)
{
if (messages.Length == types.Length)
for (int i = 0; i < messages.Length; i++)
{
Send(messages[i], types[i]);
}
}
}
public class LoggerMessageArgs
{
public string Message { get; }
public ELogType LogType { get; }
public LoggerMessageArgs(string message)
{
Message = message;
LogType = ELogType.Primary;
}
public LoggerMessageArgs(string message, ELogType logType)
{
Message = message;
LogType = logType;
}
}
public class Controller
{
Logging Logger = new Logging();
public async Task DoControl()
{
Logger.Send("beginning doControl", ELogType.Primary);
await Task.Delay(1000); //do work
Logger.Send("finished doControl", ELogType.Primary);
return Task.CompletedTask;
}
}
public partial class Form1: Form
{
Logging Logger = new Logging();
//needs an additional RichTextBox LogTextBox, and a RichTextBox SimpleLogTextBox. The simple log box isn't super important to the issue at hand. It's mostly used as a status indicator.
Form1()
{
InitializeComponent();
Logger.Log += (sender, e) =>
{
Action action = () =>
{
if (e.LogType == ELogType.Primary || e.LogType == ELogType.Debug || e.LogType == ELogType.Extra || e.LogType == ELogType.Readout)
{
LogTextBox.AppendText($@"{DateTime.Now:hh\:mm\:ss\.ffff}: {e.Message}{Environment.NewLine}");
LogTextBox.SelectionStart = LogTextBox.Text.Length;
LogTextBox.ScrollToCaret();
}
if (e.LogType == ELogType.Simple)
{
SimpleLogTextBox.AppendText($@"{e.Message}{Environment.NewLine}");
SimpleLogTextBox.SelectionStart = SimpleLogTextBox.Text.Length;
SimpleLogTextBox.ScrollToCaret();
}
};
if (LogTextBox.InvokeRequired)
{
LogTextBox.Invoke(action);
}
else
{
action();
}
};
}
private void TestButton_Click(object sender, EventArgs e)
{
Logger.Send(new string[] { "Form Primary Log Test", "Form Simple Log Test" }, new ELogType[] { ELogType.Primary, ELogType.Simple }); //logs come back from this one
Controller.DoControl() //no logs come back from this one.
}
}
With this code, I get the string "{datetime} Form Primary Test Log" in the primary log, but complete radio silence from DoControl.
How do I fix this?
Thank you.
>Solution :
The problem is that the main form attaches only to the event of its own instance of the logger, but the other forms create other instances.
One possible solution is to make your event static, so that there is only one instance of the event, even if you create several instances of the logger.
public static event EventHandler<LoggerMessageArgs> Log;
Another solution is to make the logger a singleton.
public class Logging
{
public static readonly Logging Instance = new();
private Logging() { } // Hide the constructor.
}
You can get the only instance with:
Logging Logger = Logging.Instance;
Or use it directly:
Logging.Instance.Send("beginning doControl", ELogType.Primary);