Copying TextReader/Writer to more places - c#

I have console application that can turn winform with richtextbox. I want to redirect console and that richtextbox to each other. So whenever I write to them text will copy to another. My problem is when I'm waiting for ReadLine. I would like to react to first ReadLine. This is my code:
class ConsoleFormReDirectWriter : TextWriter
{
TextWriter t;
RichTextBox r;
public ConsoleFormReDirectWriter(TextWriter TextWriter, RichTextBox FormOut)
{
t = TextWriter;
r = FormOut;
}
public override void Write(char value)
{
t.Write(value);
RichTextBoxExtensions.AppendText(r, value +"", Color.White);
}
public override void WriteLine(string line)
{
t.WriteLine(line);
RichTextBoxExtensions.AppendText(r, line+"\n", Color.White);
}
public override Encoding Encoding
{
get { return Encoding.Default; }
}
}
class ConsoleFormReDirectReader : TextReader
{
Queue<string> ReadLineQ = new Queue<string>();
public void AddToReadLineQueue(string s)
{
ReadLineQ.Enqueue(s);
}
public override string ReadLine()
{
string line = "";
while (true)
{
if (ReadLineQ.Count != 0) { line = ReadLineQ.Dequeue(); break; }
}
return line;
}
}
Then im handling press enter event on richtext box and apending queue by currentLine. I dont know how to make something similar with console.
or is there a better method of doing same?
PS: i can make new thread that will be asking console for readline in infinite loop, and when readline return something than i can append the queue. But that seems very unefective.
After a while i found semi solution. I used Console.readline with timeout and
now I have my Readline:
public override string ReadLine()
{
bool toConsole=false, toUI=false;
string line = "";
while (true)
{
if (ReadLineQ.Count != 0)
{ line = ReadLineQ.Dequeue();
toConsole = true;
break;
}
try
{
line = DelayReader.ReadLine(50);
}
catch(TimeoutException) { continue; }
toUI = true;
break;
}
if (toConsole) t.WriteLine(line);
if (toUI) RichTextBoxExtensions.AppendText(r, line + "\n", Color.White);
return line;
}
And Console.Readline with delay i foun here: ReadLine(delay)

Related

How do you run and display output from a console to a rich text box in a winform? [duplicate]

