Home > database >  C# Replacing Console.WriteLine() with Winform label with changing text
C# Replacing Console.WriteLine() with Winform label with changing text

Time:08-06

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.

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:

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:

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:

The goal of this answer it to make it so that you, instead of having to essentially replace all my Console.WriteLine() from when it was a Console app statements, can copy-paste them and use them just the way they are. So, here's the thing. It's perfectly OK for you to make your own Console class that has a WriteLine method. For example, "our" version fires a static event named ConsoleWrite whenever it's called.

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;
        }
    }
}

But you will need to tell Winforms which one to use by disambiguating it with using statements.

using System;
using System.IV;
using Console = System.IV.Console; // Set one time for this one file.
using System.Windows.Forms;

The advantage of doing this is that you can copy-paste the lines from your Console app to your Winforms version without having to change them or rewrite the syntax. If you're doing cross-platform like Xamarin for example, you can use the same strategy to keep all your MessageBox.Show statements (handling the static event and mapping it to the iOS or Android platform). For me this has been a lifesaver when porting Desktop apps to Mobile platforms.

Here is an example where entering a line in textBoxSend makes a call to Console.WriteLine. The event handler for the ConsoleWrite event echoes it in textBoxEcho.

winform


Winform code that uses the custom Console.WriteLine method.

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();  });
            }
        }
        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);
        }
    }
}
  • Related