Home
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.

  Listing 1- Window1.xaml   (Show Code ...)

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.
  Listing 2- Window1.xaml.cs   (Show Code ...)

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.

  Balloon Window
Figure 2 Balloon Window
Important <Path> Properties

Data   <Path.Data> Geometry Classes      
Effect     LineGeometry    
Fill     RectangleGeometry    
Opacity     EllipseGeometry     
RenderSize     GeometryGroup     
RenderTransform     CombinedGeometry     
Stroke     PathGeometry     
      StreamGeometry     

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. 
  Listing 3- BalloonWindow.xaml   (Show Code ...)

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>
 
  Listing 4- BalloonWindow.xaml.cs   (Show Code ...)

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();
         }
     }
 }
 
Home