When to start the WPF Progress Bar - wpf

I want my app to show a running progress bar while doing some components checking. However, due to my lack of knowledge in Desktop app programming and WPF, I cannot find suitable place for it.
I tried to show the incrementing the progress Bar during the Window_Loaded(), ContentRendered() but with no luck.
Instead of showing the progressBar increases, it just show the final state of the progress Bar.
Here is the code
public partial class Loading : Window
{
public Loading()
{
InitializeComponent();
SetProgressBar();
this.Show();
CheckComponents();
}
private void CheckComponents()
{
System.Threading.Thread.Sleep(3000);
CheckProductionDBConnection();
pgrsBar.Value = 30;
System.Threading.Thread.Sleep(3000);
CheckInternalDBConnection();
pgrsBar.Value = 60;
System.Threading.Thread.Sleep(3000);
CheckProductionPlanning();
pgrsBar.Value = 90;
//MainWindow mainWindow = new MainWindow();
//mainWindow.Show();
}
private void SetProgressBar()
{
pgrsBar.Minimum = 0;
pgrsBar.Maximum = 100;
pgrsBar.Value = 0;
}
//more code down here...
Where should I put the CheckComponents() method?

You could put this code in an event handler subscribed to the Activated event. The one catch with this is that the Activated event is fired every time the window receives focus after having lost it. To get around this, the first thing you can do in your event handler is unsubscribe from the Activated event so that your code is executed only the first time the window is activated.
You also need to offload this work to a worker thread if you don't want the delay to block the main thread. If you do that, you'll have to invoke your calls to update the progess bar's value.
Here's some sample code to get you started:
public Loader()
{
InitializeComponent();
SetProgressBar();
this.Activated += OnActivatedFirstTime;
}
private void OnActivatedFirstTime(object sender, EventArgs e)
{
this.Activated -= this.OnActivatedFirstTime;
ThreadPool.QueueUserWorkItem(x =>
{
System.Threading.Thread.Sleep(3000);
CheckProductionDBConnection();
this.Dispatcher.BeginInvoke(new Action(() => pgrsBar.Value = 30));
System.Threading.Thread.Sleep(3000);
CheckInternalDBConnection();
this.Dispatcher.BeginInvoke(new Action(() => pgrsBar.Value = 60));
System.Threading.Thread.Sleep(3000);
CheckProductionPlanning();
this.Dispatcher.BeginInvoke(new Action(() => pgrsBar.Value = 90));
});
}
private void SetProgressBar()
{
pgrsBar.Minimum = 0;
pgrsBar.Maximum = 100;
pgrsBar.Value = 0;
}

Related

Hide cursor when idle

In my WPF app, I want to hide the cursor when it hasn't moved for a number of seconds.
If it is moved, I want to show it again.
Any ideas?
You could use MouseMove event like this :
Tested code:
myTimer = new Timer(3000);
myTimer.AutoReset = false;
myTimer.Elapsed += delegate { MouseExt.SafeOverrideCursor(Cursors.None); }; //Hide cursor
private void MyView_MouseMove(object sender, MouseEventArgs e)
{
myTimer.Stop();
Mouse.OverrideCursor = null; //Show cursor
myTimer.Start();
}
This is a helper for dispatching properly the call to override the Cursor:
public static class MouseExt
{
public static void SafeOverrideCursor(Cursor cursor)
{
Application.Current.Dispatcher.Invoke(new Action(() =>
{
Mouse.OverrideCursor = cursor;
}));
}
}
When the timer elapses the cursor is hidden. When its moved it reappears and timer is reset.

Timer not getting called when backgroundworker running

I have a WPF window with a button that spawns a BackgroundWorker thread to create and send an email. While this BackgroundWorker is running, I want to display a user control that displays some message followed by an animated "...". That animation is run by a timer inside the user control.
Even though my mail sending code is on a BackgroundWorker, the timer in the user control never gets called (well, it does but only when the Backgroundworker is finished, which kinda defeats the purpose...).
Relevant code in the WPF window:
private void button_Send_Click(object sender, RoutedEventArgs e)
{
busyLabel.Show(); // this should start the animation timer inside the user control
BackgroundWorker worker = new BackgroundWorker();
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerAsync();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
this.Dispatcher.Invoke((Action)(() =>
{
string body = textBox_Details.Text;
body += "User-added addtional information:" + textBox_AdditionalInfo.Text;
var smtp = new SmtpClient
{
...
};
using (var message = new MailMessage(fromAddress, toAddress)
{
Subject = subject,
Body = body
})
{
smtp.Send(message);
}
}));
}
Relevant code in the user control ("BusyLabel"):
public void Show()
{
tb_Message.Text = Message;
mTimer = new System.Timers.Timer();
mTimer.Interval = Interval;
mTimer.Elapsed += new ElapsedEventHandler(mTimer_Elapsed);
mTimer.Start();
}
void mTimer_Elapsed(object sender, ElapsedEventArgs e)
{
this.Dispatcher.Invoke((Action)(() =>
{
int numPeriods = tb_Message.Text.Count(f => f == '.');
if (numPeriods >= NumPeriods)
{
tb_Message.Text = Message;
}
else
{
tb_Message.Text += '.';
}
}));
}
public void Hide()
{
mTimer.Stop();
}
Any ideas why it's locking up?
Using Dispatcher.Invoke in your worker_DoWork method is putting execution back on the UI thread, so you are not really doing the work asynchronously.
You should be able to just remove that, based on the code you are showing.
If there are result values that you need to show after the work is complete, put it in the DoWorkEventArgs and you will be able to access it (on the UI thread) in the worker_RunWorkerCompleted handler's event args.
A primary reason for using BackgroundWorker is that the marshalling is handled under the covers, so you shouldn't have to use Dispatcher.Invoke.

Silverlight Mouse Events: MouseEnter and MouseLeave conflicts

I have a collection of buttons in a grid. For each one of these buttons, I want to handle the MouseEnter and MouseLeave events to animate the height of the button (and do some other interesting stuff). It all works good until I start moving my mouse too fast over and off the buttons which eventually cause the events to take place at before the other is complete. What's the best way of making sure the events wait for eachother before being triggered?
UPDATE:
Going by x0r's advice, I refactored my code into an internal class which inherits from Button and has the required methods to perform the animations. Unfortunately, this did not really solve the problem because - I think - I'm handling the Completed event of the first animation in two separate places. (correct me if I'm wrong). Here's my code:
internal class MockButton : Button
{
#region Fields
private Storyboard _mouseEnterStoryBoard;
private Storyboard _mouseLeaveStoryBoard;
private Double _width;
#endregion
#region Properties
internal Int32 Index { get; private set; }
#endregion
#region Ctors
internal MockButton(Int32 index) : this(index, 200)
{
}
internal MockButton(Int32 index, Double width)
{
this.Index = index;
this._width = width;
}
#endregion
#region Event Handlers
internal void OnMouseEnter(Action action, Double targetAnimationHeight)
{
if (_mouseEnterStoryBoard == null)
{
_mouseEnterStoryBoard = new Storyboard();
DoubleAnimation heightAnimation = new DoubleAnimation();
heightAnimation.From = 10;
heightAnimation.To = targetAnimationHeight;
heightAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(300));
_mouseEnterStoryBoard.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("Height"));
Storyboard.SetTarget(heightAnimation, this);
_mouseEnterStoryBoard.Children.Add(heightAnimation);
}
_mouseEnterStoryBoard.Completed += (s, e) =>
{
action.Invoke();
};
_mouseEnterStoryBoard.Begin();
}
internal void OnMouseLeave()
{
if (_mouseLeaveStoryBoard == null)
{
_mouseLeaveStoryBoard = new Storyboard();
DoubleAnimation heightAnimation = new DoubleAnimation();
heightAnimation.To = 10;
heightAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(300));
_mouseLeaveStoryBoard.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("Height"));
Storyboard.SetTarget(heightAnimation, this);
_mouseLeaveStoryBoard.Children.Add(heightAnimation);
}
if (_mouseEnterStoryBoard.GetCurrentState() != ClockState.Stopped)
{
_mouseEnterStoryBoard.Completed += (s, e) =>
{
_mouseLeaveStoryBoard.Begin();
};
}
else
{
_mouseLeaveStoryBoard.Begin();
}
}
#endregion
}
UPDATE 2:
Some events are getting triggered multiple times. An example of that is the Click event on the close button of my Rule object...
public Rule(Action<Int32> closeAction)
{
this.Style = Application.Current.Resources["RuleDefaultStyle"] as Style;
this.CloseAction = closeAction;
this.Loaded += (s, e) =>
{
if (_closeButton != null)
{
_closeButton.Click += (btn, args) =>
{
if (this.CloseAction != null)
{
this.CloseAction.Invoke(this.Index);
}
};
if (_closeButtonShouldBeVisible)
{
_closeButton.Visibility = System.Windows.Visibility.Visible;
}
}
};
}
And below is the Action<Int32> I'm passing to the Rule object as the CloseAction:
private void RemoveRule(Int32 ruleIndex)
{
Rule ruleToRemove = Rules.FirstOrDefault(x => x.Index.Equals(ruleIndex));
Storyboard sb = new Storyboard();
DoubleAnimation animation = new DoubleAnimation();
sb.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("Opacity"));
animation.Duration = TimeSpan.FromMilliseconds(300);
animation.From = 1;
animation.To = 0;
sb.Children.Add(animation);
Storyboard.SetTarget(animation, ruleToRemove);
sb.Completed += (s, e) =>
{
if (Rules.FirstOrDefault(x => x.Index.Equals(ruleIndex)) != null)
{
this.Rules.RemoveAt(ruleIndex);
}
};
sb.Begin();
}
UPDATE 3:
In order to avoid the animations running too early, I thought I could delay the MouseEnter event, so if the user just scrolls over the item too fast, it doesn't kick off. But I have a problem now: Say the user mouses over the item and then mouses out. If I use the Storyboard.BeginTime property, that won't safe guard against that behavior because eventhough the animation gets delayed, it's still going to start eventually... So is there a way I could prevent that from happening?
Any suggestions?
check in your mouseleave eventhandler if the first storyboard is still running and if that is the case attach the starting of the second storyboard to the Completed event of the first storybaord:
private void OnOddRowMouseLeave(object sender, MouseEventArgs e)
{
...
if(_firstStoryboard.GetCurrentState() != ClockState.Stopped)
_firstStoryboard.Completed += (s,e) => _secondStoryboard.Begin();
else
_secondStoryboard.Begin()
Everything that Silverlight does is asyncronous and so most likely what is happening is that because you are moving quickly in and out of the box the mouse leave is being fired before the mouseenter has a chance to finish. You could setup your two events so thay they have an indicator of whether or not the other is in process. For example you could do this
bool mouseOut =false;
bool mouseIn =false;
void OnMouseEnter(Action action, Double targetAnimationHeight)
{
if(!this.mouseOut)
{
this.mouseIn = true;
if (_mouseEnterStoryBoard == null)
{
_mouseEnterStoryBoard = new Storyboard();
DoubleAnimation heightAnimation = new DoubleAnimation();
heightAnimation.From = 10;
heightAnimation.To = targetAnimationHeight;
heightAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(300));
_mouseEnterStoryBoard.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("Height"));
Storyboard.SetTarget(heightAnimation, this);
_mouseEnterStoryBoard.Children.Add(heightAnimation);
}
_mouseEnterStoryBoard.Completed += (s, e) =>
{
action.Invoke();
};
_mouseEnterStoryBoard.Begin();
if(this.mouseOut)
{
this.OnMouseLeave();
}
this.mouseIn = false;
}
}
void OnMouseLeave()
{
if(!this.mouseIn)
{
this.mouseOut = false;
if (_mouseLeaveStoryBoard == null)
{
_mouseLeaveStoryBoard = new Storyboard();
DoubleAnimation heightAnimation = new DoubleAnimation();
heightAnimation.To = 10;
heightAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(300));
_mouseLeaveStoryBoard.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("Height"));
Storyboard.SetTarget(heightAnimation, this);
_mouseLeaveStoryBoard.Children.Add(heightAnimation);
}
if (_mouseEnterStoryBoard.GetCurrentState() != ClockState.Stopped)
{
_mouseEnterStoryBoard.Completed += (s, e) =>
{
_mouseLeaveStoryBoard.Begin();
};
}
else
{
_mouseLeaveStoryBoard.Begin();
}
}
else
{
this.mouseOut = true;
}
}
I haven't actually checked this code but this should help you to at least get closer to what you want. This should be quick enough that your user doesn't realize that it is not firing exactly on exit if they go over it quickly. But this should help to keep you from getting overlap.
Another way you could do this is setup the initial events as null, and have the mouse in event set the mouse in event when it is complete but the problem with that is that if the mouse out fires before the event is set then you don't event get the event firing.

