So I currently have a working C# console application that I am working on converting to a WinForm app. I am trying to write a function so I can essentially replace all my Console.WriteLine() from when it was a console app, to instead display the same text to the WinForm. I wanted to write a public function that I can call from other classes, that takes a string as an argument and will display the string on the WinForm.
Essentially my main class Program1
will run, then any time it needs to display a message it calls a method from my WinForm class Form1
to print that message to the WinForm
I am a little confused how to do this, I created a label in the WinForm design tab and have a function that was created by visual studio
private void label1_TextChanged(object sender, EventArgs e)
{
}
However wouldn't this function only be called from the winform itself? How could I create the function I described above, or is this not possible using WinForms?
CodePudding user response:
That is a method signature for an event. It doesn't necessarily be TextChanged event but likely it is (it is the naming given by VS for TextChanged on control label1. If that label1 is really a Label, then TextChanged event would be useless anyway.
For displaying things on a form, probably the best control would be a textbox control with Multiline property set to true and ReadOnly to true. ie:
void Main()
{
ShowInfo("Any information string.");
}
void ShowInfo(string information)
{
var f = new Form();
var t = new TextBox
{
Dock = DockStyle.Fill,
Multiline = true,
ReadOnly = true,
Text = information
};
f.Controls.Add(t);
f.Show();
}
CodePudding user response:
Drop a label on your form. Name it UserFeedbackLabel
. Set its Text property so that it's empty.
Then create a simple function like:
private void WriteUserFeedback (string message)
{
UserFeedbackLabel.Text = message;
}
You may also want something like:
private void ClearUserFeedback()
{
WriteUserFeedback(string.Empty);
}
Variations on the theme
If you are going to want to access this from multiple threads, then you will need to use the normal InvokeRequired
/ Invoke
dance (which you can look up).
You could also have a second parameter that indicates whether message is simply a message or an error message and change the label's ForeColor
property.
Finally, if you are really keen, you could put a multiline text box on the form, and append Environment.NewLine
and the message to the textbox's Text
property to show all messages. (if you do that, add vertical scrollbars to the textbox, and move the selection to the end of the text box after each append)
Showing Errors
Add an enum
like this to your project:
public enum MessageType
{
Normal,
Error,
}
Then change WriteUserFeedback
to look like this:
private void WriteUserFeedback(string message, MessageType messageType = MessageType.Normal)
{
UserFeedbackLabel.ForeColor = messageType == MessageType.Normal ? Color.Black : Color.Red;
UserFeedbackLabel.Text = message;
}
Supporting Multiple Threads (and colors)
Here's how to use InvokeRequired
and Invoke
to allow this method to be called from any thread, not just the UI thread. Change WriteUserFeedback
to look like:
private void WriteUserFeedback(string message, MessageType messageType = MessageType.Normal)
{
if (InvokeRequired)
{
Invoke(new Action(() => WriteUserFeedback(message, messageType)));
return;
}
UserFeedbackLabel.ForeColor = messageType == MessageType.Normal ? Color.Black : Color.Red;
UserFeedbackLabel.Text = message;
}
The InvokeRequired
property returns true if the code is not running on the UI thread (everything that touches your form has to run on that thread). The Invoke
call calls the method recursively, after marshalling the call context to the UI thread.
CodePudding user response:
This answer provides:
[...] a public function that I can call from other classes, that takes a string as an argument and will display the string on the WinForm.
The name of this new function is Console.WriteLine()
just like the old one so that your code from the Console app can be copy pasted as is without changing anything. Then, following the example below, the result will be that your string will be displayed on the Winform. But how do we keep the compiler from being confused when there are two versions of Console.WriteLine
available? We simply add a using
declaration at the top of every file that uses Console.WriteLine
. The compiler uses this to disambiguate Console
and now chooses our version of Console.WriteLine
that makes the text go to the Winform.
using System;
using System.IV;
using Console = System.IV.Console; // Set one time for this one file.
using System.Windows.Forms;
Now, instead of having to essentially replace all my Console.WriteLine() from when it was a Console app statements, you can simply copy-paste them and use them just the way they are. This outcome is made possible by writing our own Console
class in the System.IV
namespace. This Console
class has a WriteLine
method that fires a static event named ConsoleWrite
whenever it's called by any of your classes. It is by subscribing to this static event that you can achieve the outcome you describe, where the string will be displayed on the Winform (in this example it goes to a multiline TextBox
named textBoxEcho
).
Simple example: Entering text in the Send box calls the new version of Console.WriteLine
in the Winforms app. So this is how you could create the function you described above. This screenshot demonstrates that it behaves the way you said you wanted it to.
This is a new class named Console
:
namespace System.IV
{
static class Console
{
public static event ConsoleWriteEventHandler ConsoleWrite;
public static void Write(object o) =>
Console.ConsoleWrite?.Invoke(new ConsoleWriteEventArgs(o.ToString(), newLine: false));
public static void WriteLine(object o) =>
Console.ConsoleWrite?.Invoke(new ConsoleWriteEventArgs(o.ToString(), newLine: true));
}
public delegate void ConsoleWriteEventHandler(ConsoleWriteEventArgs e);
public class ConsoleWriteEventArgs : EventArgs
{
public string Text { get; }
public bool NewLine { get; }
public ConsoleWriteEventArgs(string text, bool newLine)
{
Text = text;
NewLine = newLine;
}
}
}
Winform code that uses the custom Console.WriteLine
method.
using System;
using System.IV;
using Console = System.IV.Console; // Tells compiler to use our version file.
using System.Windows.Forms;
namespace winforms_with_console_writeline
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
textBoxSend.KeyDown = onKeyDown;
// Subscribe here to the static event so we can
// do the echo when 'Console.WriteLine' is invoked.
Console.ConsoleWrite = onConsoleWrite;
}
private void onKeyDown(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.Return)
{
e.Handled = e.SuppressKeyPress = true;
// ================================
// Here is the actual call in Winforms. Usage is no different.
Console.WriteLine(textBoxSend.Text);
// ================================
BeginInvoke((MethodInvoker)delegate { textBoxSend.Clear(); });
}
}
// Here is where you can write the text wherever you want to put it.
// I used 'textBoxEcho' but you might want to use 'label' instead.
private void onConsoleWrite(ConsoleWriteEventArgs e)
{
var text = e.NewLine ? $"{e.Text}{Environment.NewLine}" : e.Text;
if(InvokeRequired) Invoke((MethodInvoker)delegate { textBoxEcho.AppendText(text); });
else textBoxEcho.AppendText(text);
}
}
}