Console Application and WPF

S

shapper

Hello,

I have a Console application that performs some actions and in the
window I can see text that is saying what is doing.
I know created a WPF application with the same code.

Is it possible to show the same output text in a TextBlock?

Thanks,
Miguel
 
P

Peter Duniho

shapper said:
Hello,

I have a Console application that performs some actions and in the
window I can see text that is saying what is doing.
I know created a WPF application with the same code.

Is it possible to show the same output text in a TextBlock?

Yes. You could redirect the standard output of the Console class by
calling SetOut(). But IMHO a better way would be to abstract your
output via a custom class (e.g. a subclass of TextWriter you've
created), and then use that abstraction to allow for the output to
continue to go to the Console's standard output while intercepting it
for your WPF control.

Pete
 
S

shapper

Yes.  You could redirect the standard output of the Console class by
calling SetOut().  But IMHO a better way would be to abstract your
output via a custom class (e.g. a subclass of TextWriter you've
created), and then use that abstraction to allow for the output to
continue to go to the Console's standard output while intercepting it
for your WPF control.

I am not sure if I fully understand what you are saying but I am
starting as follows:

private class ConsoleLog : TextWriter {

private TextWriter _writer;
private TextBlock _output;

public ConsoleLog(TextBlock output, TextWriter writer) {
_writer = writer;
_output = output;
} // ConsoleLog

public override void Write(String s) {

_writer.Write(s);
_output.Text = String.Concat(_output.Text, s);

} // Write

} // ConsoleLog

_writer should be the original writer and output should be the
TextBlock control where I would like to display the output.

Then I would use something like:

using (ConsoleLog cl = new ConsoleLog(tbOutput, Console.Out)) {
Console.SetOut(cl);
}

Is this what you mean?

Thanks,
Miguel
 
P

Peter Duniho

shapper said:
[...]
Then I would use something like:

using (ConsoleLog cl = new ConsoleLog(tbOutput, Console.Out)) {
Console.SetOut(cl);
}

Is this what you mean?

Not quite, but that approach looks like it should work too.

What I really meant was for you to have a class like your ConsoleLog
class, which you use _instead_ of the Console class directly for your
logging. That class would then handle directing the output as desired
(either to the TextBlock, to the Console, or both).

There are lots of different ways you can do this. Just pick the one
that seems to fit your existing architecture the best.

Pete
 
S

shapper

Not quite, but that approach looks like it should work too.

Not quite, but that approach looks like it should work too.

I was missing:
public override Encoding Encoding { get { return Encoding.Default; } }

But I think I found what do you mean:
http://dpatrickcaldwell.blogspot.com/2008/12/using-proxy-pattern-to-write-to.html

If this is what you meant I think it is really interesting.

I am not quite sure how to use it with a TextBlock ... To keep being
updated.
But I am going to give it a try.
 
S

shapper

Hi,

I am using the TextWriterProxy in the WPF application as follows:

TextBlock output = new TextBlock();

TextWriterProxy proxy = new TextWriterProxy();
proxy.Add(Console.Out);

StringBuilder sb = new StringBuilder();
StringWriter resultStringWriter = new StringWriter(sb);
proxy.Add(resultStringWriter);
output.Text = sb.ToString();

However, text block does not show any text. Any idea how to do this
integration?

The TextWriterProxy code is as follows:

public class TextWriterProxy : TextWriter {

private List<TextWriter> _writers = new List<TextWriter>();

public override Encoding Encoding { get { return
Encoding.Default; } } // Encoding

public override string NewLine {
get { return base.NewLine; }
set {
foreach (TextWriter tw in _writers)
tw.NewLine = value;
base.NewLine = value;
}
} // NewLine

public void Add(TextWriter writer) {
if (!_writers.Contains(writer))
_writers.Add(writer);
} // Add

public bool Remove(TextWriter writer) {
return _writers.Remove(writer);
} // Remove

public override void Write(char value) {
foreach (TextWriter tw in _writers)
tw.Write(value);
base.Write(value);
} // Write

public override void Close() {
foreach (TextWriter tw in _writers)
tw.Close();
base.Close();
} // Close

protected override void Dispose(bool disposing) {
foreach (TextWriter tw in _writers)
tw.Dispose();
base.Dispose(disposing);
} // Dispose

public override void Flush() {
foreach (TextWriter tw in _writers)
tw.Flush();
base.Flush();
} // Flush

} // TextWriterProxy

Thanks,
Miguel
 
P

Peter Duniho

shapper said:
Hi,

