Console.Write in .NET GUI Application - c#

In a C# .NET GUI Application. I also need the console in the background for some tasks. Basically, I'm using a Thrid Party library for some processing (takes lot of time) which writes its intermediate results to console. This processing is a computationally time taking task. So, I'm assigning this task to backgroundworker. I mean background worker calls these library functions. But problem is there is no way for me to show the user status of computation, because I don't have source of the library. I was hoping Console will be shown. But surprisingly Console.WriteLine doesn't seem to work. I mean, there isn't any console window shown. How come?
EDIT:
I tried setting application type = console. But there seems to be a problem. Only, main thread is able to access the console. Only Console.WriteLines executed by main (Application) thread are displayed on console. Console.WriteLines executed by other (BackgroundWorker)threads of the GUI, the output is not shown. I need console only for Background workers. I mean, When background worker starts, console starts & when it ends console will be off.

Create your own console window and use the Console.SetOut(myTextWriter); method to read anything written to the console.

Set your application type to "Console Application". Console applications can also create GUI windows with no problem, and write to the console at the same time.
If you don't have control of the main application, and you want to make sure that a console is shown, you can p/invoke AllocConsole (signature here).
This isn't the same as being a console application though, your application will always get a separate console window, which might be surprising to someone who launched it from a command prompt window. You can work around that with AttachConsole (signature and example here) but shell redirection of the output still won't work. That's why I suggest setting the application subsystem to console if you can.

Followed by #jgauffin, here is the implementation of Console.SetOut method.
Create a TextWriter inherited class.
using System;
using System.Text;
using System.IO;
using System.Windows.Forms;
namespace ConsoleRedirection
{
public class TextBoxStreamWriter : TextWriter
{
TextBox _output = null;
public TextBoxStreamWriter(TextBox output)
{
_output = output;
}
public override void Write(char value)
{
base.Write(value);
_output.AppendText(value.ToString()); // When character data is written, append it to the text box.
}
public override Encoding Encoding
{
get { return System.Text.Encoding.UTF8; }
}
}
}
And in the Form, code as below.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
namespace ConsoleRedirection
{
public partial class FormConsole : Form
{
// That's our custom TextWriter class
TextWriter _writer = null;
public FormConsole()
{
InitializeComponent();
}
private void FormConsole_Load(object sender, EventArgs e)
{
// Instantiate the writer
_writer = new TextBoxStreamWriter(txtConsole);
// Redirect the out Console stream
Console.SetOut(_writer);
Console.WriteLine("Now redirecting output to the text box");
}
// This is called when the "Say Hello" button is clicked
private void txtSayHello_Click(object sender, EventArgs e)
{
// Writing to the Console now causes the text to be displayed in the text box.
Console.WriteLine("Hello world");
}
}
}
Original code is from https://saezndaree.wordpress.com/2009/03/29/how-to-redirect-the-consoles-output-to-a-textbox-in-c/
You can check the link for cross-thread calls and advanced implementations at comments.

Related

Change window location of process started with c#

I'm making a c# console .net framework application to open the file Error.vbs, and I want to be able to choose the location of the started file's window on the desktop.
This is the code I have so far:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
namespace ErrorRunner
{
internal class Program
{
static void Main(string[] args)
{
Process ExternalProcess = new Process();
ExternalProcess.StartInfo.FileName = #"C:\Users\video\Downloads\Error.vbs";
while (true)
{
ExternalProcess.Start();
}
}
}
}
How would I choose the start location of error.vbs?
(And yes, I know it might be a bad idea to start a process indefinitely, but that's the whole purpose of this program.)
You can retrieve the external process window handle by using ExternalProcess.MainWindowHandle. Maybe you need to wait for process to start completely (see https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.mainwindowhandle?view=net-6.0#System_Diagnostics_Process_MainWindowHandle)
So you can use the handle with a P/Invoke to SetWindowPos (see https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowpos)

Is it possible to open my winForm through Revit API button I have created?

I would like to run my winForm through the button I have created in Revit API, but I am new in this field and I am a bit stuck at the moment.
Here in my Command.cs I am stating what button does after clicking on it. Where instead of displaying "Hello World" I would like it to open my winForm.
Is there any way how can I do that? Do I need to somehow link my winForm application to this one?
namespace SetElevation
{
[Transaction(TransactionMode.Manual)]
class Command : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
TaskDialog.Show("SetElevation", "Hello World!");
return Result.Succeeded;
}
}
}
Here is my winForm Program.cs application
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WinFormTest
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
welcome to Revit addin development. You’re close.
I presume if you made your addin work above, that it is compiling as a DLL, not a standalone EXE. Your Winform app appears to be a separate EXE application.
To make this work, you’ll want to add your Form1 to the DLL project. Once you’ve got it in there, you can change TaskDialog.Show to instead these two lines:
var myForm - new Form1();
myForm.ShowDialog();
With that, you’re on your way.