I'm building this application in Visual Studio 2010 using C#.
Basically there are 2 files, form1.cs (which is the windows form) and program.cs (where all the logic lies).
//form1.cs
public partial class Form1 : Form
{
//runButton_click function
}
//program.cs
class Program
{
static void Main()
{
while(blah-condition)
{
//some calculation
Console.WriteLine("Progress " + percent + "% completed.");
}
}
}
There is a Run button and a blank textbox.
When the user hits the Run button, program.cs will perform some task and constantly printing out the progress using Console.WriteLine() onto the console (command prompt).
Question: How can I print to the textbox on form1 instead of printing into command prompt?
I will need to print the progress constantly without any user action.
Thanks in advance!
By the way, it doesn't have to be a textbox, it can be a label or something else that can take text. I chose textbox because it makes more sense to me.
Start by creating a new TextWriter that is capable of writing to a textbox. It only needs to override the Write method that accepts a char, but that would be ungodly inefficient, so it's better to overwrite at least the method with a string.
public class ControlWriter : TextWriter
{
private Control textbox;
public ControlWriter(Control textbox)
{
this.textbox = textbox;
}
public override void Write(char value)
{
textbox.Text += value;
}
public override void Write(string value)
{
textbox.Text += value;
}
public override Encoding Encoding
{
get { return Encoding.ASCII; }
}
}
In this case I've had it just accept a Control, which could be a Textbox, a Label, or whatever. If you want to change it to just a Label that would be fine.
Then just set the console output to a new instance of this writer, pointing to some textbox or label:
Console.SetOut(new ControlWriter(textbox1));
If you want the output to be written to the console as well as to the textbox we can use this class to create a writer that will write to several writers:
public class MultiTextWriter : TextWriter
{
private IEnumerable<TextWriter> writers;
public MultiTextWriter(IEnumerable<TextWriter> writers)
{
this.writers = writers.ToList();
}
public MultiTextWriter(params TextWriter[] writers)
{
this.writers = writers;
}
public override void Write(char value)
{
foreach (var writer in writers)
writer.Write(value);
}
public override void Write(string value)
{
foreach (var writer in writers)
writer.Write(value);
}
public override void Flush()
{
foreach (var writer in writers)
writer.Flush();
}
public override void Close()
{
foreach (var writer in writers)
writer.Close();
}
public override Encoding Encoding
{
get { return Encoding.ASCII; }
}
}
Then using this we can do:
Console.SetOut(new MultiTextWriter(new ControlWriter(textbox1), Console.Out));
I use sth like this for a listbox:
public class ListBoxWriter : TextWriter //this class redirects console.writeline to debug listbox
{
private readonly ListBox _list;
private StringBuilder _content = new StringBuilder();
public ListBoxWriter(ListBox list)
{
_list = list;
}
public override Encoding Encoding
{
get { return Encoding.UTF8; }
}
public override void Write(char value)
{
base.Write(value);
_content.Append(value);
if (value != '\n') return;
if (_list.InvokeRequired)
{
try
{
_list.Invoke(new MethodInvoker(() => _list.Items.Add(_content.ToString())));
_list.Invoke(new MethodInvoker(() => _list.SelectedIndex = _list.Items.Count - 1));
_list.Invoke(new MethodInvoker(() => _list.SelectedIndex = -1));
}
catch (ObjectDisposedException ex)
{
Console.WriteLine(Resources.Exception_raised + " (" + ex.Message + "): " + ex);
}
}
else
{
_list.Items.Add(_content.ToString());
_list.SelectedIndex = _list.Items.Count - 1;
_list.SelectedIndex = -1;
}
_content = new StringBuilder();
}
}
and in my main application:
_writer = new ListBoxWriter(DebugWin); // DebugWin is the name og my listbox
Console.SetOut(_writer);
Don't know if it will work, but you could try to redirect console output.
Use Console.SetOut() and create derivative of TextWriter which overrides WriteLine() method and simply assign method parameter to your TextBox.Text
Should work.
put textbox on the form ( multiple lines enabled) or text area then you can do in your loop
txtOutput.Text += "Progress " + percent + "% completed." + Environment.NewLine();

AutoResetEvent on C# and calling a Form

