Click here to Skip to main content
15,885,914 members
Articles / Web Development / HTML

Binary Clock in C# and WPF

Rate me:
Please Sign up or sign in to vote.
4.93/5 (16 votes)
28 Feb 2016CPOL5 min read 16.5K   464   16   1
In this article, we will create a simple binary clock using C# and WPF. The project itself will serve to show some peculiarities, like the use of Tasks, how to manipulate the UI of a WPF page, and basic data conversions.

Forewords

In this article, we will create a simple binary clock using C# and WPF. The project itself will serve to show some peculiarities, like the use of Tasks, how to manipulate the UI of a WPF page, and basic data conversions.

Introduction

A binary clock is a clock which displays the current time in a binary format. In the following example, we will create a set of graphical leds, each of which will represent a binary digit. Each led could be set in two statuses: on (which represent the value of 1) or off (which represent the value of zero). From right to left, our leds will represents the values of 1, 2, 4, 8, 16, 32, because we will base our conversion on 24h formatted time, and we need a number of digits that can represent up to the decimal value of 60 (for minutes and seconds).

Image 1

Each part of the current time (hour, minutes, seconds) will have its own row of six leds, to represent the binary conversion of the decimal value. For example, if we wish to display a time like 10:33:42, our leds must be illuminated according to the following pattern:

Image 2

A Binary Clock in XAML

The XAML rendering of the concept above is fairly simple. In a new XAML page, we need to create three rows of rectangles, which border radius will be set to 50 to give them a circular shape. Other settings will refer to the filling color, shape shadows, and so on, to draw a led the way we desire. In our example, the led will be shadowed and colored according to the following XAML code:

XML
<Rectangle HorizontalAlignment="Left" Height="35" Margin="211,40,0,0" 
	Stroke="#FF033805" VerticalAlignment="Top" 
	Width="38" RadiusX="50" RadiusY="50">
      <Rectangle.Effect>
          <DropShadowEffect BlurRadius="10" ShadowDepth="10"/>
      </Rectangle.Effect>
      <Rectangle.Fill>
          <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
              <GradientStop Color="#FFFFFF1B" Offset="0"/>
              <GradientStop Color="#FF29B413" Offset="0.568"/>
          </LinearGradientBrush>
      </Rectangle.Fill>
</Rectangle>

Once finished creating each row for our UI, and having embellished everything, the XAML page will look like this:

Image 3

You could refer to the download section, available at the end of the article for a complete reference of the snippet above.

Source Code

The following section explains how our rectangles could be controlled to show a binary representation of the current time.

In our XAML Window, we've declared an Event calling - more precisely, an event that must be fired on the Page loading (Loaded Event). In the code-behind of the Window_Loaded routine, we execute two main operations: the first is merely graphical, and consists in setting each rectangle opacity to 0.35, in order to give the impression of a switched-off led. The second one is the execution of the task which will execute calculations and update the UI. More on that later. First, let's see how we can identify a control declared on a XAML page.

Let's take a look at the loop to set all rectangle's opacity to 0.35:

C#
// Sets all rectangles opacity to 0.35
foreach (var r in LogicalTreeHelper.GetChildren(MainGrid))
{
  if (r is Rectangle) (r as Rectangle).Fill.Opacity = 0.35;
}

Speaking about identifying controls, the main difference between WinForms and WPF is that we can't refer to a container's controls by using the property Controls(). The WPF-way of doing that kind of operation passes through the LogicalTreeHelper class. Through it, we can call upon the method GetChildren, indicating the name of the main control for which retrieve children controls. In our case, we've executed LogicalTreeHelper.GetChildren on the control MainGrid (the name which identifies the Grid object of our XAML Page). Then, while traversing the controls array, we check if that particular control is a Rectangle and - if so - we'll proceed in setting its Opacity to the desired value.

The second set of instructions from the Window_Loaded event is the execution of a secondary task for calculating the binary representation of each time part, and updating the UI as well. The code is as follows:

