• 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

    • Microsoft announces multi-year partnership with AMD for future of Xbox consoles and hardware by Pulasthi Ariyasinghe The Xbox Games Showcase was earlier this month with its own hardware announcement, but Microsoft had kept a major partnership under wraps for now. Today, it revealed that it has established a "strategic multi-year partnership with AMD" to develop its next-generation Xbox consoles and other devices. In a video uploaded to the Xbox social channels, Xbox president Sarah Bond announced the partnership with AMD. Remember that Microsoft's Xbox Series X|S consoles and the previous Xbox One lineup already tout AMD silicon inside, so the continued partnership isn't a big surprise. "Together with AMD we're advancing the state of art in gaming silicon to deliver the next generation of graphics innovation to unlock a deeper level of visual quality and immersive gameplay and player experiences enhanced with the power of AI, all while maintaining compatibility with your existing library of Xbox games," said Bond in the video promoting the partnership. The two Xbox Ally Windows gaming handhelds that were announced earlier this month also come with AMD internals, offering handheld gaming experiences with a unique, lightweight version of bloat-free Windows 11. What was interesting were the few hints that were dropped regarding the focus of the Xbox brand going forward. "This is all about building you a gaming platform that's always with you, so you can play the games you want across devices anywhere you want, delivering you an Xbox experience not locked to a single store or tied to one device," added Bond. It was also confirmed that the next generation of Xbox devices will continue to offer backward compatibility, letting players keep their existing libraries when moving forward. Finishing off the video, Bond confirmed that the Xbox team is now working on Windows to make it the "number one platform for gaming," perhaps alluding to moving away from being focused on home consoles.
    • Kudos to the happy talented people who created that happy trailer.
    • Maybe the site will get out of dabbing into Identity Politics.
    • ASUS TUF Gaming A16 (2024): A capable gaming laptop with AI for $300 less by Paul Hill If you are in the market for a mid-range gaming laptop packed with AI features, check out the ASUS TUF Gaming A16 2024 laptop, which is now discounted by 19% to just $1,299. The list price of this laptop is $1,599.99, so you’re saving $300 by taking advantage of this deal. Inside are the AMD Ryzen AI 9 HX 370 processor, which supports Windows AI features such as Recall, and the Nvidia GeForce RTX 4060 GPU, which offers solid gaming performance at 1080p. What you get: Features and performance for gamers The ASUS TUF A16 includes a 16-inch display with a 16:10 aspect ratio. Its refresh rate is 165Hz to minimize lag, it supports Nvidia G-Sync, and it has a 2.5K resolution. Inside the device, we have the AMD Ryzen AI 9 HX 370 processor, Nvidia GeForce RTX 4060 GPU, 16GB of LPDDR5X RAM, and a 1TB PCIe Gen 4.0 SSD. A nice thing ASUS has taken into consideration with this laptop is durability. It is MIL-STD-810H certified, meaning it has passed durability tests. While the laptop is discounted, it’s not cheap, so knowing it’s durable is a plus. In terms of cooling, it features Arc Flow Fans, four exhaust vents, five head pipes, and an anti-dust filter. Combined, these will keep the laptop running cool, providing better gaming performance. As for sound, the A16 features Dolby Atmos, Hi-Res Audio, and two-way AI noise cancellation. All of this will make your gaming sessions more immersive, and the noise cancellation will ensure you can hear your teammate properly. The laptop runs Windows 11 and includes 3 months of PC Game Pass, so you can play games right away, and save some money given the rising cost of games. Who should buy this laptop? Buyers can expect solid performance from this laptop for its price point. With the fast SSD, AI-capable CPU, and decent graphics card, you can expect to get most school or work tasks done easily, and it should capably handle games up to 1080p. The cooling features are nice because they will prevent any performance degradation, and the MIL-STD-810H rating offers peace of mind related to durability. If you are seeking the cutting edge of gaming performance, this laptop may not be for you. Additionally, if you aren’t much of a gamer or a creator, this laptop could be overkill, and you’d get away with buying something cheaper. If you want to pick it up, check out the buying link below. ASUS TUF Gaming A16: $1,299 (Amazon US) / MSRP $1,599.99 This Amazon deal is US-specific and not available in other regions unless specified. If you don't like it or want to look at more options, check out the Amazon US deals page here. Get Prime (SNAP), Prime Video, Audible Plus or Kindle / Music Unlimited. Free for 30 days. As an Amazon Associate, we earn from qualifying purchases.
    • After Elon has been high as a kite IN the oval office on video, I don't think anyone can mention Hunter and it mean anything anymore. As far as I'm concerned, Elon wiped the Hunter laptop issue away. And Marge Traitor Green just used that laptop to show congress Hunter naked so that she could look at him more! That was horrible.
  • Recent Achievements

    • Reacting Well
      Cole Multipass earned a badge
      Reacting Well
    • Reacting Well
      JLP earned a badge
      Reacting Well
    • Week One Done
      Rhydderch earned a badge
      Week One Done
    • Experienced
      dismuter went up a rank
      Experienced
    • One Month Later
      mevinyavin earned a badge
      One Month Later
  • Popular Contributors

    1. 1
      +primortal
      696
    2. 2
      ATLien_0
      275
    3. 3
      Michael Scrip
      218
    4. 4
      +FloatingFatMan
      188
    5. 5
      Steven P.
      146
  • Tell a friend

    Love Neowin? Tell a friend!