WPF Progressbar Stops after a Few Bars - c#

In my WPF application i have to show a progressbar progress with in a timer tick event, which i am writing as below,
System.Windows.Forms.Timer timer;
public MainWindow()
{
timer = new System.Windows.Forms.Timer();
timer.Interval = 1000;
this.timer.Tick += new System.EventHandler(this.timer_Tick);
}
load event as below
private void Window_Loaded(object sender, RoutedEventArgs e)
{
progressBar1.Minimum = 0;
progressBar1.Value = DateTime.Now.Second;
progressBar1.Maximum = 700;
timer.Start();
}
And at last in tick event,
private void timer_Tick(object sender, EventArgs e)
{
Duration duration = new Duration(TimeSpan.FromSeconds(20));
//progress bar animation
System.Windows.Media.Animation.DoubleAnimation doubleanimation = new System.Windows.Media.Animation.DoubleAnimation(200.0, duration);
progressBar1.BeginAnimation(ProgressBar.ValueProperty, doubleanimation);
}
When the program's progressbar shows the progress for two-three bars and then it stops increment. Later there is no effect in the progress at all.
Why?

Since your ProgressBar doesn't relate to any particular behavior, it looks like a job for an indeterminate bar.
This other SO question provides some insight about it. In short, it's a XAML one-liner:
<!-- MinVal, MaxVal, Height needed for this to work -->
<ProgressBar x:Name="progressBar1" Margin="5" IsIndeterminate="True"
MinimumValue="0" MaximumValue="700" value="0" Height="20"/>
Then in code, you go like this:
progressBar1.IsIndeterminate = true; // start animation
progressBar1.IsIndeterminate = false; // stop animation

In my WPF application I have ... System.Windows.Forms.Timer timer;
That is the wrong type of timer. Use a DispatcherTimer instead.
When i execute my program progressbar shows the progress for two-three bars and then it stops
This surprises me, I wouldn't have expected it to work at all. This means you may have other problems too, like blocking the main (dispatcher) thread.
You are only setting the Value once, in the Loaded event:
progressBar1.Value = DateTime.Now.Second;
There is no change to progressBar1.Value in the Tick event. So it figures that it stops moving.

Use DispatcherTimer instead of Timer (Forms object), and use Value property of ProgressBar.
Try this:
MainWindows.xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="55" Width="261">
<Grid>
<ProgressBar Name="pb" Maximum="60" />
</Grid>
</Window>
MainWindows.xaml.cs:
using System.Windows;
using System.Windows.Threading;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
private DispatcherTimer timer;
public MainWindow()
{
InitializeComponent();
this.timer = new DispatcherTimer();
this.timer.Tick += timer_Tick;
this.timer.Interval = new System.TimeSpan(0, 0, 1);
this.timer.Start();
}
private void timer_Tick(object sender, System.EventArgs e)
{
this.pb.Value = System.DateTime.Now.Second % 100;
}
}
}
You can change the behaviour of the progress bar by changing the Value property (don't forget defining the Maximum property in the xaml).

I found this (WPF Multithreading: Using the BackgroundWorker and Reporting the Progress to the UI. link) to contain a great solution for my needs, albeit with a dialog box.
The one thing I found very useful was that the worker thread couldn't access the MainWindow's controls (in its own method). However, when using a delegate inside the main windows event handler, it was possible.
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
pd.Close();
// Get a result from the asynchronous worker
T t = (t)args.Result
this.ExampleControl.Text = t.BlaBla;
};

Related

C# - Problem with System.Timer Tick Eventhandling

