• 0

[C#] Output command line app progress bar to textbox on one line


Question

I got a program that I'd like to output to a textbox but I'm having an issue with the progress bar. All the stdout from the app displays correctly except once I get to a progress bar:


00
00
00
01
01
01
02
02
02
etc...
[/CODE]

I made this code to handle it:

[CODE]
string o = "";
string pattern = @"(\d{1,2}\%)";
Regex rgx = new Regex(pattern, RegexOptions.IgnoreCase);
while (!process.HasExited)
{
StringBuilder output = new StringBuilder(process.StandardOutput.ReadLine());
MatchCollection matches = rgx.Matches(o);
if (output.ToString () != o){
if (matches.Count > 0)
{
this.txtOutputLog.AppendText(output + System.Environment.NewLine);
}
else
{
this.txtOutputLog.AppendText(output + System.Environment.NewLine);
}
Application.DoEvents();
}
o = output.ToString();
}
[/CODE]

but still get this:

[CODE]
01
02
03
04
05
[/CODE]

How can I get the progress bar to print on one line? I've also tried using "\r".

21 answers to this question

Recommended Posts

  • 0

AppendText, as its name suggests, adds text after whatever existing text is already there. What you're trying to do if I understand well is to replace existing text (the progress indicator) with new text. For this purpose, you could take the existing text, extract everything but the last 2 characters (with String.Substring), add the 2 replacement characters to the resulting string, and set the textbox's Text value to this new string.

  • 0

Right, I was using AppendText to read the output in real time, line by line. I didn't know what code to use to replace the last line (progress bar). I can get a progress bar on the one line but need to keep all the other output as well.

ie. simple setting this.txtOutputLog.Text = (output); works but won't keep the previous output.

this.txtOutputLog.Text += (output); looks weird and also setting this.txtOutputLog.Text += messes with the textbox focus.

  • 0

something like

txtbox.Text = txtbox.Text.Substring(0, txtbox.Text.Length - 2);
txtbox.AppendText(newCounterValue);
[/CODE]

Substring allows to select which part of a string you want, you can use it to select everything except the characters you want to replace and add the replacement characters after that.

  • 0
  On 13/04/2013 at 00:37, Dr_Asik said:

something like

txtbox.Text = txtbox.Text.Substring(0, txtbox.Text.Length - 2);
txtbox.AppendText(newCounterValue);
[/CODE]

Substring allows to select which part of a string you want, you can use it to select everything except the characters you want to replace and add the replacement characters after that.

This seems to work OK

[CODE]
this.txtOutputLog.Text = this.txtOutputLog.Text.Substring(0, this.txtOutputLog.Text.Length - 11);
this.txtOutputLog.AppendText(o);
[/CODE]

Textbox seems to flicker slightly...

  • 0

You don't need a background worker to use a progress bar any more than to use a textbox. What you need a background worker for is having your long-running operation not block the UI thread so the application remains responsive.

  • 0
  On 13/04/2013 at 01:03, DPyro said:

Ya I was thinking about doing that but would need to figure out how to add the background worker etc.

Could just use the Application.DoEvents() function to do your updates to a progress bar without a background worker. This will stop the gui from locking, but you won't need to use a Background worker.. which is really just a thread with training wheels.

  • 0
  On 13/04/2013 at 01:16, firey said:

Could just use the Application.DoEvents() function to do your updates to a progress bar without a background worker. This will stop the gui from locking, but you won't need to use a Background worker.. which is really just a thread with training wheels.

With easy options like BackgroundWorker (.NET 2) and async in C# (C# 5) now there's really no reason to use DoEvents() anymore, which is an error-prone hack. It may be easy to write, but it's hell to debug. The UI thread is for the UI, using it to perform long-running operations is just bad design.
  • 0

This is all just standard string manipulation, I suggest you take a look at the String class, specifically methods like Trim, Substring, Replace, Concat, Join and Split. For instance here you could store the last entry you added in a temporary string and do a Replace of that with the new one you want. Or you could split by newline characters, exclude the last line, join the result and add the replacement at the end. There are many ways to go about this.

  • 0
  On 13/04/2013 at 01:29, Dr_Asik said:

With easy options like BackgroundWorker (.NET 2) and async in C# (C# 5) now there's really no reason to use DoEvents() anymore, which is an error-prone hack. It may be easy to write, but it's hell to debug. The UI thread is for the UI, using it to perform long-running operations is just bad design.

While I agree with that, it would be a working hack he could use without learning bw's and async stuff.

  • 0

TextBox isn't a very good control to use for console output. It looks like what you're trying to do is simulate a console to display the output from a CLI application you're running in a process. I would recommend using a ListBox instead. You can use the ListBox.Items.Add() method to add each line as it's output then when you get to your progress parsing you can use

ListBox.Items[ListBox.Items.Count - 1] = "<whatever you want>";

to replace the line with the new value. Then only that particular line might flicker. :)