I am using the TextWriterProxy in the WPF application as follows:

TextBlock output = new TextBlock();

TextWriterProxy proxy = new TextWriterProxy();
proxy.Add(Console.Out);

StringBuilder sb = new StringBuilder();
StringWriter resultStringWriter = new StringWriter(sb);
proxy.Add(resultStringWriter);
output.Text = sb.ToString();

However, text block does not show any text. Any idea how to do this
integration?

Not without a concise-but-complete code example.

The code you posted does not in and of itself show anything that should
necessarily add text to your TextBlock class. The StringWriter you've
added to your proxy isn't going to have any text in it until you
actually write some text to the proxy. In the code you show, the only
time you set the Text property of your TextBlock is immediately after
initializing the proxy, before you've actually written anything to it.

It seems to me that if you want immediate updates in your TextBlock
according to writes to the proxy, you need a custom TextWriter (in
addition to the proxy, or just make the proxy understand the TextBlock
instance specifically) that when written to, it updates the TextBlock
accordingly (by adding the text to the TextBlock's current text).

Pete
 
S

shapper

Not without a concise-but-complete code example.

The code you posted does not in and of itself show anything that should
necessarily add text to your TextBlock class.  The StringWriter you've
added to your proxy isn't going to have any text in it until you
actually write some text to the proxy.  

Sorry, I am very familiar with WPF and console.
I would create an full code example if I could replicate what I am
doing.

I have the following example which hopefully will explain what I am
trying to do:

DockPanel content = new DockPanel();
TextBlock output = new TextBlock();
TextWriterProxy proxy = new TextWriterProxy();
proxy.Add(Console.Out);

StringBuilder sb = new StringBuilder();
StringWriter resultStringWriter = new StringWriter(sb);
proxy.Add(resultStringWriter);

proxy.WriteLine("Start");
PackService.Run();
proxy.WriteLine("Finish");
output.Text = sb.ToString();

DockPanel.SetDock(output, Dock.Bottom);
content.Children.Add(output);
Content = content;

PackService.Run() minimizes some CSS and JS code. So in console I get:

Start
Writing output to C:\Users\Miguel\Projects\WCA.Presentation\Scripts
\WCA.Site.min.js
Writing output to C:\Users\Miguel\Projects\WCA.Presentation\Scripts
\JQuery-1.3.2.Plugins.min.js
Writing output to C:\Users\Miguel\Projects\WCA.Presentation\Styles
\WCA.Base.min.css
Finish

I would like to "see" this on my Text Block but I get only:
Start
Finish

In fact I would like the TextBlock to match what is shown in console
and in every part of my code, for example, even inside PackService.Run
() to be able to send something to console that would also show in
TextBlock.

Am I explaining this correctly?

Thank You,
Miguel
 
S

shapper

I took another small step to solve this:

Console.SetOut(proxy);
proxy.WriteLine("Start");
PackService.Run();
proxy.WriteLine("Finish");
output.Text = sb.ToString();

Now I "see" all the lines on my TextBlock.

The problem is that if I click a button on my WPF menu that calls
PackService.Run() in this case nothing happens on the TextBox.

My idea would make all my application aware of the proxy and that
SetOut(proxy) ... Is this possible?
 
P

Peter Duniho

shapper said:
[...]
In fact I would like the TextBlock to match what is shown in console
and in every part of my code, for example, even inside PackService.Run
() to be able to send something to console that would also show in
TextBlock.

Am I explaining this correctly?

I don't know. As I said, a concise-but-complete code example would go a
long way to improving your question.

That said, if I understand correctly, the problem is that your
PackService.Run() method is still writing directly to the Console class.
It should use your proxy class instead.

Though, that said, your original choice to redirect the Console standard
output by calling SetOut() should also work, and wouldn't require
changes to the PackService.Run() method. Just because my original
suggestion was different from what you thought I meant, or different
from some other approach that might work, that doesn't mean you have to
follow my original suggestion.

Beyond that, it's really impossible to provide specific advice without a
specific question.

Pete
 
P

Peter Duniho

shapper said:
[...]
My idea would make all my application aware of the proxy and that
SetOut(proxy) ... Is this possible?

Just create the proxy at the beginning of your program, and call
Console.SetOut() with the proxy before any of the stuff happens for
which you want output.
 
S

shapper

I don't know.  As I said, a concise-but-complete code example would go a
long way to improving your question.

I am not sure if the example that you are asking is what I am going to
post.

The objectives are:
1. Display the result of Console.WriteLine in Console and in
TextBlock ...
... From Window1 and from Test.Run() when the button is clicked.

2. Write the result of a command in Console and TextBlock ...
In my code I was checking the messages of minimizing the JS and CSS
code.
I didn't implement this part. Maybe saving or opening a file?

I think the TextWriterProxy would be a better option than my initial
approach as it even allows to write to other targets.

This said, this is my code (I hope this is what you are asking):

-- BEGIN Window1.xaml ---

<Window x:Class="ConsoleTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
</Window>


-- BEGIN Window1.xaml.cs ---

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;

// ConsoleTest
namespace ConsoleTest {

public partial class Window1: Window {

#region Constructors

public Window1() {

// Initialize component
InitializeComponent();

// Define proxy
TextWriterProxy proxy = new TextWriterProxy();
proxy.Add(Console.Out);
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
proxy.Add(sw);
Console.SetOut(proxy);

// Dock panel
DockPanel content = new DockPanel();

// Menu
Menu menu = new Menu();
menu.Items.Add(new MenuItem { Command = TestCommands.Test,
Header = "Test" });
DockPanel.SetDock(menu, Dock.Top);
content.Children.Add(menu);

// Text block
TextBlock output = new TextBlock();
DockPanel.SetDock(output, Dock.Bottom);
content.Children.Add(output);
Content = content;

// 1: Use Text Writer Proxy - NOT WORKING FROM Test.Run()
proxy.WriteLine("Write from Window1");
output.Text = sb.ToString();

// 2: Use Console Log - NOT WORKING
//using (ConsoleLog cl = new ConsoleLog(output, Console.Out)) {
// Console.SetOut(cl);
//}
//Console.WriteLine("Write from Window1");

} // Start

#endregion // Constructors

} // Start

} // ConsoleTest

// ConsoleLog
public class ConsoleLog : TextWriter {

public override Encoding Encoding { get { return
Encoding.Default; } } // Encoding
private TextWriter _writer;
private TextBlock _output;

public ConsoleLog(TextBlock output, TextWriter writer) {
_writer = writer;
_output = output;
} // ConsoleLog

public override void Write(String s) {

_writer.Write(s);
_output.Text = String.Concat(_output.Text, s);

} // Write

} // ConsoleLog

// TestCommands
public static class TestCommands {

public static ICommand Test { get { return new TestCommand(); } }

public class TestCommand : ICommand {

// CanExecute
public Boolean CanExecute(Object parameter) {
return true;
} // CanExecute

// CanExecuteChanged
public event EventHandler CanExecuteChanged {
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
} // CanExecuteChanged

// Execute
public void Execute(Object parameter) {
TestService.Run();
} // Execute

} // TestCommand

} // TestCommands

// TestService
public static class TestService {

#region Methods

public static void Run() {
Console.WriteLine("Write from Test Service");
} // Run

#endregion // Methods

} // TestService

public class TextWriterProxy : TextWriter {

private List<TextWriter> _writers = new List<TextWriter>();

public override Encoding Encoding { get { return
Encoding.Default; } } // Encoding

public override string NewLine {
get { return base.NewLine; }
set {
foreach (TextWriter tw in _writers)
tw.NewLine = value;
base.NewLine = value;
}
} // NewLine

public void Add(TextWriter writer) {
if (!_writers.Contains(writer))
_writers.Add(writer);
} // Add

public bool Remove(TextWriter writer) {
return _writers.Remove(writer);
} // Remove

public override void Write(char value) {
foreach (TextWriter tw in _writers)
tw.Write(value);
base.Write(value);
} // Write

public override void Close() {
foreach (TextWriter tw in _writers)
tw.Close();
base.Close();
} // Close

protected override void Dispose(bool disposing) {
foreach (TextWriter tw in _writers)
tw.Dispose();
base.Dispose(disposing);
} // Dispose

public override void Flush() {
foreach (TextWriter tw in _writers)
tw.Flush();
base.Flush();
} // Flush

} // TextWriterProxy


Thank You,
Miguel
 
P

Peter Duniho

shapper said:
I don't know. As I said, a concise-but-complete code example would go a
long way to improving your question.

I am not sure if the example that you are asking is what I am going to
post.

The objectives are:
1. Display the result of Console.WriteLine in Console and in
TextBlock ...
... From Window1 and from Test.Run() when the button is clicked.

2. Write the result of a command in Console and TextBlock ...
In my code I was checking the messages of minimizing the JS and CSS
code.
I didn't implement this part. Maybe saving or opening a file?

I think the TextWriterProxy would be a better option than my initial
approach as it even allows to write to other targets.

This said, this is my code (I hope this is what you are asking): [...]

Well, the code you posted isn't complete, because you call a method not
declared in the code you posted (InitializeComponent()).

That said, the code you did post looks much like the code I already
responded to, where I explained that simply redirecting the output to
your proxy which in turn redirects output to your StringWriter cannot in
and of itself cause any update to the TextBlock control.

As I wrote before, you need some mechanism for specifically updating the
TextBlock control any time something is written to your proxy. For
example, yet another TextWriter that wraps updates to the TextBlock
control. Add that TextWriter to your proxy (which is really a kind of
"T filter"), and then in that TextWriter, copy text passed via the
Write() method to the TextBlock.

Nothing about the expanded code example you've posted leads me to change
my mind on that point. :)