Display busyindicator in wpf application

I have a BusyIndicator from the wpf extended toolkit and I'm running a function that takes a while to complete. If I run the time consuming task in a separate thread, I get a NotSupportedException because I'm attemping to insert objects into an ObservableCollection from that different thread. I don't really want to spend a lot of time refactoring the code, if possible... Is there a way that I can set the visibility of the indicator in a separate thread instead?
EDIT
ThreadStart start = delegate()
{
System.Windows.Application.Current.Dispatcher.Invoke((Action)(() =>
{
IsBusy = true;
}));
};
new Thread(start).Start();
longRunningFunction();
This did not work for me either.
You should be able to use the Dispatcher for things like that. e.g.
Application.Current.Dispatcher.Invoke((Action)(() =>
{
_indicator.Visibility = Visibility.Visible;
}));
This will cause the code to be run on the UI-Thread.
There is more info (including how to "properly" do this, with CheckAccess and such) on it in the threading model reference.
You cannot access UI controls from a background worker. What you normally do is set the IsBusy to true before you call BackgroundWorker.RunWorkerAync(), then in the BackgroundWorker.RunWorkerCompleted event handler you would set the IsBusy to false. Seomthing like:
Backgroundworker worker = new BackgroundWorker();
worker.DoWork += ...
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
IsBusy = false;
};
IsBusy = true;
worker.RunWorkerAsync();
You can use the Dispatcher to add items to your ObservableCollection while in the DoWork event hanlder.
EDIT: Here is the complete solution
private void Button_Click(object sender, RoutedEventArgs e)
{
//on UI thread
ObservableCollection<string> collection;
ThreadStart start = delegate()
{
List<string> items = new List<string>();
for (int i = 0; i < 5000000; i++)
{
items.Add(String.Format("Item {0}", i));
}
System.Windows.Application.Current.Dispatcher.Invoke((Action)(() =>
{
//propogate items to UI
collection = new ObservableCollection<string>(items);
//hide indicator
_indicator.IsBusy = false;
}));
};
//show indicator before calling start
_indicator.IsBusy = true;
new Thread(start).Start();
}