Your second problem is a regular expression issue. If the CLI app's output is different you'd have to match it differently as well. Match(@"(\d+)%.*" ) should get you just the percentage number from your example.

  • 0

Ok the listbox works good but as you said it does flicker quite a bit.

EDIT:

I added this to select the last line which gets rid of the flicker, but how do I change the selected color so that you can't tell it's selected


lstOutputLog.SelectedIndex = lstOutputLog.Items.Count - 1;
[/CODE]

Edited by DPyro
  • 0

Should be clear that I'm replacing:


lstOutputLog.Refresh();
[/CODE]

With:

[CODE]
lstOutputLog.SelectedIndex = lstOutputLog.Items.Count - 1;
[/CODE]

Unless there's a better way to update the listbox after each line is added...

  • 0

You don't need to select anything. You can just change the last line in the Listbox with the code I posted. It will make the last line say "<whatever you want>" the way it is so you would want to use the percentage that you parsed from the process's output instead of the example string. After it doesn't match anymore (i.e.: the process is complete) you can continue logging whatever else it might print using the listbox's Add() method.

  • 0
  On 14/04/2013 at 03:05, GreyWolf said:

You don't need to select anything. You can just change the last line in the Listbox with the code I posted. It will make the last line say "<whatever you want>" the way it is so you would want to use the percentage that you parsed from the process's output instead of the example string. After it doesn't match anymore (i.e.: the process is complete) you can continue logging whatever else it might print using the listbox's Add() method.

This is what I have, but if I don't refresh the listbox somehow then nothing is displayed until the process has ended.


while (!process.HasExited)
{
string pattern = @"(\d+)%.*";
Regex rgx = new Regex(pattern, RegexOptions.IgnoreCase);
StringBuilder output = new StringBuilder(process.StandardOutput.ReadLine());
Match Match = rgx.Match(output.ToString ());
if (!Match.Success)
{
lstOutputLog.Items.Add(output + "\n");
}
else
{
lstOutputLog.Items[lstOutputLog.Items.Count - 1] = (output);
}
}
[/CODE]

The other issue I'm having is that the output freezes every couple of seconds. Think it has something to do with the buffer being filled up and waiting to be flushed. :/

  • 0
  On 14/04/2013 at 05:41, DPyro said:

The other issue I'm having is that the output freezes every couple of seconds. Think it has something to do with the buffer being filled up and waiting to be flushed. :/

So I added this to the cli program which is written in c/c++, which fixes the freezing of stdout


setvbuf(stdout, NULL, _IONBF, 0);
[/CODE]

Only issue I have is that I have to set

[CODE]lstOutputLog.SelectionMode = SelectionMode.One;[/CODE]

at the beginning and

[CODE]lstOutputLog.SelectionMode = SelectionMode.None;[/CODE]

at the end so that the listbox isn't selectable. At the end of program run, the log scrolls back to the top :wacko:

  • 0
  On 14/04/2013 at 05:41, DPyro said:

This is what I have, but if I don't refresh the listbox somehow then nothing is displayed until the process has ended.

That's normal, because you're keeping the UI thread busy doing your loop, it cannot do anything else during that. It cannot even repaint itself. That's why the UI thread is for updating the UI and responding to user events, not performing your long-running computations.

There are different ways to go about fixing this. The classical, most correct way is to spawn a background thread; there many tutorials with full code on how to do this, example: this.

Alternatively, you could still do this on the UI thread, but one small part at a time, for instance by passing what forms the body of your while loop to a timer. This will cause this code to execute repeatedly, but will let the UI execute as it wishes between each two calls.

There's as also, as firey pointed out, a hack that consists of calling Application.DoEvents() in your loop; this effectively triggers a full UI update from the currently executing UI update; as long it doesn't end up calling back into your code, it shouldn't lead to horrifying things. Note the use of conditionals there; this isn't a reliable way, which can cause your app to randomly behave in ways you don't understand. I don't recommend this approach unless you really know what you're doing (and if you did you'd probably never choose this approach).

