Since I was having some difficulties to debug the next step for Scrolly - A (very) simple infinite mouse "scroll", I've decided to make a console-like class that allows to add messages to it.
This allows you to show some messages. It also allows to pass any object you want, but it will be converted to string. You can add information, error and warning messages as well, which will have a matching color for each.
using System;
using System.Drawing;
using System.Windows.Forms;
namespace ConsoleWindow
{
public sealed partial class Console
{
private static volatile Console instance = new Console();
private static ConsoleForm form = new ConsoleForm();
public static Console Instance
{
get
{
return instance;
}
}
public static bool autoShow = true;
private static string newline = "\r\n";
private static string line = "================================================================================";
/// <summary>
/// Forces the prompt to go to the end of the text.
/// </summary>
private static void scrollToEnd()
{
form.consoleOutput.SelectionStart = form.consoleOutput.Text.Length;
form.consoleOutput.ScrollToCaret();
}
/// <summary>
/// Adds a message to the console.
/// </summary>
/// <param name="message">Message object to log.</param>
public static bool Log<T>(T message)
{
if (autoShow)
{
Show();
}
scrollToEnd();
form.consoleOutput.AppendText(message.ToString() + newline);
scrollToEnd();
return true;
}
/// <summary>
/// Adds an error message to the console.
/// </summary>
/// <param name="message">Message object to log.</param>
public static bool Error<T>(T message)
{
if (autoShow)
{
Show();
}
scrollToEnd();
form.consoleOutput.SelectionColor = Color.Red;
form.consoleOutput.SelectionLength = 0;
form.consoleOutput.AppendText("(!) " + message.ToString() + newline);
scrollToEnd();
form.consoleOutput.SelectionColor = form.consoleOutput.ForeColor;
return true;
}
/// <summary>
/// Adds a warning message to the console.
/// </summary>
/// <param name="message">Message object to log.</param>
public static bool Warning<T>(T message)
{
if (autoShow)
{
Show();
}
scrollToEnd();
form.consoleOutput.SelectionColor = Color.Yellow;
form.consoleOutput.SelectionLength = 0;
form.consoleOutput.AppendText(@"/!\ " + message.ToString() + newline);
scrollToEnd();
form.consoleOutput.SelectionColor = form.consoleOutput.ForeColor;
return true;
}
/// <summary>
/// Adds a warning error message to the console.
/// </summary>
/// <param name="message">Message object to log.</param>
public static bool Info<T>(T message)
{
if (autoShow)
{
Show();
}
scrollToEnd();
form.consoleOutput.SelectionColor = Color.LightBlue;
form.consoleOutput.SelectionLength = 0;
form.consoleOutput.AppendText("[!] " + message.ToString() + newline);
scrollToEnd();
form.consoleOutput.SelectionColor = form.consoleOutput.ForeColor;
return true;
}
/// <summary>
/// Clears the console text.
/// </summary>
public static void Clear()
{
form.consoleOutput.Clear();
bool autoShowOriginal = autoShow;
autoShow = false;
autoShow = autoShowOriginal;
}
/// <summary>
/// Clears the console text. Returns false on exception.
/// </summary>
/// <param name="filename">Filename path in a string</param>
/// <param name="log">Logs a message indicating the operation successfulness</param>
/// <param name="clear">Clears the console on success</param>
public static bool SaveToFile(string filename, bool log = true, bool clear = false)
{
try
{
System.IO.File.WriteAllLines(form.saveFile.FileName, form.consoleOutput.Lines);
if (clear)
{
Clear();
}
if (log == true)
{
Log(line);
Info("File saved: " + filename);
}
return true;
}
catch (System.IO.IOException e)
{
if (log == true)
{
Log(line);
Error("Failed to save: " + filename);
Error(e);
}
return false;
}
}
/// <summary>
/// Closes the console.
/// </summary>
public static void Hide()
{
form.Hide();
}
/// <summary>
/// Closes the console. Warning: output will be lost!
/// </summary>
public static void Close()
{
form.Hide();
Clear();
}
/// <summary>
/// Shows the console.
/// </summary>
public static void Show()
{
form.Show();
}
/// <summary>
/// Private form functions and elements.
/// </summary>
private class ConsoleForm : Form
{
private static System.ComponentModel.IContainer components = new System.ComponentModel.Container();
public RichTextBox consoleOutput = new RichTextBox();
public ContextMenuStrip consoleMenu = new ContextMenuStrip(components);
public ToolStripMenuItem consoleMenuSave = new ToolStripMenuItem();
public ToolStripMenuItem consoleMenuClose = new ToolStripMenuItem();
public SaveFileDialog saveFile = new SaveFileDialog();
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
this.Dispose(disposing);
}
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.consoleMenu.SuspendLayout();
this.SuspendLayout();
//
// consoleOutput
//
this.consoleOutput.BackColor = System.Drawing.Color.Black;
this.consoleOutput.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.consoleOutput.ContextMenuStrip = this.consoleMenu;
this.consoleOutput.DetectUrls = false;
this.consoleOutput.Dock = System.Windows.Forms.DockStyle.Fill;
this.consoleOutput.Font = new System.Drawing.Font("Consolas", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.consoleOutput.ForeColor = System.Drawing.Color.Silver;
this.consoleOutput.Location = new System.Drawing.Point(0, 0);
this.consoleOutput.Name = "consoleOutput";
this.consoleOutput.ReadOnly = true;
this.consoleOutput.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.ForcedVertical;
this.consoleOutput.Size = new System.Drawing.Size(583, 261);
this.consoleOutput.TabIndex = 0;
this.consoleOutput.TabStop = false;
this.consoleOutput.Text = "";
//
// consoleMenu
//
this.consoleMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.consoleMenuSave,
this.consoleMenuClose
});
this.consoleMenu.Name = "consoleMenu";
this.consoleMenu.ShowImageMargin = false;
this.consoleMenu.Size = new System.Drawing.Size(107, 48);
//
// consoleMenuSave
//
this.consoleMenuSave.Name = "consoleMenuSave";
this.consoleMenuSave.Size = new System.Drawing.Size(106, 22);
this.consoleMenuSave.Text = "&Save to file";
this.consoleMenuSave.Click += new System.EventHandler(this.consoleMenuSave_Click);
//
// consoleMenuClose
//
this.consoleMenuClose.Name = "consoleMenuClose";
this.consoleMenuClose.Size = new System.Drawing.Size(106, 22);
this.consoleMenuClose.Text = "&Close";
this.consoleMenuClose.Click += new System.EventHandler(this.consoleMenuClose_Click);
//
// saveFile
//
this.saveFile.DefaultExt = "txt";
this.saveFile.Filter = "Text file (*.txt)|*.txt|All files|*.*";
this.saveFile.Title = "Save console output";
//
// consoleForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(583, 261);
this.Controls.Add(this.consoleOutput);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "consoleForm";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.Text = "Console";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.consoleForm_FormClosing);
this.consoleMenu.ResumeLayout(false);
this.ResumeLayout(false);
}
public ConsoleForm()
{
InitializeComponent();
}
private void consoleMenuSave_Click(object sender, EventArgs e)
{
if (saveFile.ShowDialog() == DialogResult.OK)
{
Console.SaveToFile(saveFile.FileName);
}
}
private void consoleMenuClose_Click(object sender, EventArgs e)
{
Console.Close();
}
private void consoleForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason != CloseReason.ApplicationExitCall
|| e.CloseReason != CloseReason.TaskManagerClosing)
{
e.Cancel = true;
Console.Close();
}
}
}
}
}
It is quite a long wall of code, I know. Most of it is just to set the elements' properties. Most of it is copy-paste from the generated code. I've tried to keep it to a minimum.
The whole idea is to just throw this into a file and you are ready to use it. It should show on your first message. It can be easily disabled.
My main concern is in the whole pattern, which is ... bad... really bad! I have a namespace with a partial class with a private class that inherits from the Form
class... But, I can't find any better way to keey the form in a separate class and still private.
In terms of readability, what other crimes am I committing?