I've got a problem while programming a little game for myself.
I'm using the "System.Timers"-Timer and want to decrease the value of a progress bar
by every tick of the timer. There I faced my problem. I can't set a custom event handler to decrease the value of the progress bar.
I've using for the Timer the following code:
private Timer t = new Timer();
t.Interval = 600000;
t.Elapsed += Ended; //For ending event
t.AutoReset = true;
So how can I register a tick to decrease the value of the progress bar.
Thank you for your answers in advance.
Greetings
SirCodiac
You cant invoke a control from system.timers. In order to invoke the progress bar either use System.Windows.Forms.Timer or use a MethodInvoker as below:
private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (progressBar1.InvokeRequired)
{
progressBar1.Invoke(new MethodInvoker(delegate { progressBar1.Value++; }));
}
else
{
progressBar1.Value++;
}
}

Show a running timer in a WPF window

I need to show a running timer on the window along with information of a test, such as ID, test name, status, start time, end time, etc.
I really wish that I could have a timer control on the page that tells the user that how long the test has been running.
How would one add a running timer on the page?
In addition, if this is possible, I wish my timer could start from some specific time instead of 00:00:00. The reason I need this is because the user can open this page when the test has been running for a while, and the elapsed time shown on the timer should be (current_time - start_time) and start from here.
If the test start at: 7:00 AM and the user opens the page at 7:05AM and the test is still running, the timer should start from 00:05:00.
Here is a very basic example I threw together.
using System.Windows.Threading;
namespace BasicTimer
{
public partial class MainWindow : Window
{
DispatcherTimer t;
DateTime start;
public MainWindow()
{
InitializeComponent();
t = new DispatcherTimer(new TimeSpan(0, 0, 0, 0, 50), DispatcherPriority.Background,
t_Tick, Dispatcher.CurrentDispatcher); t.IsEnabled = true;
start = DateTime.Now;
}
private void t_Tick(object sender, EventArgs e)
{
TimerDisplay.Text = Convert.ToString(DateTime.Now - start);
}
MainWindow XAML
<Window x:Class="BasicTimer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="100" Width="200">
<Grid>
<TextBlock x:Name="TimerDisplay" HorizontalAlignment="Left"/>
</Grid>
</Window>
Here is how I achieved this.
Stopwatch watch = new Stopwatch();
private void StartTimer()
{
new Thread(() =>
{
watch.Restart();
while (watch.IsRunning)
{
Dispatcher.Invoke(() =>
{
timeText.Text = Math.Round(watch.Elapsed.TotalSeconds, 2).ToString() + "s";
});
}
}).Start();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
//i had to start and stop the timer inside a thread, i was having issues without doing so
new Thread(() =>
{
StartTimer();
//I was calling an api here
Dispatcher.Invoke(() =>
{
messageText.Text = response.message;
});
watch.Stop();
}).Start();
}

Working with Backgroundworker

I want to load a text file in WPF-RichTextbox using BackgroundWorker
Here is my code :
private delegate void update();
private void reader()
{
StreamReader str = new StreamReader("C:\\test.txt");
while (!str.EndOfStream)
{
richTextBox1.AppendText(str.ReadToEnd());
}
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
progress wnd = new progress();
BackgroundWorker bg = new BackgroundWorker();
bg.DoWork += delegate(object s, DoWorkEventArgs args)
{
update up = new update(reader);
richTextBox1.Dispatcher.Invoke(up);
};
bg.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
wnd.Close();
};
wnd.Show();
bg.RunWorkerAsync();
}
and for progress.xaml :
<Window x:Class="WpfApplication3.progress"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="progress" Height="192" Width="452" ResizeMode="NoResize" ShowInTaskbar="False" Topmost="True" WindowStartupLocation="CenterScreen" WindowStyle="None">
<Grid>
<Label Content="loading ...." Height="28" HorizontalAlignment="Left" Margin="46,32,0,0" Name="label1" VerticalAlignment="Top" />
<ProgressBar Height="29" HorizontalAlignment="Left" Margin="46,78,0,0" Name="progressBar1" VerticalAlignment="Top" Width="325" IsIndeterminate="True" />
</Grid>
When I set wnd.showdialog() , it shows the indeterminate status of progress bar but the application does not load any text .
When I set wnd.show() , it loads the text but when loading , the progress bar freezes and
does not show indeterminate status.
Thanks for any help.
EDIT :
due to Servey and SLaks answers , i have updated the code but it has the same problem (it loads the text but when loading , the progress bar freezes and does not show indeterminate status.)
what is the problem? here is my updated code :
StringBuilder sb = new StringBuilder();
private void reader()
{
StreamReader str = new StreamReader("C:\\test.txt");
while (!str.EndOfStream)
{
sb.Append(str.ReadToEnd());
}
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
progress wnd = new progress();
BackgroundWorker bg = new BackgroundWorker();
bg.DoWork += delegate(object s, DoWorkEventArgs args)
{
reader();
};
bg.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
richTextBox1.AppendText(sb.ToString());
wnd.Close();
};
wnd.Show();
bg.RunWorkerAsync();
}
Ok I've had a look at your uploaded code and your problem is most probably with how you're doing the loads. As far as your loading goes, the following code does the job as you want it to:
MainWindow.xaml.cs
public MainWindow()
{
InitializeComponent();
Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
Loaded -= OnLoaded;
var wnd = new ProgressWindow();
var dispatcher = wnd.Dispatcher;
Task.Factory.StartNew(() =>
{
using (var stream = new StreamReader("C:\\test.txt"))
return stream.ReadToEnd();
}).ContinueWith(x =>
{
//Main window dispatcher
Dispatcher.Invoke(DispatcherPriority.Background, new Action(() => richTextBox1.AppendText(x.Result)));
//progress window dispatcher
dispatcher.Invoke(DispatcherPriority.DataBind, new Action(wnd.Close));
});
wnd.ShowDialog();
}
While this works as intended, the freeze will happen regardless (i had a freeze of several seconds loading an 800kb text file). The problem is your use of RichTextBox. Judging by your code, you have no idea how wfp works and you're trying to work in a typical winform style.
RichTextBox in Wpf is very different from winforms RichTextBox. Similarly, a TextBlock is very different from a typical Label. If all you want is color and other formatting options, a TextBlock is very capable of doing that on its own and is far faster than building RichTextBox the way you're building.
If you must build RichTextBox from a text file, you need to learn about FlowDocument and how to create one from a text file yourself in order to avoid the freeze. The alternate to this solution will be running two UI threads (one for each window) and you're very likely to find that a lot more hassle than its worth.
Before attempting this sort of thing, you should sort your WPF basics out and learn the typical MVVM pattern before taking a dive into threaded WPF world. And again, avoid RTB in WPF if you can. The base controls in WPF are far more powerful than their winforms counterparts if you know how to use them.
You're not actually doing any work in the background. You're starting a background worker and the the only thing that the worker is doing in the background is scheduling an operation to run on the UI thread, and that operation is where you do all of your non-UI work. Since you're doing non-UI work on the UI thread, no other graphical operations can be performed until it finishes.
You'll want to read from the file in the BGW's do work event, store it in memory, and then in the Completed event, that runs in the UI thread, you can then place that data into the UI.