Pete
 
S

shapper

shapper said:
On Nov 20, 7:25 pm, Peter Duniho <[email protected]>
wrote:
I am not sure if the example that you are asking is what I am going to
post.
The objectives are:
1. Display the result of Console.WriteLine in Console and in
TextBlock ...
   ... From Window1 and from Test.Run() when the button is clicked.
2. Write the result of a command in Console and TextBlock ...
   In my code I was checking the messages of minimizing the JS and CSS
code.
   I didn't implement this part. Maybe saving or opening a file?
I think the TextWriterProxy would be a better option than my initial
approach as it even allows to write to other targets.
This said, this is my code (I hope this is what you are asking): [...]

Well, the code you posted isn't complete, because you call a method not
declared in the code you posted (InitializeComponent()).

It is a part of WPF code and is present in all Windows or User
Controls. A better description:

"The call to InitializeComponent() (which is usually called in the
default constructor of at least Window and UserControls) is actually a
method call to the partial class of the control (rather than a call up
the object hierarchy as I first expected).

This method locates a URI to the XAML for the Window/UserControl that
is loading, and passes it to the
System.Windows.Application.LoadComponent() static method. LoadComponent
() loads the XAML file that is located at the passed in URI, and
converts it to an instance of the object that is specified by the root
element of the XAML file.

