I've written a c# application to run an external program and i've redirectet it's output to a richtextbox in my form. I've created the process using the following settings
p1.StartInfo.RedirectStandardOutput = true;
p1.OutputDataReceived += new DataReceivedEventHandler(outputreceived);
and in the outputreceived event
void outputreceived(object sender, DataReceivedEventArgs e)
{
if (!string.IsNullOrEmpty(e.Data))
{
richTextBox1.Invoke(new UpdateOutputCallback(this.updateoutput),
new object[] { e.Data });
}
}
void updateoutput(string text)
{
int len = text.Length;
int start = richTextBox1.Text.Length;
richTextBox1.Text += text + Environment.NewLine;
richTextBox1.Select(start, len);
richTextBox1.SelectionColor = System.Drawing.Color.White;
richTextBox1.Select(richTextBox1.Text.Length, 0);
richTextBox1.ScrollToCaret();
}
Now the thing is though it is working, but my main form which contains the textbox, hangs if the output is huge from the application. I think each time the invoke call leads to repainting of the form, which happens very frequently. Is there any alternative so that i can see the updates to the textbox as they happen and also keep the form completely active?
Update:
I think I got my answer, I used BeginInvoke when I should have used Invoke.
Update 1:
I tried both BeginInvoke and Suspendlayout but it is not giving me the desired functionality, what happens is that the process has returened all the standardoutput to the string, but the thread which is responsible for updating the text is taking it's own time to print the data. Can i do any thing to it?
Since you've already solved your problem, I'll just note that it will be faster if you use rtb.AppendText (instead of Text += ...) and use pinvoke to scroll to the bottom:
private const int WM_VSCROLL = 0x115;
private const int SB_BOTTOM = 7;
[DllImport("user32.dll", CharSet=CharSet.Auto)]
private static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam,
IntPtr lParam);
// ...
// Scroll to the bottom, but don't move the caret position.
SendMessage(rtb.Handle, WM_VSCROLL, (IntPtr) SB_BOTTOM, IntPtr.Zero);
You might want to try
richTextBox1.BeginInvoke()
rather than
richTextBox1.Invoke()
That will at least make the call Asynchronous. Still not sure if that will cause the UI thread to lock while the updates are being painted.
Try to suspend and resume layout of richTextBox1
void updateoutput(string text)
{
try
{
richTextBox1.SuspendLayout();
int len = text.Length;
int start = richTextBox1.Text.Length;
richTextBox1.Text += text + Environment.NewLine;
richTextBox1.Select(start, len);
richTextBox1.SelectionColor = Color.White;
richTextBox1.Select(richTextBox1.Text.Length, 0);
richTextBox1.ScrollToCaret();
}
finally
{
richTextBox1.ResumeLayout();
}
}
Is there any alternative so that i can see the updates to the textbox as they happen and also keep the form completely active?
I think you should use Debug.Print to view what's going on instead.
It is an old post, but maybe somebody still looking for it like I did.
You also can do, like "for(writeToTextbox % 10 == 0)" then invoke.
In this case it will be updated only every 10 times.
UPDATE: sorry for the misspelling! (wirte -> write) and thanks for "HaveNoDisplayName" to show it to me!
Related
I have this chunk of code in my Notepad clone program, it is to track my carat location in my RichTextBox and has been working fine for me.
private void richTextBox1_KeyDown(object sender, KeyEventArgs e)
{
Curpos();
}
private static int EM_LINEINDEX = 0xbb;
[DllImport("user32.dll")]
extern static int SendMessage(IntPtr hwnd, int message, int wparam, int lparam);
private void Curpos()
{
{
int line, col, index;
index = richTextBox1.SelectionStart;
line = richTextBox1.GetLineFromCharIndex(index);
col = index - SendMessage(richTextBox1.Handle, EM_LINEINDEX, -1, 0);
Lblcurpos.Text = "Line: " + (++line).ToString() + ", Column:" + (++col).ToString();
}
}
It has been working perfectly for me, until i added some code so that i could close my program with the Esc Key.
Here is the code for my Esc Key:
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Escape)
{
this.Close();
}
}
EDIT: This is where it has the problem:
index = richTextBox1.SelectionStart;
When i am doing this, i can run my program fine, type text in my RichTextBox and everything but when i press the Esc Key it says:
Cannot Access disposed object
Object name: 'RichTextBox'
Then is says:
Trouble Shooting Tips:
Make sure you have not released a resource before attempting to use this.
Get general help for this exception
Any ideas on what i can do? I tried to use the shortcut keys but it doesn't have a Esc key you can use for the shortcut. Any help with either doing a different shortcut or a fix to this problem, i would like it!!
Try:
Application.Exit();
instead of
this.Close();
From MSDN:
Application.Exit
Informs all message pumps that they must terminate, and then closes all application windows after the messages have been processed. This is the code to use if you are have called Application.Run (WinForms applications), this method stops all running message loops on all threads and closes all windows of the application.
Do note that Application.Exit() is not a substitute for this.Close(). Exit() terminates the entire application, Close() just close the form.
There are better ways to avoid accidents like this. When you handle shortcut keystrokes like you did, you should always set e.Handled and e.SuppressKeyPress to true so the keystroke will be completely dismissed and not generate any additional events. Like the one that bombed your code.
By far the best way is to use the dedicated method for this in Winforms, ProcessCmdKey(). It implements true shortcut keystroke behavior, the KeyPreview property is a VB6 compatibility feature which is close but not equivalent. Make it look like this:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
if (keyData == Keys.Escape) {
this.Close();
return true; // Used, don't process any further
}
return base.ProcessCmdKey(ref msg, keyData);
}
I need to show cursor in RichTextBox control in WinForms application even when it's not in focus. How can I do this? I found only the way for WPF ( How to keep WPF TextBox selection when not focused?)
You can use WinAPI ..
[DllImport("user32.dll", EntryPoint = "ShowCaret")]
public static extern long ShowCaret(IntPtr hwnd);
[DllImport("user32.dll", EntryPoint = "HideCaret")]
public static extern long HideCaret(IntPtr hwnd);
and call ShowCaret whenever you want
You can't set focus to the two or more UI at same time however you can preserve the selection by setting HideSelection=false.
I don't know what you are trying to achieve and how much is it really useful. But if it is just for visual purpose, write some thing like '|' in it. Its a bad, weird, awkward way or what ever you call it, for visual purpose it may work.
public void blink()
{
while (true)
{
textBox1.Text = "|";
Thread.Sleep(200);
textBox1.Text = "";
Thread.Sleep(200);
}
}
private void Form1_Load(object sender, EventArgs e)
{
Thread t1 = new Thread(new ThreadStart(blink));
t1.Start();
}
I am not sure if I am giving is what you are asking, but to get accurate answer, you have to expose your need of this requirement.
Hope it helps.
I'm creating an app that is threaded. I started GUI (Announce : Form) as separate thread.
That window will be very minimalistic with one input box and maybe a button. On another thread there will be Tcp Client running and when it gets info from TcpServer it should pass what it gets into that inputbox and show the gui (and topmost windows). After couple of seconds the gui should hide itself and wait for another tcp msg and so on.
public void setTextBox(string varText) {
if (InvokeRequired) {
textBox.BeginInvoke(new textBoxCallBack(setTextBox), new object[] {varText});
} else {
textBox.Text = varText;
}
}
This code is used to fill the textBox from the Tcp Thread. The only problem now is getting the window show and hide properly. Been trying many solutions and always something went wrong. Like:
private void windowStateChange(string varState) {
if (InvokeRequired) {
Invoke(new WindowStateChangeCallBack(windowStateChange), new object[] {varState});
} else {
if (varState == "Hide") {
//Hide();
// TopMost = false;
//TopMost = varState != FormWindowState.Minimized;
} else {
//Show();
//MessageBox.Show("TEST1");
}
}
}
public void windowStateChangeDiffrent(FormWindowState varState) {
if (InvokeRequired) {
Invoke(new WindowStateChangeCallBack(windowStateChange), new object[] {varState});
} else {
WindowState = varState;
// Hide();
TopMost = varState != FormWindowState.Minimized;
}
}
What would be best aproach to do this (and fastest, as time matters)?
Answer 1 which seems to work:
private static void windowStateChange(string varState) {
if (mainAnnounceWindow.InvokeRequired) {
mainAnnounceWindow.BeginInvoke(new StateCallBack(windowStateChange), new object[] {varState});
} else {
if (varState == "Hide") {
mainAnnounceWindow.Hide();
mainAnnounceWindow.TopMost = false;
} else {
mainAnnounceWindow.Show();
mainAnnounceWindow.TopMost = true;
}
}
}
Anything bad about it?
Making the form hide with form.Hide() should pose no problems.
However I've experienced making the form show again does not always work.
So if you're encountering the same problem, you could use something like this:
string RunningProcess = Process.GetCurrentProcess().ProcessName;
Process[] processes = Process.GetProcessesByName(RunningProcess);
int SW_SHOW = 5, SW_HIDE = 0, SW_RESTORE = 9, SW_SHOWNORMAL = 1;
for (int a = 0; a < processes.Length; a++)
{
IntPtr hWnd = processes[a].MainWindowHandle;
ShowWindowAsync(hWnd, SW_RESTORE);
ShowWindowAsync(hWnd, SW_SHOWNORMAL);
ShowWindowAsync(hWnd, SW_SHOW);
SetForegroundWindow((int)hWnd);
}
//Required Win32 API imports
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
static extern bool ShowWindowAsync(IntPtr windowHandle, int cmd);
[System.Runtime.InteropServices.DllImportAttribute("User32.dll")]
private static extern IntPtr SetForegroundWindow(int hWnd);
you might try
form.Hide();
but make sure that you show/hide the form from the same thread that created them
this.Invoke(new MethodInvoker(this.hide()));
Another approach would be to expose events on your TCP thread object. It could define an event such as RecievedData(...) then the GUI could subscribe to that event and update itself without having to do any InvokeRequired checks etc.
Update: link to C# events tutorial
http://msdn.microsoft.com/en-us/library/aa645739%28VS.71%29.aspx
Form.Hide() is the right method to hide the form. I remember having issues with Form.Show(), I have a vague memory of having to use Form.Activate() as well to get the form to restore correctly.
You're already dealing with thread marshalling correctly (InvokeRequired and Invoke). You could also use Form.BeginInvoke(), which is an async version of Form.Invoke. That might be faster.
I have this method:
private delegate void watcherReader(StreamReader sr);
private void watchProc(StreamReader sr) {
while (true) {
string line = sr.ReadLine();
while (line != null) {
if (stop) {
return;
}
//Console.WriteLine(line);
line = stripColors(line);
txtOut.Text += line + "\n";
line = sr.ReadLine();
}
}
}
And it reads the streams from a Process (cmd.exe). When the user closes the cmd.exe window, it causes the CPU usage to jump to 100%. When playing with the debugger I see that it stops on the sr.ReadLine() and never returns. Because this is watching both the StandardErrorStream and the StandardOutputStream it uses 100% on both cores.
Here's some more code of the project if you need it.
[DllImport("User32")]
private static extern int ShowWindow(int hwnd, int nCmdShow); //this will allow me to hide a window
public ConsoleForm(Process p) {
this.p = p;
p.Start();
ShowWindow((int)p.MainWindowHandle, 0); //0 means to hide the window.
this.inStream = p.StandardInput;
this.outStream = p.StandardOutput;
this.errorStream = p.StandardError;
InitializeComponent();
wr = new watcherReader(watchProc);
wr.BeginInvoke(this.outStream, null, null);
wr.BeginInvoke(this.errorStream, null, null);
}
public void start(string[] folders, string serverPath) {
this.inStream.WriteLine("chdir C:\\cygwin\\bin");
this.inStream.WriteLine("bash --login -i");
this.inStream.WriteLine("");
}
//code example from http://geekswithblogs.net/Waynerds/archive/2006/01/29/67506.aspx it is
//to make the textbox autoscroll I don't understand what it does, but it works.
#region autoscroll
[DllImport("User32.dll", CharSet = CharSet.Auto, EntryPoint = "SendMessage")]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
const int WM_VSCROLL = 277;
const int SB_BOTTOM = 7;
private void txtOut_TextChanged(object sender, EventArgs e) {
IntPtr ptrWparam = new IntPtr(SB_BOTTOM);
IntPtr ptrLparam = new IntPtr(0);
SendMessage(((RichTextBox)sender).Handle, WM_VSCROLL, ptrWparam, ptrLparam);
}
#endregion
private void ConsoleForm_FormClosed(object sender, FormClosedEventArgs e) {
this.stop = true;
try {
this.p.Kill();
} catch (InvalidOperationException) {
return;
}
}
Another interesting this is that it doesn't always hide the cmd window like it's supposed to. It hides it the first time, and then the second (or after) it won't hide it. This is when the user can close the cmd.exe window and cause the readline to act funny. It also never reads the last line outputted to cmd unless it exits.
Any suggestions on how to fix this?
I would change:
while(true)
to:
while(!sr.EOS) {
}
It is a better way to check to end the loop.
Whenever you have a while(true) loop in your code you're going to peg your cpu (or at least one core) at 100%, unless you also have a way to break out of the loop. In your case, you do have a return statement, but at no point in the loop do you ever do anything to the stop variable guarding it.
This seems like an interesting issue here. At first glance, it would appear that ReadLine has an issue with the handle being closed from under it while it's trying to read data, and thus would seem to be a bug in the Framework. However, I'm not convinced that easily that it's a bug in the .Net framework...
However, there are a couple low level issues here.
The other answers you have got so far all suggest you modify the while loop. I would do this as well, but I don't think this is the root of your problem. You do not need a sleep in there, because you will get your waitstate from the ReadLine(), unless there is no data to read, and it just returns a failue, THEN you will 'tight-loop'. So, make sure you are checking any and all error states during this loop.
If you do not, I can see issues.
If everything else is working as it should, then if I were you, I would start by trying to identify if you can duplicate it outside of your program with a small demo program. I'm sure there is plenty of error checking in the Framework's Stream handling. However, it looks like you are running some stuff from Cygwin, and that's the output you are reading from the cmd shell.
Try making a simple app that just spits out data to stdout, and stderr, and then make sure the app closes while you are still reading..
Also use the debugger to see what line== after the failure occurs.
Larry
Having while(true) with no sleep in the loop will cause 100% CPU usage.
You need to sleep for some amount of time or break out of the loop at some point so the CPU can do something else.
At the very least you should be doing something along the lines of:
while (sr.Peek() >= 0)
{
Console.WriteLine(sr.ReadLine());
Thread.Sleep(0);
}
I'm using a Form to show notifications (it appears at the bottom right of the screen), but when I show this form it steals the focus from the main Form. Is there a way to show this "notification" form without stealing focus?
Hmmm, isn't simply overriding Form.ShowWithoutActivation enough?
protected override bool ShowWithoutActivation
{
get { return true; }
}
And if you don't want the user to click this notification window either, you can override CreateParams:
protected override CreateParams CreateParams
{
get
{
CreateParams baseParams = base.CreateParams;
const int WS_EX_NOACTIVATE = 0x08000000;
const int WS_EX_TOOLWINDOW = 0x00000080;
baseParams.ExStyle |= ( int )( WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW );
return baseParams;
}
}
Stolen from PInvoke.net's ShowWindow method:
private const int SW_SHOWNOACTIVATE = 4;
private const int HWND_TOPMOST = -1;
private const uint SWP_NOACTIVATE = 0x0010;
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
static extern bool SetWindowPos(
int hWnd, // Window handle
int hWndInsertAfter, // Placement-order handle
int X, // Horizontal position
int Y, // Vertical position
int cx, // Width
int cy, // Height
uint uFlags); // Window positioning flags
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
static void ShowInactiveTopmost(Form frm)
{
ShowWindow(frm.Handle, SW_SHOWNOACTIVATE);
SetWindowPos(frm.Handle.ToInt32(), HWND_TOPMOST,
frm.Left, frm.Top, frm.Width, frm.Height,
SWP_NOACTIVATE);
}
(Alex Lyman answered this, I'm just expanding it by directly pasting the code. Someone with edit rights can copy it over there and delete this for all I care ;) )
This is what worked for me. It provides TopMost but without focus-stealing.
protected override bool ShowWithoutActivation
{
get { return true; }
}
private const int WS_EX_TOPMOST = 0x00000008;
protected override CreateParams CreateParams
{
get
{
CreateParams createParams = base.CreateParams;
createParams.ExStyle |= WS_EX_TOPMOST;
return createParams;
}
}
Remember to omit setting TopMost in Visual Studio designer, or elsewhere.
This is stolen, err, borrowed, from here (click on Workarounds):
https://connect.microsoft.com/VisualStudio/feedback/details/401311/showwithoutactivation-is-not-supported-with-topmost
If you're willing to use Win32 P/Invoke, then you can use the ShowWindow method (the first code sample does exactly what you want).
Doing this seems like a hack, but it seems to work:
this.TopMost = true; // as a result the form gets thrown to the front
this.TopMost = false; // but we don't actually want our form to always be on top
Edit: Note, this merely raises an already created form without stealing focus.
The sample code from pinvoke.net in Alex Lyman/TheSoftwareJedi's answers will make the window a "topmost" window, meaning that you can't put it behind normal windows after it's popped up. Given Matias's description of what he wants to use this for, that could be what he wants. But if you want the user to be able to put your window behind other windows after you've popped it up, just use HWND_TOP (0) instead of HWND_TOPMOST (-1) in the sample.
In WPF you can solve it like this:
In the window put these attributes:
<Window
x:Class="myApplication.winNotification"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Notification Popup" Width="300" SizeToContent="Height"
WindowStyle="None" AllowsTransparency="True" Background="Transparent" ShowInTaskbar="False" Topmost="True" Focusable="False" ShowActivated="False" >
</Window>
The last one attribute is the one you need ShowActivated="False".
I have something similar, and I simply show the notification form and then do
this.Focus();
to bring the focus back on the main form.
Create and start the notification Form in a separate thread and reset the focus back to your main form after the Form opens. Have the notification Form provide an OnFormOpened event that is fired from the Form.Shown event. Something like this:
private void StartNotfication()
{
Thread th = new Thread(new ThreadStart(delegate
{
NotificationForm frm = new NotificationForm();
frm.OnFormOpen += NotificationOpened;
frm.ShowDialog();
}));
th.Name = "NotificationForm";
th.Start();
}
private void NotificationOpened()
{
this.Focus(); // Put focus back on the original calling Form
}
You can also keep a handle to your NotifcationForm object around so that it can be programmatically closed by the main Form (frm.Close()).
Some details are missing, but hopefully this will get you going in the right direction.
This works well.
See: OpenIcon - MSDN and SetForegroundWindow - MSDN
using System.Runtime.InteropServices;
[DllImport("user32.dll")]
static extern bool OpenIcon(IntPtr hWnd);
[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);
public static void ActivateInstance()
{
IntPtr hWnd = IntPtr hWnd = Process.GetCurrentProcess().MainWindowHandle;
// Restore the program.
bool result = OpenIcon(hWnd);
// Activate the application.
result = SetForegroundWindow(hWnd);
// End the current instance of the application.
//System.Environment.Exit(0);
}
You might want to consider what kind of notification you would like to display.
If it's absolutely critical to let the user know about some event, using Messagebox.Show would be the recommended way, due to its nature to block any other events to the main window, until the user confirms it. Be aware of pop-up blindness, though.
If it's less than critical, you might want to use an alternative way to display notifications, such as a toolbar on the bottom of the window. You wrote, that you display notifications on the bottom-right of the screen - the standard way to do this would be using a balloon tip with the combination of a system tray icon.
You can handle it by logic alone too, although I have to admit that the suggestions above where you end up with a BringToFront method without actually stealing focus is the most elegant one.
Anyhow, I ran into this and solved it by using a DateTime property to not allow further BringToFront calls if calls were made already recently.
Assume a core class, 'Core', which handles for example three forms, 'Form1, 2, and 3'. Each form needs a DateTime property and an Activate event that call Core to bring windows to front:
internal static DateTime LastBringToFrontTime { get; set; }
private void Form1_Activated(object sender, EventArgs e)
{
var eventTime = DateTime.Now;
if ((eventTime - LastBringToFrontTime).TotalMilliseconds > 500)
Core.BringAllToFront(this);
LastBringToFrontTime = eventTime;
}
And then create the work in the Core Class:
internal static void BringAllToFront(Form inForm)
{
Form1.BringToFront();
Form2.BringToFront();
Form3.BringToFront();
inForm.Focus();
}
On a side note, if you want to restore a minimized window to its original state (not maximized), use:
inForm.WindowState = FormWindowState.Normal;
Again, I know this is just a patch solution in the lack of a BringToFrontWithoutFocus. It is meant as a suggestion if you want to avoid the DLL file.
I don't know if this is considered as necro-posting, but this is what I did since I couln't get it working with user32's "ShowWindow" and "SetWindowPos" methods. And no, overriding "ShowWithoutActivation" doesn't work in this case since the new window should be always-on-top.
Anyway, I created a helper method that takes a form as parameter; when called, it shows the form, brings it to the front and makes it TopMost without stealing the focus of the current window (apparently it does, but the user won't notice).
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern IntPtr SetForegroundWindow(IntPtr hWnd);
public static void ShowTopmostNoFocus(Form f)
{
IntPtr activeWin = GetForegroundWindow();
f.Show();
f.BringToFront();
f.TopMost = true;
if (activeWin.ToInt32() > 0)
{
SetForegroundWindow(activeWin);
}
}
I know it may sound stupid, but this worked:
this.TopMost = true;
this.TopMost = false;
this.TopMost = true;
this.SendToBack();
I needed to do this with my window TopMost. I implemented the PInvoke method above but found that my Load event wasn't getting called like Talha above. I finally succeeded. Maybe this will help someone. Here is my solution:
form.Visible = false;
form.TopMost = false;
ShowWindow(form.Handle, ShowNoActivate);
SetWindowPos(form.Handle, HWND_TOPMOST,
form.Left, form.Top, form.Width, form.Height,
NoActivate);
form.Visible = true; //So that Load event happens
You don't need to make it anywhere near as complicated.
a = new Assign_Stock();
a.MdiParent = this.ParentForm;
a.Visible = false; //hide for a bit.
a.Show(); //show the form. Invisible form now at the top.
this.Focus(); //focus on this form. make old form come to the top.
a.Visible = true; //make other form visible now. Behind the main form.
Github Sample
Form.ShowWithoutActivation Property
Add this in your child form class
protected override bool ShowWithoutActivation
{
get { return true; }
}
Working Code
Form2
public partial class Form2 : Form
{
Form3 c;
public Form2()
{
InitializeComponent();
c = new Form3();
}
private void textchanged(object sender, EventArgs e)
{
c.ResetText(textBox1.Text.ToString());
c.Location = new Point(this.Location.X+150, this.Location.Y);
c .Show();
//removethis
//if mdiparent 2 add this.focus() after show form
c.MdiParent = this.MdiParent;
c.ResetText(textBox1.Text.ToString());
c.Location = new Point(this.Location.X+150, this.Location.Y);
c .Show();
this.Focus();
////-----------------
}
}
Form3
public partial class Form3 : Form
{
public Form3()
{
InitializeComponent();
//ShowWithoutActivation = false;
}
protected override bool ShowWithoutActivation
{
get { return true; }
}
internal void ResetText(string toString)
{
label2.Text = toString;
}
}
When you create a new form using
Form f = new Form();
f.ShowDialog();
it steals focus because your code can't continue executing on the main form until this form is closed.
The exception is by using threading to create a new form then Form.Show(). Make sure the thread is globally visible though, because if you declare it within a function, as soon as your function exits, your thread will end and the form will disappear.
Figured it out: window.WindowState = WindowState.Minimized;.