I'm trying to come up with an unobtrusive way to to display minor error messages to a user. So I've added a statusbar to my form,
<StatusBar Margin="0,288,0,0" Name="statusBar" Height="23" VerticalAlignment="Bottom">
<TextBlock Name="statusText">Ready.</TextBlock>
</StatusBar>
And then when they click an "Add" button, it should do some stuff, or display an error message:
private void DownloadButton_Click(object sender, RoutedEventArgs e)
{
addressBar.Focus();
var url = addressBar.Text.Trim();
if (string.IsNullOrEmpty(url))
{
statusText.Text = "Nothing to add.";
return;
}
if (!url.Contains('.'))
{
statusText.Text = "Invalid URL format.";
return;
}
if (!Regex.IsMatch(url, @"^\w://")) url = "http://" + url;
addressBar.Text = "";
But the message just sits there for the life of the app... I think I should reset it after about 5 seconds... how can I set a timer to do that?
Bonus: How do I give it a nifty fade-out effect as I do so?
I've created a System.Timers.Timer
,
private Timer _resetStatusTimer = new Timer(5000);
void _resetStatusTimer_Elapsed(object sender, ElapsedEventArgs e)
{
statusText.Text = "Ready";
}
But the Elapsed
event runs on a different thread than the UI, which it doesn't like... how to I get around that?
You can use a Storyboard to do the trick.
<Storyboard x:Key="Storyboard1">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="statusBarItem">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="1"/>
<EasingDoubleKeyFrame KeyTime="0:0:3" Value="1"/>
<EasingDoubleKeyFrame KeyTime="0:0:4" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
When the message has to be displayed you just call programmatically the Begin method of the StoryBoard or insert a trigger as below.
<Window.Triggers>
<EventTrigger RoutedEvent="TextBoxBase.TextChanged" SourceName="textBox">
<BeginStoryboard Storyboard="{StaticResource Storyboard1}"/>
</EventTrigger>
</Window.Triggers>
EDIT: Another option is to do like this:
<TextBlock Name="statusText" Text="{Binding Path=StatusBarText, NotifyOnTargetUpdated=True}">
<TextBlock.Triggers>
<EventTrigger RoutedEvent="Binding.TargetUpdated">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity">
<EasingDoubleKeyFrame KeyTime="0" Value="0"/>
<EasingDoubleKeyFrame KeyTime="0:0:0.25" Value="1"/>
<EasingDoubleKeyFrame KeyTime="0:0:4" Value="1"/>
<EasingDoubleKeyFrame KeyTime="0:0:5" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</TextBlock.Triggers>
Then to create a DependencyProperty called StatusBarText in this case that is implemented as follow:
public string StatusBarText
{
get { return (string)GetValue(StatusBarTextProperty); }
set { SetValue(StatusBarTextProperty, value); }
}
// Using a DependencyProperty as the backing store for StatusBarText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty StatusBarTextProperty =
DependencyProperty.Register("StatusBarText", typeof(string), typeof(MyOwnerClass), new UIPropertyMetadata(""));
Hope this helps.
Your timer is a good approach, and you even identified your problem: you just need a way to access statusText.Text
on the UI thread. (In WPF, threads other than the UI thread are prohibited from accessing UI elements). Here comes the WPF Dispatcher to the rescue:
http://msdn.microsoft.com/en-us/magazine/cc163328.aspx#S5
You can use the DispatcherTimer
class to do exactly what you were attempting (here's their code):
// Create a Timer with a Normal Priority
_timer = new DispatcherTimer();
// Set the Interval to 2 seconds
_timer.Interval = TimeSpan.FromMilliseconds(2000);
// Set the callback to just show the time ticking away
// NOTE: We are using a control so this has to run on
// the UI thread
_timer.Tick += new EventHandler(delegate(object s, EventArgs a)
{
statusText.Text = string.Format(
"Timer Ticked: {0}ms", Environment.TickCount);
});
// Start the timer
_timer.Start();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With