.NET BackgroundWorker: I don't understand how ReportProgress works

I created a BackgroundWorker (mainBw) in my UI (WPF) thread. It has an infinite loop where it sleeps for 1.5 sec and calls a function via Application.Current.Dispatcher.Invoke which just outputs text from the "global" text variable to a TextBox.
Also before the loop it created another (child) BackgroundWorker which ReportsProgress, in ProgressChanged event handler it modifies the text variable.
I thought that it will not work because there is no anything like WinForms Application.DoEvents() in the mainBw loop so it can't process the event handler. But it works. Why?
Here is the code:
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows;
namespace WpfApplication6
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private BackgroundWorker mainBw = new BackgroundWorker();
private void Button_Click(object sender, RoutedEventArgs e)
{
mainBw.DoWork += MainBwOnDoWork;
mainBw.RunWorkerAsync();
btn.IsEnabled = false;
}
private string text = "abc";
private void MainBwOnDoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += BwOnDoWork;
bw.ProgressChanged += BwOnProgressChanged;
bw.WorkerReportsProgress = true;
bw.RunWorkerAsync();
while (true)
{
Thread.Sleep(1500);
text += " main ";
Application.Current.Dispatcher.Invoke(new Action(() => { WriteToUIThread(); }));
}
}
private void WriteToUIThread()
{
tbox.Text = DateTime.Now + " " + text + Environment.NewLine + tbox.Text;
}
private void BwOnProgressChanged(object sender, ProgressChangedEventArgs e)
{
text += e.UserState.ToString();
}
private void BwOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
while (true)
{
Thread.Sleep(3000);
(sender as BackgroundWorker).ReportProgress(0, "child");
}
}
}
}
// XAML
<Window x:Class="WpfApplication6.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Name="btn" Content="Button" HorizontalAlignment="Left" Height="105" Margin="43,47,0,0" VerticalAlignment="Top" Width="165" Click="Button_Click"/>
<TextBox Name="tbox" HorizontalAlignment="Left" Height="114" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="456" Margin="27,182,0,0"/>
</Grid>
</Window>
BackgroundWorker uses a universal way to get code to run on the UI thread, it uses the static SynchronizationContext.Current property to find a synchronization provider. ReportProgress() uses its Post() method to marshal the call.
If you run a Winforms app then the Current property will reference an instance of WindowsFormsSynchronizationContext class. Automatically installed when you create a Form or call Application.Run(). It uses Control.Begin/Invoke() to implement the Post and Send methods.
And if you run a WPF app then the Current property will reference an instance of DispatcherSynchronizationContext, it uses Dispatcher.Begin/Invoke().
So this just works automagically.
It works because the BackgroundWorker does work in a background thread (hence the name). Since it's not running in the UI thread, it's not blocking the UI thread, it is just sending short methods to be run in the UI thread every once in a while.
That said, it's still not a particularly well designed approach to solving the problem. If you want to run some code every 3 seconds just use a Timer instead. If you use the timer in the Forms namespace it will fire it's event in the UI thread on your behalf.
It works, because the BackgroundWorker runs - as its name says - in a background thread independently fromo the main UI thread.
The ReportProgress-event gets marshalled to the UI thread so that this event can easily be handled, without calling Invoke-methods on the controls involved.
Contraray, the Application.DoEvents()-method in WinForms allowed the process to handle other messages for perfoming long going operations in main thread, without using a background thread.

