One of the long-standing jokes at the office is about using the much hated <Blink> tag in XAML user interfaces. (The other one being about mapping the UI onto the inside of a sphere..)
Disclaimer: This is NOT in ANY WAY suggested for any use WHATSOEVER. We have already seen way too many blinking webpages, thank you very much. And the code is "proof-of-concept" quality..
So basically, I wanted to do a a component that I can use in XAML, making it's content blink. And just for the fun of it, I wanted the on and off durations tunable, and the same goes for the durations of the fade in and fade out between those two extremes.
So here goes nothing...
First a little test case:
<Window x:Class="Blink.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:BlinkTest" Title="Blink" Height="300" Width="450">
<StackPanel Orientation="Vertical">
<!-- Use default blinking settings -->
<src:Blink>
<TextBlock Padding="20" Margin="20" Background="Pink">Hello World</TextBlock>
</src:Blink>
<!-- Explicitely specify blinking settings -->
<src:Blink OnDuration="1" OffDuration="1" FadeInDuration="0" FadeOutDuration="0">
<StackPanel Orientation="Horizontal" Margin="50">
<Label>Some nice text about the button</Label>
<Button>Click Me!</Button>
</StackPanel>
</src:Blink>
</StackPanel>
</Window>
And then the code for the Blink class
The using statements
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;
We are inheriting System.Windows.Controls.Control so we have to do some cruft related to layout
protected override int VisualChildrenCount {
get { return Child != null ? 1 : 0; }
}
protected override Visual GetVisualChild(int index) {
if (index > 0 || Child == null) throw new ArgumentException("index");
return Child;
}
protected override Size MeasureOverride(Size constraint) {
Size sizeDesired = new Size(0, 0);
if (Child != null) {
Child.Measure(constraint);
}
sizeDesired.Width += Child.DesiredSize.Width;
sizeDesired.Height += Child.DesiredSize.Height;
return sizeDesired;
}
protected override Size ArrangeOverride(Size arrangeBounds) {
if (Child != null) {
Rect rect = new Rect(
new Point((arrangeBounds.Width - Child.DesiredSize.Width) / 2,
(arrangeBounds.Height - Child.DesiredSize.Height) / 2),
Child.DesiredSize);
Child.Arrange(rect);
}
return arrangeBounds;
}
And for the animation to work we need a number of dependency properties for the durations
Now, all that is left is just the blinking animation. An obvious choice for
// Using a DependencyProperty as the backing store for OffDuration. This enables animation, styling, binding, etc...
public static readonly DependencyProperty OffDurationProperty;
// Using a DependencyProperty as the backing store for OnDuration. This enables animation, styling, binding, etc...
public static readonly DependencyProperty OnDurationProperty;
// Using a DependencyProperty as the backing store for RiseDuration. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FadeInDurationProperty;
// Using a DependencyProperty as the backing store for DeclineDuration. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FadeOutDurationProperty;
public double OnDuration {
get { return (double)GetValue(OnDurationProperty); }
set { SetValue(OnDurationProperty, value); }
}
public double OffDuration {
get { return (double)GetValue(OffDurationProperty); }
set { SetValue(OffDurationProperty, value); }
}
public double FadeInDuration {
get { return (double)GetValue(FadeInDurationProperty); }
set { SetValue(FadeInDurationProperty, value); }
}
public double FadeOutDuration {
get { return (double)GetValue(FadeOutDurationProperty); }
set { SetValue(FadeOutDurationProperty, value); }
}
static Blink() {
// register dependency properties
OffDurationProperty = DependencyProperty.Register("OffDuration", typeof(double), typeof(Blink), new UIPropertyMetadata(1.0));
OnDurationProperty = DependencyProperty.Register("OnDuration", typeof(double), typeof(Blink), new UIPropertyMetadata(1.0));
FadeInDurationProperty = DependencyProperty.Register("FadeInDuration", typeof(double), typeof(Blink), new UIPropertyMetadata(0.2));
FadeOutDurationProperty = DependencyProperty.Register("FadeOutDuration", typeof(double), typeof(Blink), new UIPropertyMetadata(0.5));
}
// setup the animation
DoubleAnimationUsingKeyFrames animation = new DoubleAnimationUsingKeyFrames();
animation.RepeatBehavior = RepeatBehavior.Forever;
double currentSeconds = 0; // keep track of cumulated time
LinearDoubleKeyFrame start = new LinearDoubleKeyFrame(0,
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0)));
animation.KeyFrames.Add(start);
currentSeconds += FadeInDuration;
LinearDoubleKeyFrame fadeInFrame = new LinearDoubleKeyFrame(1,
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(currentSeconds)));
animation.KeyFrames.Add(fadeInFrame);
currentSeconds += OnDuration;
LinearDoubleKeyFrame onFrame = new LinearDoubleKeyFrame(1,
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(currentSeconds)));
animation.KeyFrames.Add(onFrame);
currentSeconds += FadeOutDuration;
LinearDoubleKeyFrame fadeOutFrame = new LinearDoubleKeyFrame(0,
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(currentSeconds)));
animation.KeyFrames.Add(fadeOutFrame);
currentSeconds += OffDuration;
LinearDoubleKeyFrame offFrame = new LinearDoubleKeyFrame(0,
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(currentSeconds)));
animation.KeyFrames.Add(offFrame);
Child.BeginAnimation(UIElement.OpacityProperty, animation);
And here is the whole Blink.cs in its entirety once again:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;
namespace BlinkTest {
[ContentProperty("Child")]
public class Blink : Control {
UIElement m_child;
public UIElement Child {
get { return m_child; }
set {
if (m_child != null) {
RemoveVisualChild(m_child);
RemoveLogicalChild(m_child);
}
if ((m_child = value) != null) {
AddVisualChild(m_child);
AddLogicalChild(m_child);
// setup the animation
DoubleAnimationUsingKeyFrames animation = new DoubleAnimationUsingKeyFrames();
animation.RepeatBehavior = RepeatBehavior.Forever;
double currentSeconds = 0; // keep track of cumulated time
LinearDoubleKeyFrame start = new LinearDoubleKeyFrame(0,
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0)));
animation.KeyFrames.Add(start);
currentSeconds += FadeInDuration;
LinearDoubleKeyFrame fadeInFrame = new LinearDoubleKeyFrame(1,
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(currentSeconds)));
animation.KeyFrames.Add(fadeInFrame);
currentSeconds += OnDuration;
LinearDoubleKeyFrame onFrame = new LinearDoubleKeyFrame(1,
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(currentSeconds)));
animation.KeyFrames.Add(onFrame);
currentSeconds += FadeOutDuration;
LinearDoubleKeyFrame fadeOutFrame = new LinearDoubleKeyFrame(0,
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(currentSeconds)));
animation.KeyFrames.Add(fadeOutFrame);
currentSeconds += OffDuration;
LinearDoubleKeyFrame offFrame = new LinearDoubleKeyFrame(0,
KeyTime.FromTimeSpan(TimeSpan.FromSeconds(currentSeconds)));
animation.KeyFrames.Add(offFrame);
Child.BeginAnimation(UIElement.OpacityProperty, animation);
}
}
}
protected override int VisualChildrenCount {
get { return Child != null ? 1 : 0; }
}
protected override Visual GetVisualChild(int index) {
if (index > 0 || Child == null) throw new ArgumentException("index");
return Child;
}
protected override Size MeasureOverride(Size constraint) {
Size sizeDesired = new Size(0, 0);
if (Child != null) {
Child.Measure(constraint);
}
sizeDesired.Width += Child.DesiredSize.Width;
sizeDesired.Height += Child.DesiredSize.Height;
return sizeDesired;
}
protected override Size ArrangeOverride(Size arrangeBounds) {
if (Child != null) {
Rect rect = new Rect(
new Point((arrangeBounds.Width - Child.DesiredSize.Width) / 2,
(arrangeBounds.Height - Child.DesiredSize.Height) / 2),
Child.DesiredSize);
Child.Arrange(rect);
}
return arrangeBounds;
}
// Using a DependencyProperty as the backing store for OffDuration. This enables animation, styling, binding, etc...
public static readonly DependencyProperty OffDurationProperty;
// Using a DependencyProperty as the backing store for OnDuration. This enables animation, styling, binding, etc...
public static readonly DependencyProperty OnDurationProperty;
// Using a DependencyProperty as the backing store for RiseDuration. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FadeInDurationProperty;
// Using a DependencyProperty as the backing store for DeclineDuration. This enables animation, styling, binding, etc...
public static readonly DependencyProperty FadeOutDurationProperty;
public double OnDuration {
get { return (double)GetValue(OnDurationProperty); }
set { SetValue(OnDurationProperty, value); }
}
public double OffDuration {
get { return (double)GetValue(OffDurationProperty); }
set { SetValue(OffDurationProperty, value); }
}
public double FadeInDuration {
get { return (double)GetValue(FadeInDurationProperty); }
set { SetValue(FadeInDurationProperty, value); }
}
public double FadeOutDuration {
get { return (double)GetValue(FadeOutDurationProperty); }
set { SetValue(FadeOutDurationProperty, value); }
}
static Blink() {
// register dependency properties
OffDurationProperty = DependencyProperty.Register("OffDuration", typeof(double), typeof(Blink), new UIPropertyMetadata(1.0));
OnDurationProperty = DependencyProperty.Register("OnDuration", typeof(double), typeof(Blink), new UIPropertyMetadata(1.0));
FadeInDurationProperty = DependencyProperty.Register("FadeInDuration", typeof(double), typeof(Blink), new UIPropertyMetadata(0.2));
FadeOutDurationProperty = DependencyProperty.Register("FadeOutDuration", typeof(double), typeof(Blink), new UIPropertyMetadata(0.5));
}
}
}
The code was developed on Vista using the RC1 bits of the .NET 3.0 SDK
No comments:
Post a Comment