I am starting 2 different threads on a C# program. The first is a form which should sniff the clipboard and the second is starting a java Program which is writing into the clipboard.
The calling looks like this:
new Thread(() => StartClipListening()).Start();
new Thread(() => executeScripts(path2Sikuli, path2Scripts, SikVars)).Start();
SuccFailEvent.WaitOne();
SuccFailEvent.Reset();
The second thread is working fine, but the first is being called and write after that closed before even starting the Clipboard sniffing.
It looks like this:
private void StartClipListening()
{
var clites = new CBForm();
clites.Start_Lintening(this);
clites.Show();
}
In the Form I am doing the following stuff:
public void Start_Lintening(TradingExecution trex)
{
this.trex = trex;
//this.are = are;
AddClipboardFormatListener(this.Handle);
}
const int WM_CLIPBOARDUPDATE = 0x31D;
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_CLIPBOARDUPDATE:
IDataObject iData = Clipboard.GetDataObject();
if (iData.GetDataPresent(DataFormats.Text))
{
label1.Text = (string)iData.GetData(DataFormats.Text);
trex.ClipboardMonitor_OnClipboardChange (label1.Text);
Stop_listening();
this.Close();
}
break;
default:
base.WndProc(ref m);
break;
}
}
public void Stop_listening()
{
RemoveClipboardFormatListener(this.Handle);
}
and the called method is this one:
public void ClipboardMonitor_OnClipboardChange(string data)
{
var ClipboardText = data;
string[] dataInSlices = ClipboardText.Split(';');
try
{
Clipboard.Clear();
}
catch (Exception)
{
}
if (dataInSlices.Count() > 1)
{
if (dataInSlices[1] == "Success")
{
logger.Info("Sccess executing Sikuli");
SuccessExecute = true;
FailureStep = 0;
Failureval = dataInSlices[2];
SuccFailEvent.Set();
}
else if (dataInSlices[1] == "Failure")
{
logger.Info("Failure executing Sikuli");
try
{
FailureStep = Int32.Parse(dataInSlices[0]);
}
catch (Exception)
{
}
SuccessExecute = false;
Failureval = dataInSlices[2];
SuccFailEvent.Set();
}
}
}
Thanks in advance for your help!
i've solved it now on the following way:
private void StartClipListening()
{
var clites = new CBForm();
clites.Start_Lintening(this);
clites.Show();
}
private void StarttwoTasks(string path2Sikuli, string path2Scripts, SikuliVariables SikVars)
{
StartClipListening();
new Thread(() => executeScripts(path2Sikuli, path2Scripts, SikVars)).Start();
new Thread(() => waitforthat###()).Start();
}
private void waitforthat###()
{
SuccFailEvent.WaitOne();
SuccFailEvent.Reset();
}
Now i have a polling problem but thats another story. Thanks!

Redirecting Console.WriteLine() to Textbox

I'm building this application in Visual Studio 2010 using C#.
Basically there are 2 files, form1.cs (which is the windows form) and program.cs (where all the logic lies).
//form1.cs
public partial class Form1 : Form
{
//runButton_click function
}
//program.cs
class Program
{
static void Main()
{
while(blah-condition)
{
//some calculation
Console.WriteLine("Progress " + percent + "% completed.");
}
}
}
There is a Run button and a blank textbox.
When the user hits the Run button, program.cs will perform some task and constantly printing out the progress using Console.WriteLine() onto the console (command prompt).
Question: How can I print to the textbox on form1 instead of printing into command prompt?
I will need to print the progress constantly without any user action.
Thanks in advance!
By the way, it doesn't have to be a textbox, it can be a label or something else that can take text. I chose textbox because it makes more sense to me.
Start by creating a new TextWriter that is capable of writing to a textbox. It only needs to override the Write method that accepts a char, but that would be ungodly inefficient, so it's better to overwrite at least the method with a string.
public class ControlWriter : TextWriter
{
private Control textbox;
public ControlWriter(Control textbox)
{
this.textbox = textbox;
}
public override void Write(char value)
{
textbox.Text += value;
}
public override void Write(string value)
{
textbox.Text += value;
}
public override Encoding Encoding
{
get { return Encoding.ASCII; }
}
}
In this case I've had it just accept a Control, which could be a Textbox, a Label, or whatever. If you want to change it to just a Label that would be fine.
Then just set the console output to a new instance of this writer, pointing to some textbox or label:
Console.SetOut(new ControlWriter(textbox1));
If you want the output to be written to the console as well as to the textbox we can use this class to create a writer that will write to several writers:
public class MultiTextWriter : TextWriter
{
private IEnumerable<TextWriter> writers;
public MultiTextWriter(IEnumerable<TextWriter> writers)
{
this.writers = writers.ToList();
}
public MultiTextWriter(params TextWriter[] writers)
{
this.writers = writers;
}
public override void Write(char value)
{
foreach (var writer in writers)
writer.Write(value);
}
public override void Write(string value)
{
foreach (var writer in writers)
writer.Write(value);
}
public override void Flush()
{
foreach (var writer in writers)
writer.Flush();
}
public override void Close()
{
foreach (var writer in writers)
writer.Close();
}
public override Encoding Encoding
{
get { return Encoding.ASCII; }
}
}
Then using this we can do:
Console.SetOut(new MultiTextWriter(new ControlWriter(textbox1), Console.Out));
I use sth like this for a listbox:
public class ListBoxWriter : TextWriter //this class redirects console.writeline to debug listbox
{
private readonly ListBox _list;
private StringBuilder _content = new StringBuilder();
public ListBoxWriter(ListBox list)
{
_list = list;
}
public override Encoding Encoding
{
get { return Encoding.UTF8; }
}
public override void Write(char value)
{
base.Write(value);
_content.Append(value);
if (value != '\n') return;
if (_list.InvokeRequired)
{
try
{
_list.Invoke(new MethodInvoker(() => _list.Items.Add(_content.ToString())));
_list.Invoke(new MethodInvoker(() => _list.SelectedIndex = _list.Items.Count - 1));
_list.Invoke(new MethodInvoker(() => _list.SelectedIndex = -1));
}
catch (ObjectDisposedException ex)
{
Console.WriteLine(Resources.Exception_raised + " (" + ex.Message + "): " + ex);
}
}
else
{
_list.Items.Add(_content.ToString());
_list.SelectedIndex = _list.Items.Count - 1;
_list.SelectedIndex = -1;
}
_content = new StringBuilder();
}
}
and in my main application:
_writer = new ListBoxWriter(DebugWin); // DebugWin is the name og my listbox
Console.SetOut(_writer);
Don't know if it will work, but you could try to redirect console output.
Use Console.SetOut() and create derivative of TextWriter which overrides WriteLine() method and simply assign method parameter to your TextBox.Text
Should work.
put textbox on the form ( multiple lines enabled) or text area then you can do in your loop
txtOutput.Text += "Progress " + percent + "% completed." + Environment.NewLine();

Unresponsive UI when too fast and too much data needs to be updated

I made a control to log messages from different threads to screen. It uses rich text box to display formatted text.
When there are 20 threads which append their messages every 200-250ms the main UI becomes unresponsive for some time and after the messages waiting are processed the UI starts to response again. When the threads are running the moving of the window is not smooth.
Message writing to rich text box is synchronised with locks.
What can you suggest to improve the performance? I'm planning to run 100 threads.
Here is my code. I redirect the console output(s) to it and it logs everything that's going on and displays in formatted form inside a rich text box.
public void RedirectStandardOutput()
{
Console.SetOut(ConsoleStream);
System.Diagnostics.Debug.Listeners.Add(new System.Diagnostics.TextWriterTraceListener(Console.Out));
System.Diagnostics.Debug.AutoFlush = true;
}
After the console is redirected all Console.WriteLine("bla bla"); is written to screen.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using CoreLib.Parsers;
namespace ConsoleWidget
{
public class ConsoleStream : System.IO.TextWriter
{
private readonly object _textBoxLock = new object();
public RichTextBox TextBox { get; set; }
public List<TextFormat> TextFormatList { get; set; }
public bool AutoClear { get; set; }
public int AutoClearLength { get; set; }
public bool AutoSave { get; set; }
public string AutoSaveDir { get; set; }
public ConsoleStream()
{
TextFormatList = new List<TextFormat>();
}
public void AppendText(string text)
{
if (TextBox == null) return;
var textLength = TextBox.TextLength;
if (AutoClear && textLength > AutoClearLength)
{
if (AutoSave)
{
var dir = string.Format(#"{0}\{1}\{2}", Environment.CurrentDirectory, AutoSaveDir, CoreLib.Extensions.DateTimeExtensions.DateTimeNowDir);
if (!System.IO.Directory.Exists(dir))
System.IO.Directory.CreateDirectory(dir);
var path = string.Format(#"{0}\{1}.log", dir, CoreLib.Extensions.DateTimeExtensions.GetDateTimeNowFileName);
TextBox.SaveFile(path);
}
TextBox.Clear();
}
TextBox.AppendText(text);
// Format text.
foreach (var textFormat in TextFormatList)
{
int beginIndex;
int length;
if (textFormat.GetFormatProperties(text, out beginIndex, out length))
{
// RichTextBox counts newline "\r\n" which is double char as single char.
// Causes shifting in selection starts. The lines below count the "\r" chars before the beginIndex.
var leftText = text.Substring(0, beginIndex);
var newLineCount = leftText.Count(c => c == '\r');
TextBox.SelectionStart = textLength + beginIndex - newLineCount;
TextBox.SelectionLength = length;
if (!textFormat.Color.IsEmpty)
TextBox.SelectionColor = textFormat.Color;
if (textFormat.Font != null)
TextBox.SelectionFont = textFormat.Font;
}
}
TextBox.ScrollToCaret();
}
public void Clear()
{
lock (_textBoxLock)
{
TextBox.Clear();
}
}
public int TextLength
{
get
{
lock (_textBoxLock)
{
return TextBox.TextLength;
}
}
}
public void SaveAs(string path)
{
lock (_textBoxLock)
{
TextBox.SaveFile(path);
}
}
public override Encoding Encoding
{
get { return Encoding.Default; }
}
public override void Write(string value)
{
if (TextBox == null) return;
var action = (Action)(() => AppendText(value));
lock (_textBoxLock)
{
if (TextBox.InvokeRequired)
TextBox.BeginInvoke(action);
else
action();
}
}
public override void WriteLine()
{
Write(NewLine);
}
public override void WriteLine(string value)
{
Write(value);
WriteLine();
}
}
}
Have them write to a buffer RichTextBox (one that's not actually part of your form) and only append the buffer to the UI RichTextBox every 250 ms or so.
Have your worker threads add their data to some sort of queue/list and then have the main thread add a batch of new data from the store of new data every second/half second (tune to fit your process).
Something basic like this would probably be fine:
public class DataStore<T>{
private object _lock = new object();
private List<T> _data = new List<T>();
public void Add(T data){
lock (_lock){
_data.Add(data);
}
}
public T[] TakeWork(){
T[] result;
lock (_lock){
result= _data.ToArray();
_data.Clear();
}
return result;
}
}
Just create a DataStore and have your work threads use the Add function to add work to be displayed then do
foreach (var s in _dataStore.TakeWork()){
richTextBox.AppendText(s);
}
in a System.Windows.Forms.Timer event. You will probably want to trim the rich text box text as well though since your app will start to slow down if you just keep pumping data in all day....
Maybe you can try thread pool or task instead to manage your threads better.

Exit from while loop when stream closed?

This is a network stream problem but i simplified test case to Console input:
i started a thread ehich waits 2 seconds and closes the stream reader. But after
closing stream/stream reader. While loop still waits for sr.ReadLine() method.
i wan't to make it exit loop automatically when closes the stream/stream reader.
i tried also the thread safe version of Stream Reader; TextReader.synchronized.
But the result is the same.
using System;
using System.IO;
using System.Threading;
namespace StreamReaderTest {
class Program {
static void Main(string[] args) {
new Program();
}
private StreamReader sr;
public Program() {
sr = new StreamReader(Console.OpenStandardInput());
new Thread(new ThreadStart(this.Close)).Start(); ;
string line;
while ((line = sr.ReadLine()) != null) {
Console.WriteLine(line);
}
}
public void Close() {
Thread.Sleep(2000);
sr.Close();
Console.WriteLine("Stream Closed");
}
}
}
In the console example, you may be able to use Peek to check if a character is available.
For a network stream, you may be able to use Length to check if any input is available.
If you don't want it to block, never read without input already pending.
Will this work for you?
class Program
{
static void Main(string[] args)
{
new Program();
}
private StreamReader sr;
private bool forcefullyClose = false;
public Program()
{
new Thread(new ThreadStart(this.Close)).Start(); ;
using (sr = new StreamReader(Console.OpenStandardInput()))
{
string line;
while (!forcefullyClose && (line = sr.ReadLine()) != null)
{
Console.WriteLine(line);
}
}
}
public void Close()
{
Thread.Sleep(5000);
forcefullyClose = true;
Console.WriteLine("Stream Closed");
}
}
Encapsulate the stream operations in a class, so that you can easily synchronise the methods to make them thread safe and make the ReadLine notice the closed state:
using System;
using System.IO;
using System.Threading;
namespace StreamReaderTest {
class SynchronizedReader {
private StreamReader _reader;
private object _sync;
public SynchronizedReader(Stream s) {
_reader = new StreamReader(s);
_sync = new object();
}
public string ReadLine() {
lock (_sync) {
if (_reader == null) return null;
return _reader.ReadLine();
}
}
public void Close() {
lock (_sync) {
_reader.Close();
_reader = null;
}
}
}
class Program {
static void Main(string[] args) {
new Program();
}
private SynchronizedReader reader;
public Program() {
reader = new SynchronizedReader(Console.OpenStandardInput());
new Thread(new ThreadStart(this.Close)).Start();
string line;
while ((line = reader.ReadLine()) != null) {
Console.WriteLine(line);
}
}
public void Close() {
Thread.Sleep(2000);
reader.Close();
Console.WriteLine("Stream Closed");
}
}
}
To prevent the blocking that the ReadLine method can do while waiting for a complete line, you might want to read a character at a time from the stream instead. Note that you would have to check the closed status inside the loop that reads the characters:
class SynchronizedReader {
private Stream _stream;
private object _sync;
public SynchronizedReader(Stream s) {
_stream = s;
_sync = new object();
}
public string ReadLine() {
lock (_sync) {
StringBuilder line = new StringBuilder();
while (true) {
if (_stream == null) return null;
int c = _stream.ReadByte();
switch (c) {
case 10: break;
case 13:
case -1: return line.ToString();
default: line.Append((char)c);
}
}
}
}
public void Close() {
lock (_sync) {
_stream.Close();
_stream = null;
}
}
}

Categories