StatusBar with timeout for messages implementation

I want to implement a statusbar where i will be able to display messages for a specified period of time after which they will fadeout.
Can this be done with any ready made control in wpf? I can't find such a functionality in StatusBar which as far as i understand is a container of other items.
Any suggestions?
StatusBar is indeed only a container of other items.
I think there isn't any built-in functionality like this.
But you can use a Timer to achieve what you want.
Make a method that sets the text message as the content of a StatusBarItem, set a time in the Timer and in the Timer.Elapsed event you remove the text from the StatusBarItem.
StatusBar XAML:
<StatusBar Height="25" Margin="5">
<StatusBarItem x:Name="StatusMessage" />
</StatusBar>
Procedural code:
private void ShowStatusMessage(string message)
{
StatusMessage.Content = message;
var timer = new System.Timers.Timer();
timer.Interval = 2000; //2 seconds
timer.Elapsed += delegate(object sender, System.Timers.ElapsedEventArgs e)
{
//stop the timer
timer.Stop();
//remove the StatusMessage text using a dispatcher, because timer operates in another thread
this.Dispatcher.BeginInvoke(new Action(() =>
{
StatusMessage.Content = "";
}));
};
timer.Start();
}
If you want to animate you should look for DoubleAnimation
Just bind the data in the progress bar and update it whenever faded out. Let us know more details for giving better solutions

Categories

Resources