Silverlight & C# - Open a childwindow only if a time frame passes

Hopefully the title makes sense but I will discribe my issue. I am using a childwindow in Silverlight to display a Processing message and rotating image when the UI is doing some work. Once a Completed Event is called, the window then closes.
Problem is that it does look a little ugly when the UI performs a quick task as the child window opens and then closes in under 1 second.
What I want to be able to do is have the child window open only if 2 seconds of processing has passed and then close on complete.
I have added a section of my xaml where I am calling the child below. I have searched but cannot find anything on this and it might not be possible.
void edit_Closed(object sender, EventArgs e)
{
EditChannelDetails edit = sender as EditChannelDetails;
if (edit.DialogResult == true)
{
if (edit != null)
{
Channel edited = new Channel();
edited.channelId = Int32.Parse(edit.ChannelID.Text);
edited.name = edit.ChannelName.Text;
edited.description = edit.ChannelDescription.Text;
ChannelClient proxy = new ChannelClient(new BasicHttpBinding(), new EndpointAddress("http://servername"));
proxy.UpdateChannelCompleted += new EventHandler<UpdateChannelCompletedEventArgs>(proxy_UpdateChannelCompleted);
proxy.UpdateChannelAsync(edited);
}
}
processingDialog.Show();
}
void proxy_UpdateChannelCompleted(object sender, UpdateChannelCompletedEventArgs e)
{
processingDialog.Close();
Etc.....
Boolean closeFlag = false;
Start a timer:
DispatcherTimer timer = new DispatcherTimer() { Interval = TimeSpan.FromSeconds(2) };
timer.Tick += (tts, tte) => {
timer.Stop();
closeFlag = true;
};
timer.Start();
And check flag:
if (!closeFlag)
{
processingDialog.Close();
}

Resources