C#
Task.Factory.StartNew(() =>
{
    // while the thread is running...
    while (true)
    {
        // ...get the current system time
        DateTime _now = System.DateTime.Now;

        // Convert each part of the system time (i.e.: hour, minutes, seconds) to 
        // binary, filling with 0s up to a length of 6 char each
        String _binHour = Convert.ToString(_now.Hour, 2).PadLeft(6, '0');
        String _binMinute = Convert.ToString(_now.Minute, 2).PadLeft(6, '0');
        String _binSeconds = Convert.ToString(_now.Second, 2).PadLeft(6, '0');

        // For each digit of the binary hour representation
        for (int i = 0; i <= _binHour.Length - 1; i++)
        {
          // Dispatcher invoke to refresh the UI, which belongs to the main thread
          H0.Dispatcher.Invoke(() =>
          {
             // Update the contents of the labels which use decimal h/m/s representation
             lbHour.Content = _now.Hour.ToString("00");
             lbMinute.Content = _now.Minute.ToString("00");
             lbSeconds.Content = _now.Second.ToString("00");

             // Search for a rectangle which name corresponds to the _binHour current char index.
             // Then, set its opacity to 1 if the current _binHour digit is 1, or to 0.35 otherwise
             (MainGrid.FindName("H" + i.ToString()) as Rectangle).Fill.Opacity = 
             _binHour.Substring(i, 1).CompareTo("1") == 0 ? 1 : 0.35;
             (MainGrid.FindName("M" + i.ToString()) as Rectangle).Fill.Opacity = 
             _binMinute.Substring(i, 1).CompareTo("1") == 0 ? 1 : 0.35;
             (MainGrid.FindName("S" + i.ToString()) as Rectangle).Fill.Opacity = 
             _binSeconds.Substring(i, 1).CompareTo("1") == 0 ? 1 : 0.35;
          });
        }
    }
});

Pretty self-explanatory, the task consists in a neverending loop, which continuously retrieves the current system time. Then, it separates it in its three main parts (hours, minutes, seconds) and proceeds in converting them to their binary representation, through the use of the Convert.ToString() function, to which we'll pass the numeric base for conversion (in our case, 2). Since we need three strings of length equal to six (we have six leds for each row), we need to pad each string up to six characters. So, if for example, we are converting the value of 5, the function will produce 101 as output - a value we will pad to 000101.

A second loop which works up to the length of the binary string related to hours (a value that will be ever six), will the provide the UI update, using the Dispatcher property to Invoke the update method of an object which runs on another thread (please refer to «VB.NET: Invoke Method to update UI from secondary threads» for further details on Invoke method). For each digit contained in our strings, we need to identify the correct Rectangle, to update its Opacity value.

We can accomplish this kind of task through the FindName() function: given a parent object (MainGrid, in our case), FindName will proceed in referring an UI control, if the passed parameter corresponds to an existent control name. Since we've named the Rectangles for each time part with a progressive number (0 to 5, beginning with H for hours, M for minutes, S for seconds), we can ask the function to retrieve the control whose name starts with a particular letter, and continues with an index equal to the current binary string index.

Let's see one of those lines for the sake of clarity: with the following line:

C#
(MainGrid.FindName("H" + i.ToString()) as Rectangle).Fill.Opacity = 
		_binHour.Substring(i, 1).CompareTo("1") == 0 ? 1 : 0.35;

we are asking: retrieve from MainGrid a control whose name is equal to "H" + the current loop index. Consider it as a Rectangle, then set its Opacity according to the following rule: if the indexed character from the binary string is 1, then the Opacity must be 1, otherwise it must be set to 0.35. Executing the program will result in what can be seen in the following video.

Demonstrative Video

Download

The complete source code for the article sample can be downloaded at https://code.msdn.microsoft.com/Binary-Clock-in-C-and-WPF-f954c9a5.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Italy Italy
Working in IT since 2003 as Software Developer for Essetre Srl, a company in Northern Italy.
I was awarded in 2014, 2015 and 2016 with Microsoft MVP, for Visual Studio and Development Technologies expertise. My technology interests and main skills are in .NET Framework, Visual Basic, Visual C# and SQL Server, but i'm proficient in PHP and MySQL also.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Evgeny Bestfator9-Jan-17 0:11
professionalEvgeny Bestfator9-Jan-17 0:11 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.