Introduction
Sometimes, many question the use of Silverlight and WPF over Flash technology. One advantage that Silverlight and WPF have is that they are technologies that can receive high-definition video/audio streams to play them. Graphics normally point to still images, as do photographs. Video, however (in TV) uses a frame rate of around 60 frames of an image per second to fool the eye into seeing a motion picture. A television signal is transmitted with the video and audio signals synchronized. That signal is received by the television receiver and stepped (in voltage) by something called the heterodyne principle. The video images and audio are retraced to form a motion picture. So how would we form a TV application using WPF? This article assumes that the reader has a working knowledge of WPF. It presents a WPF application that models a television. The GUI depicts a 3-D-looking environment feature a TV (or an image of) that be turned on to play, pause, and stop the TV's video. When the video plays, a semitransparent reflection plays simultaneously. While this is complex, it is actually a basic WPF GUI application built using controls and modified appearances. In this case, the example demonstrates the use of WPF bitmap effects to apply visual effects to some of the GUI elements. This application, however, is not an attempt to build a Windows Media Player in disguise in the form of a WPF application; it also introduces opacity masks, which can be used to hide parts of an element.
When you download this file, you will watch a video named "future_nasa.wmv". This can be downloaded from here. Add that file to the video folder, and ensure that the video's property settings are correct: Copy to Output: always; File Name: future_nasa.wmv; FilePath, etc. You will notice this title in the markup XAML file:
<Window x:Class="TV.TVWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TV" Height="720" Width="720">
<Window.Resources>
<ControlTemplate x:Key="RadioButtonTemplate"
TargetType="RadioButton">
<Grid>
<Ellipse Width="25" Height="25" Fill="Silver" />
<Ellipse Name="backgroundEllipse" Width="22" Height="22">
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Offset="0" Color="Red"/>
<GradientStop Offset="1.25" Color="Black" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<ContentPresenter Content="{TemplateBinding Content}" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="RadioButton.IsEnabled" Value="False">
<Setter TargetName="backgroundEllipse" Property="Fill">
<Setter.Value>
<RadialGradientBrush>
<GradientStop Offset="0" Color="LightGray" />
<GradientStop Offset="1.25" Color="Black" />
</RadialGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="RadioButton.IsChecked" Value="True">
<Setter TargetName="backgroundEllipse" Property="Fill">
<Setter.Value>
<RadialGradientBrush>
<GradientStop Offset="0" Color="LimeGreen" />
<GradientStop Offset="1.25" Color="Black" />
</RadialGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Window.Resources>
<Canvas>
<Border Canvas.Left="150" Height="370" Width="490"
Canvas.Top="20" Background="DimGray">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border Margin="0,20,0,10" Background="Black"
HorizontalAlignment="Center" VerticalAlignment="Center"
BorderThickness="2" BorderBrush="Silver" CornerRadius="2">
<MediaElement Height="300" Width="400"
Name="videoMediaElement" Source="Video/future_nasa.wmv"
LoadedBehavior="Manual" Stretch="Fill" />
</Border>
<StackPanel Grid.Row="1" HorizontalAlignment="Right"
Orientation="Horizontal">
<RadioButton Name="playRadioButton" IsEnabled="False"
Margin="0,0,5,15" Checked="playRadioButton_Checked"
Template="{StaticResource RadioButtonTemplate}">
<Image Height="20" Width="20"
Source="Images/play.png" Stretch="Uniform" />
</RadioButton>
<RadioButton Name="pauseRadioButton" IsEnabled="False"
Margin="0,0,5,15" Checked="pauseRadioButton_Checked"
Template="{StaticResource RadioButtonTemplate}">
<Image Height="20" Width="20"
Source="Images/pause.png" Stretch="Uniform" />
</RadioButton>
<RadioButton Name="stopRadioButton" IsEnabled="False"
Margin="0,0,15,15" Checked="stopRadioButton_Checked"
Template="{StaticResource
RadioButtonTemplate}">
<Image Height="20" Width="20"
Source="Images/stop.png" Stretch="Uniform" />
</RadioButton>
</StackPanel>
<CheckBox Name="powerCheckBox" Grid.Row="1" Width="25"
Height="25" HorizontalAlignment="Left"
Margin="15,0,0,15" Checked="powerCheckBox_Checked"
Unchecked="powerCheckBox_Unchecked">
<CheckBox.Template>
<ControlTemplate TargetType="CheckBox">
<Grid>
<Ellipse Width="25" Height="25"
Fill="Silver" />
<Ellipse Name="backgroundEllipse" Width="22"
Height="22">
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Offset="0"
Color="LightGray" />
<GradientStop Offset="1.25"
Color="Black" />
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Image Source="Images/power.png" Width="20"
Height="20" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="CheckBox.IsChecked"
Value="True">
<Setter TargetName="backgroundEllipse"
Property="Fill">
<Setter.Value>
<RadialGradientBrush>
<GradientStop Offset="0"
Color="LimeGreen" />
<GradientStop Offset="1.25"
Color="Black" />
</RadialGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</CheckBox.Template>
</CheckBox>
</Grid>
<Border.RenderTransform>
<SkewTransform AngleY="15" />
</Border.RenderTransform>
<Border.BitmapEffect>
<BitmapEffectGroup>
<BevelBitmapEffect BevelWidth="10" />
<DropShadowBitmapEffect Color="Gray" ShadowDepth="15" Softness="1"/>
</BitmapEffectGroup>
</Border.BitmapEffect>
</Border>
<Border Canvas.Left="185" Canvas.Top="410" Height="300"
Width="400">
<Rectangle Name="reflectionRectangle">
<Rectangle.Fill>
<VisualBrush
Visual="{Binding ElementName=videoMediaElement}">
<VisualBrush.RelativeTransform>
<ScaleTransform ScaleY="-1" CenterY="0.5" />
</VisualBrush.RelativeTransform>
</VisualBrush>
</Rectangle.Fill>
<Rectangle.OpacityMask>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="Black" Offset="-0.25" />
<GradientStop Color="Transparent" Offset="0.5" />
</LinearGradientBrush>
</Rectangle.OpacityMask>
</Rectangle>
<Border.RenderTransform>
<SkewTransform AngleY="15" AngleX="-45"/>
</Border.RenderTransform>
</Border>
</Canvas>
</Window>
The control on the lower-left side of the TV turns the TV on, and the arrow control loads the video, in this case, a video of a rocket being launched to the moon. Here is an image of the GUI elements of the TV. The TV buttons in this example are not Button controls. The play, pause, and stop buttons are RadioButton
s, and the power button is a CheckBox
. This should make sense. Those controls have a difference in shape, but perform nearly the same function and usually come in groups. In the background of each button are two circles, defined by Ellipse
objects. The images used are back, play, pause and stop symbols on a transparent background. In its default state, (enabled and unchecked), each playback button glows red. You first hit the Power button on the far left to turn the TV on, (i.e., check box unchecked), the control is gray. When the user clicks that button, the control turns green. You then click the arrow radio button. A reflection is shown, and the video starts, as does the audio.
The functionality for this application comes from the code-behind file. When the use turns on the TV (i.e., checks the powerCheckBox
), the reflection is made visible, and the playback options are enabled. when the user turns off the TV, the MediaElement
's close
method is called to close the media. In addition, the reflection is made invisible and the playback options are disabled. Whenever one of the RadioButton
s that represent each playback option is checked, the MediaElement
executes one of the event handlers, such as play video:
using System.Windows;
namespace TV
{
public partial class TVWindow : Window
{
public TVWindow()
{
InitializeComponent();
}
private void powerCheckBox_Checked( object sender,
RoutedEventArgs e )
{
reflectionRectangle.Visibility = Visibility.Visible;
playRadioButton.IsEnabled = true;
pauseRadioButton.IsEnabled = true;
stopRadioButton.IsEnabled = true;
}
private void powerCheckBox_Unchecked( object sender,
RoutedEventArgs e )
{
videoMediaElement.Close();
reflectionRectangle.Visibility = Visibility.Hidden;
playRadioButton.IsChecked = false;
pauseRadioButton.IsChecked = false;
stopRadioButton.IsChecked = false;
playRadioButton.IsEnabled = false;
pauseRadioButton.IsEnabled = false;
stopRadioButton.IsEnabled = false;
}
private void playRadioButton_Checked( object sender,
RoutedEventArgs e )
{
videoMediaElement.Play();
}
private void pauseRadioButton_Checked( object sender,
RoutedEventArgs e )
{
videoMediaElement.Pause();
}
private void stopRadioButton_Checked( object sender,
RoutedEventArgs e )
{
videoMediaElement.Stop();
}
}
}
Note that this downloads as a deployed application that shows a video of a tech worker maneuvering in zero gravity. The reason that there is solution file to download is that the entire container of files, images, and video is too large to upload. I built these applications using a downloaded and burned ISO image of Visual Studio 2010. There is no reason, however, why they shouldn’t run on Visual Studio 2008, as that platform is compatible with .NET 3.5.
References
- Professional Visual Studio 2008, by Joe Mayo
- The Deitel Associates