WPF application without loading a window automatically issue

I have set up a WPF application so that it does not load a window automatically by modifying the app.xaml.cs as shown below and by setting the build action to Page. I have also removed the starturl from the app.xaml. I have introduced a controller class from where the application starts as you can see in the code below and in the constructor for the controller I have created and opened a new window. I deleted the original MainWindow which was produced automatically when opening the application.
Anyway the problem is that after opening the new window that I have added in the controller I then close the window and the Application terminates? Why? I don't want the application to terminate and I don't understand why it is terminating on closing the window. Or to put it another way how can I introduce a window that doesn't cause the application to terminate on closing the window? Any help greatly appreciated.
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace UserTraining
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
App()
{
InitializeComponent();
}
[STAThread]
static void Main()
{
App app = new App();
UserTrainingController control = new UserTrainingController();
app.Run();
}
}
}
UserTrainingController class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace UserTraining
{
class UserTrainingController
{
public UserTrainingController()
{
IntroductionWindow w = new IntroductionWindow();
w.Show();
}
}
}
A WPF application has a ShutdownMode property, whose value is one of the members of the ShutdownMode enumeration. This has three possible values: OnExplicitShutdown, OnLastWindowClose and OnMainWindowClose.
The default is set to OnLastWindowClose which means that the application will shutdown when either the last window closes, or Application.Shutdown() is called. I suspect this is why your application terminates when your IntroductionWindow is closed.
What you need to do is set ShutdownMode to OnExplicitShutdown instead. That way, your application will only terminate when you explicitly call Application.Shutdown().
You can set the ShutdownMode property either in code or in your App.xaml file.
After UserTrainingController is called, you get the window shown in the screen with w.Show();. When you close the window, w.Show(); terminates and comes back to the caller, which is Main method. It's the last method on the stack.
But then you run another method, app.Run(), but it's terminates immediately, returning to Main method, which after app.Run() has nothing more to do, so it terminates as well.
Since it was last method on the stack, stack is empty, meaning that your application finished.
You need to have some parent window, from which you can open your custom window.
Generally, you have to prevent stack from emptying :)

Powershell: Unable to interact with Form stored in a .Net Assembly