This topic is now closed to further replies.
  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Posts

    • That's the standard? it needs to have top notch support for Windows games? LOL MacOS have never needed to do that. It's amazing how well Proton runs a lot of Windows games (some games actually better) and it's not appreciated by the causal user but Linux is never going to be for the hardcore gamer. Any serious, day one AAA gamer or needs to play games that use anti-cheat will not be happy with Linux for Windows gaming ever. When I still used Windows it was mostly because of games and I had a second PC for that. An Xbox is another option.
    • You know nothing. Go learn what she really understands, instead of what you've been told about her.
    • That's wonderful. A shame that a few fringe nutjobs will somehow concoct a story to make this something nefarious. I do wish he'd committed to using more of the foundation's funds for scientific/medical research here at home, though. It looks like the U.S. is suddenly poised to become a backwoods nation in research.
    • Scientists explain how water could have actually made Mars the red planet it is today by Sayan Sen For centuries, scientists have wondered why Mars is red. The long-standing theory was that the planet’s rusty color came from hematite, an iron-rich mineral formed in dry conditions. But new research suggests something else might be responsible: ferrihydrite, an iron oxide-hydroxide mineral that forms in wet environments. A study published in Nature Communications by researchers from Brown University and the University of Bern suggests that ferrihydrite (Fe5O8H · nH2O) is the dominant iron-containing mineral in Martian dust. Their findings—based on orbital observations, rover data, and lab experiments—challenge previous ideas about Mars' surface composition. “The fundamental question of why Mars is red has been thought of for hundreds if not thousands of years,” said Adomas Valantinas, a postdoctoral fellow at Brown University. “From our analysis, we believe ferrihydrite is everywhere in the dust and also probably in the rock formations, as well.” Ferrihydrite is formed when iron reacts with oxygen and water. On Earth, it’s commonly found in volcanic rock and ash. Its presence on Mars suggests that the planet was once much wetter, with conditions that could have supported liquid water. This contrasts with hematite, which forms in drier environments. To test their theory, the researchers recreated Martian conditions in a lab. They ground minerals into tiny particles—about 1/100th the width of a human hair—matching the size of real Martian dust. They then studied how light reflected off these particles. The results showed that ferrihydrite remains stable in Mars’ current dry, cold climate, but its structure still holds signs that it formed when the planet had water. “What we know from this study is the evidence points to ferrihydrite forming, and for that to happen there must have been conditions where oxygen and water could react with iron,” Valantinas explained. “Those conditions were very different from today’s dry, cold environment.” To confirm ferrihydrite’s presence, the team studied data from NASA’s Mars Reconnaissance Orbiter and ESA’s Mars Express and Trace Gas Orbiter. They also used spectral readings from rovers like Curiosity, Pathfinder, and Opportunity. Combining all of these sources, they found that the mineral appears widespread on the Martian surface. This discovery challenges previous theories that Mars gradually oxidized in dry conditions. Instead, it suggests that ancient Mars went through a wetter phase before drying out. That shift from a water-rich past to the dry, dusty planet we see today is key to understanding Mars’ climate history—and possibly its ability to support life. “The study is a door-opening opportunity,” said Jack Mustard, senior author of the study. “It gives us a better chance to apply principles of mineral formation and conditions to tap back in time.” While the findings provide strong evidence for ferrihydrite’s role in Mars’ red dust, definitive proof will have to wait until Mars samples—currently being collected by NASA’s Perseverance rover—are brought back to Earth. Scientists hope these samples will confirm the theory and shed more light on the planet’s environmental history. Source: Brown University, University of Bren, Nature | Image via Depositphotos This article was generated with some help from AI and reviewed by an editor. Under Section 107 of the Copyright Act 1976, this material is used for the purpose of news reporting. Fair use is a use permitted by copyright statute that might otherwise be infringing.
    • I would say 98% of people you can't figure out how to install Linux would never attempt to install Windows. It's not news non-tech savvy people can't install an PC OS.
  • Recent Achievements

    • Week One Done
      jrromero17 earned a badge
      Week One Done
    • One Month Later
      jrromero17 earned a badge
      One Month Later
    • Conversation Starter
      johnwin1 earned a badge
      Conversation Starter
    • One Month Later
      Marwin earned a badge
      One Month Later
    • One Year In
      fred8615 earned a badge
      One Year In
  • Popular Contributors

    1. 1
      +primortal
      239
    2. 2
      snowy owl
      156
    3. 3
      ATLien_0
      143
    4. 4
      +FloatingFatMan
      133
    5. 5
      Xenon
      131
  • Tell a friend

    Love Neowin? Tell a friend!