In more detail, LoadComponent creates and instance of the XamlParser,
and builds a tree of the XAML. Each node is parsed by the
XamlParser.ProcessXamlNode(). This gets passed to the BamlRecordWriter
class. Sometime after this I get a bit lost in how the BAML is
converted to objects, but this may be enough to help you on the path
to enlightenment.

Note: Interestingly, the InitializeComponent is an method on the
System.Windows.Markup.IComponentConnector interface, of which Window/
UserControls implement in the partial generated class."
That said, the code you did post looks much like the code I already
responded to, where I explained that simply redirecting the output to
your proxy which in turn redirects output to your StringWriter cannot in
and of itself cause any update to the TextBlock control.

As I wrote before, you need some mechanism for specifically updating the
TextBlock control any time something is written to your proxy.  For
example, yet another TextWriter that wraps updates to the TextBlock
control.  Add that TextWriter to your proxy (which is really a kind of
"T filter"), and then in that TextWriter, copy text passed via the
Write() method to the TextBlock.

I am a little bit lost about the code you suggesting but what comes to
mind is to add a ConsoleLog to the proxy.
Since ConsoleLog already implements TextWriter and works with
TextBlock ...

But I think I need to make some changes in it ... I am confused. :)
Nothing about the expanded code example you've posted leads me to change
my mind on that point.  :)

See that my "strange" initial code said it all? :p

Miguel
 
P

Peter Duniho

shapper said:
[...]
Nothing about the expanded code example you've posted leads me to change
my mind on that point. :)

See that my "strange" initial code said it all? :p

No. It's just that your subsequent post didn't add anything to it.
 
S

shapper

shapper said:
[...]
Nothing about the expanded code example you've posted leads me to change
my mind on that point.  :)
See that my "strange" initial code said it all? :p

No.  It's just that your subsequent post didn't add anything to it.

I a not sure but maybe turning the proxy into a static class would
make it globally and solve the problem.

I am trying the following:

public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();

ConsoleWriter.TextBlock = tbOutput;
ConsoleWriter.WriteLine("TEST");
}
}

public static class ConsoleWriter
{
public static TextBlock TextBlock { get; set; }

public static void WriteLine(string message)
{
Console.WriteLine(message);
TextBlock.Text = message;
}
}

But instead of using this approach maybe have the ConsoleWriter wrapp
the TextBlock and the Proxy ..
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top