I'm just trying to learn this thing and in future, wanted to use it in one of my projects.
I have a small Form with a simple Text box, stored in a .Net dll (C#). And here is my class in this dll which contains methods to interact with this Form:
using System;
using System.Collections.Generic;
using System.Text;
namespace ClassLibrary1
{
public class Class1
{
static Form1 dlg = new Form1();
public static void ShowForm()
{
dlg.ShowIcon = true;
dlg.Show();
}
public static void SetText(string MyText)
{
dlg.Text = "Form Text ";
dlg.SetText(MyText);
}
}
}
Successfully loaded this form by referencing this dll into another C# application while calling its method i.e.:
private void button1_Click(object sender, EventArgs e)
{
ClassLibrary1.Class1.ShowForm();
}
And I was able to interact with the form perfectly.
Now loading same in Powershell using:
[Reflection.Assembly]::LoadFile("D:\Playing\ClassLibrary1\ClassLibrary1\bin\Debug\ClassLibrary1.dll")
[ClassLibrary1.Class1]::ShowForm()
Now this is loaded successfully at its default position, but I can't interact with this form i.e. I can't type in its Text Box, neither I can move or even close this form by clicking on its Close (x) button on right corner. Whenever I put my mouse on it, it becomes a HourGlass i.e. waiting for some process .
To verify if form is not hanged, I called SetText at Powershell prompt:
[ClassLibrary1.Class1]::SetText("String from Powershell")
and it worked fine. TextBox received this text properly, but still I can't interact with the form with my mouse.
I feel, I have to manually set its Window Handler i.e. System.Windows.Forms.IWin32Window.
But I don't know which Handler and how to achieve this?
Please guide .... Would really appreciate for any alternative tricks.
You can't show a form from PowerShell using Form.Show() method because it needs a message pump (and it's not provided by PowerShell host process).
Here what you can do to solve this issue:
Use Form.ShowDialog() or Application.Run(), your form will have its own message pump.
It'll be modal then you need to run it in another thread. I suggest to use a background thread and BeginInvoke() in your SetText() method.
Here code to do that (I won't change your code too much so I'll keep it as a singleton instance even if this prevents to display form multiple times). Code is just an example (I wouldn't suggest to use Thread Pool for this task) to illustrate the procedure.
public static void ShowForm()
{
if (dlg != null)
dlg.BeginInvoke(new MethodInvoker(delegate { dlg.Dispose(); }));
ThreadPool.QueueUserWorkItem(delegate(object state)
{
Application.Run(_dlg = new Form1());
});
}
public static void SetText(string text)
{
_dlg.BeginInvoke(new MethodInvoker(delegate { dlg.SetText(text); }));
}
In this way Form1 will be modal in another thread (with its own message pump) and your calling PowerShell thread won't be stopped. Communication between them is still possible via message dispatching (Invoke()/BeginInvoke()).
Please note that SetText() is now asynchronous, to make it synchronous just replace BeginInvoke() with Invoke().

WPF DocumentViewer: Switching between windows makes the window as if it has crashed

I have a WPF application with a custom control, based on DocumentViewer. There is a "Print" button, which leads to printer selection dialog. When I select a certain printer ("novaPDF Lite Server v7") and then press the "Print" button, a dialog box with printer settings is displayed. That dialog box appears in the task bar as a separate application.
When I activate that printer dialog, then activate some other application and then switch back to the WPF application, its client are (everything below the title bar) is white. It looks as if it hangs.
When I close the printer settings dialog, the WPF application starts to look in the normal way (all the controls are there).
I suppose that the problem is caused by the way the print process is started. Maybe it can fixed by doing the printing in a separate thread.
Here's how it is done now:
using System.Printing;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
namespace MyControls
{
public class MyDocumentViewer : DocumentViewer
{
[...]
protected override void OnPrintCommand()
{
base.OnPrintCommand();
this.FirePrintFinished();
}
[...]
}
What can I do in order to fix the problem?
Update 1 (03.09.2013): Changing the MyDocumentViewer so that printing is done asynchronously didn't help.
namespace MyControls
{
public class MyDocumentViewer : DocumentViewer
{
[...]
protected override void OnPrintCommand()
{
var worker = new PrintWorker(this);
Dispatcher.Invoke(new Action(worker.DoWork));
}
public void OnPrintCommandBase()
{
base.OnPrintCommand();
}
[...]
}
public class PrintWorker
{
private readonly MyDocumentViewer _myDocumentViewer;
public PrintWorker(MyDocumentViewer myDocumentViewer)
{
_myDocumentViewer = myDocumentViewer;
}
public void DoWork()
{
_myDocumentViewer.OnPrintCommandBase();
_myDocumentViewer.FirePrintFinished();
}
}
}
Update 2 (05.09.2013): The problem can only be reproduced, when I access the machine with the application via Remote Desktop (it does not occur, if I run the program locally).
Dispatcher is here to run a delegate in the UI thread, Dispatcher.Invoke() will run a delegate synchronously on the thread the Dispatcher is associated with (see msdn).
What you need may be Dispatcher.BeginInvoke(), or you should even use BackgroundWorker class to have a real background operation.
I'd also recommend you using Application.Current.Dispatcher (see this question: Dispatcher.CurrentDispatcher vs. Application.Current.Dispatcher)

Categories