WPF Quick & Easy Timer


This is a WPF app that counts down the hours, minutes, and seconds from a starting point that you specify. When time is up, an optional bell sounds, and a simple message box appears. There is a pause button so you can pause the clock. There is also a Reset button. You can simply use it to keep track of time so that you take a break from programming every hour, for example.

Below is how the interface appears when you first start it. The arrow pointing to the right starts the timer, and the pause button pauses the timer.

When the user clicks on most of the surfaces of the app and moves the mouse with the mouse button still down, the opacity gets set to 1.0 (no longer partly transparent), as seen below.

Here is the XAML code.

<Window x:Class="GedgetAlarmOneHour.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        xmlns:clr="clr-namespace:System;assembly=mscorlib"
        mc:Ignorable="d"
        AllowsTransparency="True" WindowStyle="None" Background="Transparent"
        WindowStartupLocation="CenterScreen"
        MouseLeftButtonDown="Window_MouseLeftButtonDown"
        MouseLeftButtonUp="Window_MouseLeftButtonUp"
        Title="GedgetAlarmOneHour" Height="161.067" Width="220"
        Loaded="Window_Loaded"
        Topmost="True">
    <Window.Resources>
        <clr:Double x:Key="MyOpacity">0.5</clr:Double>
    </Window.Resources>
    <Grid>
        <Rectangle Name="rectAlarm" Fill="LightBlue" Opacity="0.1" Margin="2,2,2,10" RadiusX="8" RadiusY="8">
            <Rectangle.Effect>
                <DropShadowEffect/>
            </Rectangle.Effect>
        </Rectangle>
        <TextBlock Margin="55,10,54,132" FontFamily="Parsons" FontWeight="Bold" Opacity="{StaticResource MyOpacity}" Text="Quick &amp; Easy Timer"></TextBlock>
        <Button Name="btnAbout" Margin="10,2,194,139.8" FontWeight="Bold" Foreground="Blue" Click="BtnAbout_Click"
                Opacity="{StaticResource MyOpacity}" Background="Transparent" BorderThickness="0">?</Button>
        <Button Name="btnClose" Margin="203,2,2,139.8" FontWeight="Bold" Foreground="Red" Click="BtnClose_Click"
                Opacity="{StaticResource MyOpacity}" Background="Transparent" BorderThickness="0">X
        </Button>
        <TextBlock Margin="24,99,160,41.8" RenderTransformOrigin="2.831,0.567" Opacity="{StaticResource MyOpacity}">Hours</TextBlock>
        <TextBlock x:Name="txtblkUp" FontSize="16" Margin="70,124,63,15.8" Opacity="{StaticResource MyOpacity}" 
                   RenderTransformOrigin="0.293,1.158">00:00:00:00</TextBlock>
        <TextBlock Margin="114,99,63,41.8" RenderTransformOrigin="2.831,0.567" Opacity="{StaticResource MyOpacity}">Minutes</TextBlock>
        <TextBlock x:Name="txtblkDown" FontSize="24" Margin="47,29,26,104.8" Opacity="{StaticResource MyOpacity}" 
                   RenderTransformOrigin="0.293,1.158">00:00:00:00</TextBlock>
        <Button Name="btnStart" Margin="27,66,170,71.8" Opacity="{StaticResource MyOpacity}" FontSize="14" Click="BtnStart_Click">&#9654;</Button>
        <Button Name="btnPause" Margin="61,66,134,71.8" Opacity="{StaticResource MyOpacity}" FontSize="14" Click="BtnPause_Click">&#9614; &#9614;</Button>
        <Button Name="btnReset" Margin="124,66,57,71.8" Opacity="{StaticResource MyOpacity}" Click="BtnReset_Click">Reset</Button>
        <ToggleButton Name="tgbtn" Margin="171,66,20,71.8" Opacity="{StaticResource MyOpacity}" FontSize="8"
                      Checked="Tgbtn_Checked" Unchecked="Tgbtn_Unchecked">
            <Image x:Name="imgSound" Source="./Images/SndOn.png"></Image>
        </ToggleButton>
        <!--#region comboboxes-->
        <ComboBox Name="cboBoxHours" Margin="61,96,119,41.8" Opacity="{StaticResource MyOpacity}" SelectedIndex="0" SelectionChanged="CboBoxHours_SelectionChanged">
            <ComboBoxItem>0</ComboBoxItem>
            <ComboBoxItem IsSelected="True">1</ComboBoxItem>
            <ComboBoxItem>2</ComboBoxItem>
            <ComboBoxItem>3</ComboBoxItem>
            <ComboBoxItem>4</ComboBoxItem>
            <ComboBoxItem>5</ComboBoxItem>
            <ComboBoxItem>6</ComboBoxItem>
            <ComboBoxItem>7</ComboBoxItem>
            <ComboBoxItem>8</ComboBoxItem>
            <ComboBoxItem>9</ComboBoxItem>
            <ComboBoxItem>10</ComboBoxItem>
            <ComboBoxItem>11</ComboBoxItem>
            <ComboBoxItem>12</ComboBoxItem>
            <ComboBoxItem>13</ComboBoxItem>
            <ComboBoxItem>14</ComboBoxItem>
            <ComboBoxItem>15</ComboBoxItem>
            <ComboBoxItem>16</ComboBoxItem>
            <ComboBoxItem>17</ComboBoxItem>
            <ComboBoxItem>18</ComboBoxItem>
            <ComboBoxItem>19</ComboBoxItem>
            <ComboBoxItem>20</ComboBoxItem>
            <ComboBoxItem>21</ComboBoxItem>
            <ComboBoxItem>22</ComboBoxItem>
            <ComboBoxItem>23</ComboBoxItem>
            <ComboBoxItem>24</ComboBoxItem>
        </ComboBox>
        <ComboBox Name="cboBoxMinutes" Margin="162,96,20,41.8" Opacity="{StaticResource MyOpacity}" SelectedIndex="1" SelectionChanged="CboBoxMinutes_SelectionChanged">
            <ComboBoxItem IsSelected="True">0</ComboBoxItem>
            <ComboBoxItem>1</ComboBoxItem>
            <ComboBoxItem>2</ComboBoxItem>
            <ComboBoxItem>3</ComboBoxItem>
            <ComboBoxItem>4</ComboBoxItem>
            <ComboBoxItem>5</ComboBoxItem>
            <ComboBoxItem>6</ComboBoxItem>
            <ComboBoxItem>7</ComboBoxItem>
            <ComboBoxItem>8</ComboBoxItem>
            <ComboBoxItem>9</ComboBoxItem>
            <ComboBoxItem>10</ComboBoxItem>
            <ComboBoxItem>11</ComboBoxItem>
            <ComboBoxItem>12</ComboBoxItem>
            <ComboBoxItem>13</ComboBoxItem>
            <ComboBoxItem>14</ComboBoxItem>
            <ComboBoxItem>15</ComboBoxItem>
            <ComboBoxItem>16</ComboBoxItem>
            <ComboBoxItem>17</ComboBoxItem>
            <ComboBoxItem>18</ComboBoxItem>
            <ComboBoxItem>19</ComboBoxItem>
            <ComboBoxItem>20</ComboBoxItem>
            <ComboBoxItem>21</ComboBoxItem>
            <ComboBoxItem>22</ComboBoxItem>
            <ComboBoxItem>23</ComboBoxItem>
            <ComboBoxItem>24</ComboBoxItem>
            <ComboBoxItem>25</ComboBoxItem>
            <ComboBoxItem>26</ComboBoxItem>
            <ComboBoxItem>27</ComboBoxItem>
            <ComboBoxItem>28</ComboBoxItem>
            <ComboBoxItem>29</ComboBoxItem>
            <ComboBoxItem>30</ComboBoxItem>
            <ComboBoxItem>31</ComboBoxItem>
            <ComboBoxItem>32</ComboBoxItem>
            <ComboBoxItem>33</ComboBoxItem>
            <ComboBoxItem>34</ComboBoxItem>
            <ComboBoxItem>35</ComboBoxItem>
            <ComboBoxItem>36</ComboBoxItem>
            <ComboBoxItem>37</ComboBoxItem>
            <ComboBoxItem>38</ComboBoxItem>
            <ComboBoxItem>39</ComboBoxItem>
            <ComboBoxItem>40</ComboBoxItem>
            <ComboBoxItem>41</ComboBoxItem>
            <ComboBoxItem>42</ComboBoxItem>
            <ComboBoxItem>43</ComboBoxItem>
            <ComboBoxItem>44</ComboBoxItem>
            <ComboBoxItem>45</ComboBoxItem>
            <ComboBoxItem>46</ComboBoxItem>
            <ComboBoxItem>47</ComboBoxItem>
            <ComboBoxItem>48</ComboBoxItem>
            <ComboBoxItem>49</ComboBoxItem>
            <ComboBoxItem>50</ComboBoxItem>
            <ComboBoxItem>51</ComboBoxItem>
            <ComboBoxItem>52</ComboBoxItem>
            <ComboBoxItem>53</ComboBoxItem>
            <ComboBoxItem>54</ComboBoxItem>
            <ComboBoxItem>55</ComboBoxItem>
            <ComboBoxItem>56</ComboBoxItem>
            <ComboBoxItem>57</ComboBoxItem>
            <ComboBoxItem>58</ComboBoxItem>
            <ComboBoxItem>59</ComboBoxItem>
        </ComboBox>
        <!--#endregion comboboxes-->
    </Grid>
