WPF Application 1
|
We present a pair of WPF examples.
The first example converts Arabic numerals to Roman numerals and vice-versa as
shown in Figure 1.
|
Figure 1 - Arabic / Roman Numeral Converter
|
This consists of two windows, one with a rectangular frame and one which is balloon shaped. Let's walk through the rectangular frame first..
Listing 1 shows the XAML declarative code for this window.
|
|
Window1 XAML Listing
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
| <Window x:Class="FrmWpfMain.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Loaded="OnLoaded" Title="Window1" Height="316" Width="471" Background="Transparent" Unloaded="OnUnloaded"> <Window.Resources> <Style x:Key="RoundGelButton" TargetType="Button"> <Setter Property="Background" Value="Black" /> <Setter Property="Foreground" Value="White" /> <Setter Property="Width" Value="100" /> <Setter Property="Height" Value="100" /> <Setter Property="Grid.Row" Value="2" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type Button}"> <Grid> <Ellipse Name="GelBackground" StrokeThickness="0.5" Fill="Black"> <Ellipse.Stroke> <LinearGradientBrush StartPoint="0,0" EndPoint="0,1"> <GradientStop Offset="0" Color="#ff7e7e7e" /> <GradientStop Offset="1" Color="Black" /> </LinearGradientBrush> </Ellipse.Stroke> </Ellipse> <Ellipse Margin="15,5,15,50"> <Ellipse.Fill> <LinearGradientBrush StartPoint="0,0" EndPoint="0,1"> <GradientStop Offset="0" Color="#aaffffff" /> <GradientStop Offset="1" Color="Transparent" /> </LinearGradientBrush> </Ellipse.Fill> </Ellipse> <ContentPresenter Name="GelButtonContent" VerticalAlignment="Center" HorizontalAlignment="Center" Content="{TemplateBinding Content}" /> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Rectangle.Fill" TargetName="GelBackground"> <Setter.Value> <RadialGradientBrush> <GradientStop Offset="0" Color="Lime" /> <GradientStop Offset="1" Color="DarkGreen" /> </RadialGradientBrush> </Setter.Value> </Setter> <Setter Property="Foreground" Value="Black" /> </Trigger> <Trigger Property="IsPressed" Value="True"> <Setter Property="Rectangle.Fill" TargetName="GelBackground"> <Setter.Value> <RadialGradientBrush> <GradientStop Offset="0" Color="#ffcc00" /> <GradientStop Offset="1" Color="#cc9900" /> </RadialGradientBrush> </Setter.Value> </Setter> <Setter Property="Foreground" Value="Black" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> </Window.Resources> <Grid Name="OuterGrid" ShowGridLines="False" Height="261" Width="453"> <Grid.RowDefinitions> <RowDefinition Height="118*"></RowDefinition> <RowDefinition Height="118*"></RowDefinition> </Grid.RowDefinitions> <Grid Name="InnerGrid" Margin="11,40,17,22" > <Grid.ColumnDefinitions> <ColumnDefinition Width="104*"></ColumnDefinition> <ColumnDefinition Width="143*"></ColumnDefinition> </Grid.ColumnDefinitions> <TextBlock TextWrapping="Wrap" Margin="0,0,0,-16">Enter Roman or Arabic numeral. Ctrl + Roman numeral multiplies value by 1000 and inserts macron over character. </TextBlock> <TextBox Name="tbNumeral" Grid.Column="1" CharacterCasing="Upper" KeyDown="tbNumeral_KeyDown" Height="23" VerticalAlignment="Top"></TextBox> </Grid> <Button Grid.Row="2" Margin="10" Style="{StaticResource RoundGelButton}" Click="Button_Click" Name="BigRoundButton">Convert</Button> </Grid> </Window> |
|
|
Listing 2 shows the C# code-behind for Window1.
|
|
Window1 C# Listing
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
| using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Windows.Interop; namespace FrmWpfMain { /// <summary> /// Interaction logic for Window1.xaml /// </summary> public partial class Window1 : Window { BalloonWindow bw; public Window1() { InitializeComponent(); this.Title = "Arabic / Roman Numeral Converter"; } private void Button_Click(object sender, RoutedEventArgs e) { string txt = tbNumeral.Text; A2RConverter converter = new A2RConverter(); string result = converter.Convert(txt); if (bw == null) { bw = new BalloonWindow(); bw.Show(); } else bw.Show(); bw.Top = this.Top + 150; bw.Left = this.Left - 100; if (txt != "") bw.BalloonMsg.Text = result; else bw.BalloonMsg.Text = "Type a Roman numeral or Arabic number into the box above."; } private void OnLoaded(object sender, RoutedEventArgs e) { try { VistaGlassHelper.ExtendGlass(this, -1, -1, -1, -1); } // If not Vista, paint background white. catch //(DllNotFoundException) { this.Background = Brushes.White; } } private void OnUnloaded(object sender, RoutedEventArgs e) { bw.Close(); this.Close(); Application.Current.Shutdown(0); } private void tbNumeral_KeyDown(object sender, KeyEventArgs e) { return; // FIX string key = e.Key.ToString(); string next = ""; switch (key) { case "M": tbNumeral.Text = A2RConverter.million; break; case "D": tbNumeral.Text += A2RConverter.fivehundredthou; break; case "C": tbNumeral.Text += A2RConverter.hundredthou; break; case "L": tbNumeral.Text += A2RConverter.fiftythou; break; case "X": tbNumeral.Text += A2RConverter.tenthou; break; case "V": tbNumeral.Text += A2RConverter.fivethou; break; default: break; } } } } |
|
|
In the XAML listing, first we go through some effort to create a round "gel"
button -- appropriately named RoundGelButton -- using a Style dependency
property beginning at line 8 inside the top container window resources.
The style template contains a ControlTemplate which targets the control type
Button. The template for the button has a Grid container and inside the
grid are three objects: an outer Ellipse named GelBackground (line 19) and
smaller ellipse (line 29) and a ContentPresenter (line 38) which is bound to the
button's Content property, that is, its text consisting of the word "Convert".
The gel effect is produced primarily by the smaller ellipse in the top half of
the button which is painted by a linear gradient brush (lines 31-34) that varies
from a semi-transparent white (or more properly, translucent white) to black
over the solid black background of the outer ellipse (line 20). The outer
ellipse has a stroke around its circumference. The stroke can be viewed by
changing the Fill value to Transparent in line 20.
The control template Triggers collection is set to respond to the mouse-over
(line 45) and mouse-left-button-pressed (line 58) events. Both change the
Fill property of the outer ellipse (the property Rectangle.Fill in lines 46 and
59) to radial gradient brushes. For mouse-over, the gradient varies from
Lime at the center of the button to DarkGreen along the circumference. For
mouse-left-button-pressed, the gradient varies from yellow orange at the center
to dark orange along the circumference. For both events, the smaller
ellipse remains in view contributing to the "shininess" of the button.
A Grid (line 81) named OuterGrid is the only object at the Window base class
level. The grid has two rows (lines 84, 85). Inside OuterGrid
is a second grid (line 89) named InnerGrid with two columns (lines 91, 93).
The column has a TextBlock object with the text "Enter Roman or Arabic
numeral..." and the second column has a TextBox named tbNumeral and a KeyDown
event handler named tbNumeral_KeyDown defined in the C# code-behind.
The second row of OuterGrid has the button named BigRoundButton which accesses
(line 106) the StaticReource RoundGelButton (lines 8-75) which was discussed
earlier. The button's Click event is routed to Button_Click in the C#
code-behind.
The code-behind is displayed in Listing 2. A reference bw to
class BalloonWindow
described later is created on line 23. In the Button_Click handler,
if this reference is null, then a new BalloonWindow is created.
Otherwise, an instance is created and positioned with respect to Window1 in
lines 47 and 48.
Now let's have a look at the code which creates the non-rectangular
BalloonWindow, shown in Figure 2.
|
Figure 2 Balloon Window
|
|
|
Listing 3 shows the XAML for this object. and isting 4 shows the C# code-behind.
Properties of the Window, including a title which is never displayed, are in
lines 5-10. The rectangular window frame is suppressed by making its background
transparent and specifying "None" for WindowStyle which makes the window
borderless. The only object in the window is a Grid container.
The
non-rectilinear shape of the balloon is drawn with a Path object which has an
outline with fixed color, Stroke="DarkGreay" (line 15). (See the
sidebar above for some of the important properties of Path.) The interior of the
closed path is filled with a LinearGradientBrush (lines 17-26). The balloon
shape
is established in the PathFigure described in Lines 31-43. The path begins
at coordinates 20,0 (that is, the upper left corner of the balloon), is closed,
meaning that the last segment in the path connects to the path start even if
this does not occur in the path description (line 31), and is followed by
LineSegments and ArcSegments. The triangular segment on the underside of
the balloon occurs at lines 37 and 38.
|
|
BalloonWindow XAML Listing
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| <Window x:Class="FrmWpfMain.BalloonWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Balloon Window" Width="210" Height="170" WindowStyle="None" AllowsTransparency="True" Background="Transparent" Closing="BalloonWindow_Closing" > <Grid> <!--Non-Rectangular window edge, create with paths--> <Path Stroke="DarkGray" StrokeThickness="1" SnapsToDevicePixels="True"> <Path.Fill> <LinearGradientBrush StartPoint="0.2,0" EndPoint="0.8,1" > <LinearGradientBrush.GradientStops> <GradientStop Color="White" Offset="0"></GradientStop> <GradientStop Color="White" Offset="0.45"></GradientStop> <GradientStop Color="LightBlue" Offset="0.9"></GradientStop> <GradientStop Color="Gray" Offset="1"></GradientStop> </LinearGradientBrush.GradientStops> </LinearGradientBrush> </Path.Fill> <Path.Data> <PathGeometry> <PathFigure StartPoint="20,0" IsClosed="True"> <LineSegment Point="140,0"></LineSegment> <ArcSegment Point="160,20" Size="20,20" SweepDirection="Clockwise"></ArcSegment> <LineSegment Point="160,60"></LineSegment> <ArcSegment Point="140,80" Size="20,20" SweepDirection="Clockwise"></ArcSegment> <LineSegment Point="70,80"></LineSegment> <LineSegment Point="70,130"></LineSegment> <LineSegment Point="40,80"></LineSegment> <LineSegment Point="20,80"></LineSegment> <ArcSegment Point="0,60" Size="20,20" SweepDirection="Clockwise"></ArcSegment> <LineSegment Point="0,20"></LineSegment> <ArcSegment Point="20,0" Size="20,20" SweepDirection="Clockwise"></ArcSegment> </PathFigure> </PathGeometry> </Path.Data> <Path.RenderTransform> <ScaleTransform ScaleX="1.3" ScaleY="1.3"></ScaleTransform> </Path.RenderTransform> </Path> <StackPanel Margin="5"> <!--Close button--> <Button HorizontalAlignment="Right" Click="cmdClose_Click" Margin="0,5,10,0"> x </Button> <TextBlock Name ="BalloonMsg" TextWrapping="Wrap" MouseLeftButtonDown="window_MouseLeftButtonDown" FontSize="15" HorizontalAlignment="Center"></TextBlock> </StackPanel> </Grid> </Window> |
|
|
|
BalloonWindow C# Listing
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; namespace FrmWpfMain { /// <summary> /// Interaction logic for BalloonWindow.xaml /// </summary> public partial class BalloonWindow : Window { public BalloonWindow() { InitializeComponent(); } private void window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { this.DragMove(); } private void cmdClose_Click(object sender, RoutedEventArgs e) { this.Close(); } delegate void HideMe(); private void BalloonWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e) { e.Cancel = true; Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, new HideMe(HideMyWindow)); } public void HideMyWindow() { this.Hide(); } } } |
|
|
|
|