</Window>

Below is the code behind.

using System;
using System.Diagnostics;
using System.Media;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using System.Windows.Controls;
using System.Windows.Media.Imaging;

namespace GedgetAlarmOneHour
{
    public partial class MainWindow : Window
    {
        DispatcherTimer dt = new DispatcherTimer();
        Stopwatch sw = new Stopwatch();
        string currentTime = string.Empty;
        int minutes = 0; // counting down from minutes
        int hours = 1;
        TimeSpan cd = new TimeSpan(0, 0, 0, 0, 0); // cd is countdown
        bool almSilent = false; // by default, sound is on.

        public MainWindow()
        {
            InitializeComponent();
            dt.Tick += new EventHandler(dt_Tick);
            dt.Interval = new TimeSpan(0, 0, 0, 0, 0);
        }
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            hours = 1;
            minutes = 0;
            txtblkDown.Text = String.Format("{0:00}:{1:00}:{2:00}:{3:00}", hours, minutes, 0, 0);
        }
        void dt_Tick(object sender, EventArgs e)
        {
            if (sw.IsRunning)
            {
                TimeSpan ts = sw.Elapsed;
                //cd = new TimeSpan(0, minutes, 0, 0, 0); // cd is countdown
                TimeSpan tl = cd - ts;  // tl is time left
                currentTime = String.Format("{0:00}:{1:00}:{2:00}:{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10);
                txtblkUp.Text = currentTime;
                txtblkDown.Text = String.Format("{0:00}:{1:00}:{2:00}:{3:00}", tl.Hours, tl.Minutes, tl.Seconds, tl.Milliseconds / 10);

                if (tl.Hours == 0 && tl.Minutes == 0 && tl.Seconds == 0 && tl.Milliseconds <= 0 && tl.Milliseconds > -100)
                {
                    SoundPlayer p = new SoundPlayer("./Sounds/emergency_bell_alarm_small_ring.wav");
                    if (!almSilent)
                    {
                        p.Play();
                    }
                    MessageBox.Show("Times Up! ", "One Hour Gadget Alarm", MessageBoxButton.OK);
                    p.Stop();
                    txtblkDown.Visibility = Visibility.Collapsed;
                }
            }
        }
        void Window_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            rectAlarm.Opacity = 1;
            txtblkDown.Opacity = 1;
            txtblkUp.Opacity = 1;
            this.DragMove();
        }
        void Window_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            rectAlarm.Opacity = 0.1;
            txtblkDown.Opacity = 0.4;
            txtblkUp.Opacity = 0.4;
        }
        private void CboBoxHours_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
                        if (e.AddedItems.Count > 0)
            {
                string text = (e.AddedItems[0] as ComboBoxItem).Content as string;
                if (text != null)
                {
                    hours = Convert.ToInt32(text);
                }
                string str = "";
                str = hours.ToString("D2") + ":" + minutes.ToString("D2") + ":00:00";
                txtblkDown.Text = str;
            }
        }
        private void CboBoxMinutes_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (e.AddedItems.Count > 0)
            {
                string text = (e.AddedItems[0] as ComboBoxItem).Content as string;
                if (text != null)
                {
                    minutes = Convert.ToInt32(text);
                }
                string str = "";
                str = hours.ToString("D2") + ":" + minutes.ToString("D2") + ":00:00";
                txtblkDown.Text = str;
            }
        }
        private void BtnAbout_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("NYI - About info");
        }
        private void BtnClose_Click(object sender, RoutedEventArgs e)
        {
            Close();
        }
        private void BtnStart_Click(object sender, RoutedEventArgs e)
        {
            cd = new TimeSpan(0, hours, minutes, 0, 0); // cd is countdown
            sw.Start();
            dt.Start();
        }

        private void BtnPause_Click(object sender, RoutedEventArgs e)
        {
            if (sw.IsRunning)
            {
                sw.Stop();
            }
        }
        private void BtnReset_Click(object sender, RoutedEventArgs e)
        {
            sw.Reset();
            txtblkUp.Text = "00:00:00:00";
            string str = "";
            str = hours.ToString("D2") + ":" + minutes.ToString("D2") + ":00:00";
            txtblkDown.Text = str;
            txtblkDown.Visibility = Visibility.Visible; 
        }
        private void Tgbtn_Checked(object sender, RoutedEventArgs e)
        {
            // Clicking a toggle button for the first time makes it checked meaning it's default
            // is not checked. The default here is Sound On, which makes the first time
            // its clicked mean that Silence is true and icon is SndOff.png.
            almSilent = true;
            Uri fileUri = new Uri("./Images/SndOff.png", UriKind.Relative);
            imgSound.Source = new BitmapImage(fileUri);
        }
        private void Tgbtn_Unchecked(object sender, RoutedEventArgs e)
        {
            almSilent = false;
            Uri fileUri = new Uri("./Images/SndOn.png", UriKind.Relative);
            imgSound.Source = new BitmapImage(fileUri);
        }
    }
}