Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / X11

Writing a XAML 7 segment LCD display UserControl for X11 and Windows

5.00/5 (4 votes)
30 Sep 2015CPOL7 min read 21.3K   853  
Currently none of the big Linux/Unix (X11) GUI application frameworks (GTK+, KDE) support XAML based application development. The Moonlight project (including XAML support) was abandoned on May 29, 2012. This article introduces a XAML based 7 segment LCD display utilizing a WPF UserControl.

Image 1 Download XamlSevenSegment_X11_32.zip Mono solution including full source and executable
Image 2 Download XamlSevenSegment_X11_64.zip Mono solution including full source and executable
Image 3 Download XamlSevenSegment_Win8.zip Visual Studio 2013 solution including full source and executable

Introduction

This article is a case study, how to write a MVVM (Model View ViewModel) design pattern based X11/Windows (cross platform) 7 segment LCD display (utilizing WPF UserControls) with XAML using the Roma Widget Set (Xrw). The Roma Widget Set is a zero dependency GUI application framework for X11 (it requires only assemblies of the free Mono standard installation and libraries of the free X11 distribution; it doesn't particularly require GNOME, KDE, cairo, pango or commercial libraries) and is implemented entirely in C#.

This article continues the works Writing a XAML dialog application for X11, Writing a XAML ribbon application for X11, Writing a XAML application for X11 with massive data binding and zero code, Writing a XAML calculator application for X11, Writing a XAML application with geometry objects (shapes) for X11 and Writing a XAML application for X11 with UserControls and . As far as i know, this (utilizing the Xrw) is the first attempt to use XAML for X11 application development after the abandonment of Moonlight.

Neither the Roma Widget Set nor the XAML implementation are complete. This sample application is intended as yet another 'proof of concept' and checks out if and how it is possible to create MVVM design pattern based X11/Windows (cross platform) application with XAML.

Since this seventh attempt to use XAML for a X11 application development has been successful, further articles about XAML using the Roma Widget Set on X11 will follow certenly.

Background

The Motivation and the general Concept to use XAML for X11 application development are already explained in the Writing a XAML dialog application for X11 article.

The XAML interpreter has been reworked almost completely for the Xrw version 0.9 to work more generic. The XAML preprocessor takes care for project sub-folders now. Both changes made working with WPF UserControls more comfortable.

Focus

Since the previous six articles "Writing XAML ..." already demonstrated several MVVM techniques, this article shall demonstrate that with XAML for X11

  • a windows compatible nice looking 7 segment LCD display UserControls can be created easily.

Using the code

The UserControl and sample application was written with Mono Develop 2.4.1 for Mono 2.8.1 on OPEN SUSE 11.3 Linux 32 bit EN and GNOME desktop. Neither the port to any older nor to any newer version should be a problem. The sample application's solution is build with MONO/.NET 3.5. It consists of two projects (the complete sources are provided for download):

  • XamlSevenSegment contains the source code of the UserControl and the sample application.
  • XamlPreprocessor contains the source code of the XAML preprocessor.

The UserControl and sample application are also tested with Mono Develop 3.0.6 for Mono 3.0.4 on OPEN SUSE 12.3 Linux 64 bit DE and GNOME desktop, IceWM, TWM und Xfce.

The only difference between the 32 bit and the 64 bit solution is the definition of some X11 specific data types, as already described in the Programming Xlib with Mono develop -Part 1: Low level (proof of concept) article.

The Xlib/X11 window handling is based on the X11Wrapper assembly version 0.9 (a library version 0.9 preview is included in this solution), that defines the function prototypes, structures and types for Xlib/X11 calls to the libX11.so. This assembly has been developed for the Programming Xlib with Mono Develop - Part 1: Low-level (proof of concept) project and has been advanced during the Programming the Roma Widget Set (C# X11) - a zero dependency GUI application framework - Basics project.

The GUI framework is based on the Xrw assembly version 0.9 (a library version 0.9 preview is included in this solution), that defines the widgets/gadgets and its wrapper classes used within the XAML code (that should be as near to the Microsoft® original as reasonable). This assembly has been developed during the Programming the Roma Widget Set (C# X11) - a zero dependency GUI application framework - Basics project.

Advice: To use the class library documentation shortcut (F1) from MonoDevelop, the "mono-tools" package has to be installed.

The sample application is inspired by the woderful articles A Fine-looking Segmented LED Control by Liu Xia (this article is a very good and detailed, the control provides high flexibility but utilizes Windows.Forms) and Seven Segment in WPF by Mohammad Dayyan.

The images of the sample application show a resizable digital clock with auto-scaling 7 segment LCD display.

The first image shows the sample application on OPEN SUSE 11.3 Linux 32 bit EN and GNOME desktop.

Image 4

The second image shows the sample application on OPEN SUSE 12.3 Linux 64 bit DE and Xfce.

Image 5

The third image shows the sample application on Windows® 8.1 64 Bit Edition.

Image 6

The application window utilizes the System.Windows.Controls.Grid as it's root layout manager. It realizes the margins and the position of six System.Windows.ExtControls.SimpleSevenSegment UserControls and the two System.Windows.ExtControls.ColumnForSevenSegment UserControls. All UserControls resize automatically. A System.Windows.Forms.Timer updates the clock every 50 ms.

There is no functional difference between the X11 and Windows 8.1 versions of the sample application, they share the same code entirely.

Walk through

Main view file context

The XAML (MainWindow.xaml)

Here the XAML file with references to the eight user controls (6 x SimpleSevenSegment and 2 x ColumnForSevenSegment).

XML
<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:ext="clr-namespace:System.Windows.ExtControls"
        x:Class="XamlSevenSegment.MainWindow"
        Name="DigiClock" Title="Digital clock"
        Height="208" Width="780" Icon="XrwIcon16.bmp"
        Background="#FFDDDDDD">
    <Grid Name="gridDigiClock" Background="#FFDDDDDD" Grid.Row="1" Grid.Column="1">
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="10*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="3*"/>
            <ColumnDefinition Width="3*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="3*"/>
            <ColumnDefinition Width="3*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="3*"/>
            <ColumnDefinition Width="3*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <ext:SimpleSevenSegment x:Name="TenHours" Grid.Row="1" Grid.Column="1" DisplayCharacter="0"
                                SegmentStroke="#FFAADDAA" SegmentFillOn="#FF22CC22"
                                SegmentFillOff="#FFD8E1D8"/>
        <ext:SimpleSevenSegment x:Name="SingleHours" Grid.Row="1" Grid.Column="2" DisplayCharacter="0"
                                SegmentStroke="#FFAADDAA" SegmentFillOn="#FF22CC22"
                                SegmentFillOff="#FFD8E1D8"/>
        <ext:ColumnForSevenSegment x:Name="HoursMinutesDelimiter" Grid.Row="1" Grid.Column="3"
                                Background="#FFDDDDDD" />
        <ext:SimpleSevenSegment x:Name="TenMinutes" Grid.Row="1" Grid.Column="4" DisplayCharacter="0"
                                SegmentStroke="#FFAADDAA" SegmentFillOn="#FF22CC22"
                                SegmentFillOff="#FFD8E1D8"/>
        <ext:SimpleSevenSegment x:Name="SingleMinutes"  Grid.Row="1" Grid.Column="5" DisplayCharacter="0"
                                SegmentStroke="#FFAADDAA" SegmentFillOn="#FF22CC22"
                                SegmentFillOff="#FFD8E1D8"/>
        <ext:ColumnForSevenSegment x:Name="MinutesSecondsDelimiter" Grid.Row="1" Grid.Column="6"
                                Background="#FFDDDDDD" />
        <ext:SimpleSevenSegment x:Name="TenSeconds" Grid.Row="1" Grid.Column="7" DisplayCharacter="0"
                                SegmentStroke="#FFAADDAA" SegmentFillOn="#FF22CC22"
                                SegmentFillOff="#FFD8E1D8"/>
        <ext:SimpleSevenSegment x:Name="SingleSeconds" Grid.Row="1" Grid.Column="8" DisplayCharacter="0"
                                SegmentStroke="#FFAADDAA" SegmentFillOn="#FF22CC22"
                                SegmentFillOff="#FFD8E1D8"/>
    </Grid>
</Window>

The complete XAML code is fully Microsoft® compatible.

Compared to Writing a XAML application for X11 with UserControls and previous articles, no enhancments or differences are to be discussed.

The code behind (MainWindow.xaml.cs)

The corresponding C# code file of the main view is MainWindow.xaml.cs. and looks like that:

C#
using System;
using System.Windows;
using System.Windows.Controls;

using X11;
using Xrw;
using XrwXAML;

namespace XamlSevenSegment
{
    /// <summary>Interaction logic for MainWindow.xaml</summary>
    public partial class MainWindow : XrwXAML.Window
    {
        public MainWindow()
            : base (-1, -1)
        {
            InitializeComponent();

            System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
            timer.Interval = 50;
            timer.Tick += timer_Tick;
            timer.Start ();
        }

        void timer_Tick(object sender, EventArgs e)
        {
            DateTime now = DateTime.Now;
            int hrs = now.Hour;
            int mns = now.Minute;
            int scs = now.Second;

            TenHours.DisplayCharacter = (char)('0' + (hrs / 10));
            SingleHours.DisplayCharacter = (char)('0' + (hrs % 10));

            TenMinutes.DisplayCharacter = (char)('0' + (mns / 10));
            SingleMinutes.DisplayCharacter = (char)('0' + (mns % 10));

            TenSeconds.DisplayCharacter = (char)('0' + (scs / 10));
            SingleSeconds.DisplayCharacter = (char)('0' + (scs % 10));
            
            gridDigiClock.InvalidateVisual ();
        }
    }
}

Main view model file context

There is no ModelView for the main view, because no Model is required.

Main model file context

There is no Model for the main view, because no data are to process.

SimpleSevenSegment UserControl

The XAML (SimpleSevenSegment.xaml)

The XAML file of the UserControl is:

XML
<UserControl x:Class="System.Windows.ExtControls.SimpleSevenSegment"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:my="clr-namespace:System.Windows.ExtControls">
    <Canvas Name="canvasSimpleSevenSegment" Background="#FFDDDDDD" Grid.Row="0" Grid.Column="0"
            Clip="{Binding ElementName=canvasSimpleSevenSegment, Path=DefiningGeometry}"/>
</UserControl>

The UserControl consists of one System.Windows.Controls.Canvas only. All seven segment shapes are created dynamically by the code behind.

The code behind (SimpleSevenSegment.xaml.cs)

The corresponding C# code file of the UserControl is SimpleSevenSegment.xaml.cs. It contains several utility methods:

  • CreateSegments() to create the seven shapes (System.Windows.Shapes.Polygon) to display on top of the System.Windows.Controls.Canvas.
  • UpdateSegmentGeometry() to recalculate the size of the seven shapes dynamically, based on the size of the parent System.Windows.Controls.Canvas.
  • UpdateSegmentCharacter() configures the on/off flags of the seven shapes according to the current character to display, set be the DisplayCharacter control property.
  • UpdateSegmentColors() configures the stroke colors an fill colors of the seven shapes according to the current
    • on/off flags based on the character to display, set by the DisplayCharacter control property, and
    • colors, set by the SegmentStroke, SegmentFillOn and SegmentFillOff control properties.

The UpdateSegmentColors() method always calls UpdateSegmentCharacter() to make sure, that the on/off flags of the seven shapes are up to date.

The dependency properties, the SimpleSevenSegment UserControl supports, are:

XML
/// <summary>Identify the Mines.SimpleSevenSegment.SegmentStroke dependency property.</summary>
/// <returns>The identifier for the Mines.SimpleSevenSegment.SegmentStroke dependency property.</returns>
public static readonly DependencyProperty SegmentStrokeProperty;

/// <summary>Identify the Mines.SimpleSevenSegment.SegmentFillOn dependency property.</summary>
/// <returns>The identifier for the Mines.SimpleSevenSegment.SegmentFillOn dependency property.</returns>
public static readonly DependencyProperty SegmentFillOnProperty;

/// <summary>Identify the Mines.SimpleSevenSegment.SegmentFillOff dependency property.</summary>
/// <returns>The identifier for the Mines.SimpleSevenSegment.SegmentFillOff dependency property.</returns>
public static readonly DependencyProperty SegmentFillOffProperty;

/// <summary>Identify the Mines.SimpleSevenSegment.DisplayCharacter dependency property.</summary>
/// <returns>The identifier for the Mines.SimpleSevenSegment.DisplayCharacter dependency property.</returns>
public static readonly DependencyProperty DisplayCharacterProperty;

The according properties are implemented as:

C#
/// <summary>The segment's outline stroke color.</summary>
[Browsable(true)]
[Bindable(true)]
[Category("Appearence")]
[Description("The segment's outline stroke color.")]
public System.Windows.Media.Color SegmentStroke
{
    get
    {
        return ((System.Windows.Media.Color)GetValue(SegmentStrokeProperty));
    }
    set
    {
        SetValue(SegmentStrokeProperty, value);
    }
}

/// <summary>The segment's fill color.</summary>
[Browsable(true)]
[Bindable(true)]
[Category("Appearence")]
[Description("The segment's fill color for on segments.")]
public System.Windows.Media.Color SegmentFillOn
{
    get
    {
        return ((System.Windows.Media.Color)GetValue(SegmentFillOnProperty));
    }
    set
    {
        SetValue(SegmentFillOnProperty, value);
    }
}

/// <summary>The segment's fill color.</summary>
[Browsable(true)]
[Bindable(true)]
[Category("Appearence")]
[Description("The segment's fill color for off segments.")]
public System.Windows.Media.Color SegmentFillOff
{
    get
    {
        return ((System.Windows.Media.Color)GetValue(SegmentFillOffProperty));
    }
    set
    {
        SetValue(SegmentFillOffProperty, value);
    }
}

/// <summary>The segment's character to display.</summary>
[Browsable(true)]
[Bindable(true)]
[Category("Appearence")]
[Description("The character to display.")]
public System.Char DisplayCharacter
{
    get
    {
        return ((System.Char)GetValue(DisplayCharacterProperty));
    }
    set
    {
        SetValue(DisplayCharacterProperty, value);
    }
}

ColumnForSevenSegment UserControl

The XAML (ColumnForSevenSegment.xaml)

The XAML file of the UserControl is:

XML
<UserControl x:Class="System.Windows.ExtControls.ColumnForSevenSegment"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="30">
    <Grid Name="gridColumnForSevenSegment">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="10*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="3*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="2*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="3*"/>
        </Grid.RowDefinitions>

        <Viewbox Name="viewboxUpperDot" Grid.Row="1" Grid.Column="1" >
            <Ellipse Fill="#FF22CC22" Stroke="#FF22CC22" />
        </Viewbox>
        <Viewbox Name="viewboxLowerDot" Grid.Row="3" Grid.Column="1" >
            <Ellipse Fill="#FF22CC22" Stroke="#FF22CC22"/>
        </Viewbox>
    </Grid>
</UserControl>

The UserControl contains a System.Windows.Controls.Grid as it's layout manager. The two System.Windows.Controls.ViewBox controls realize a quadratic size/resize of the two ellipses (x-radius == y-radius). The two System.Windows.Shapes.Ellipse shapes realize the column dots.

The code behind (ColumnForSevenSegment.xaml.cs)

The corresponding C# code file of the UserControl is ColumnForSevenSegment.xaml.cs and it is as bare as possible:

C#
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Shapes;

namespace System.Windows.ExtControls
{
    /// <summary>
    /// Interaction logic for ColumnForSevenSegment.xaml
    /// </summary>
    public partial class ColumnForSevenSegment : UserControl
    {
        public ColumnForSevenSegment()
        {
            InitializeComponent();
        }
    }
}

Points of Interest

This is another XAML application for X11, fully compatible with Microsoft®, showing the main advantages of this approach (compared to an implementation with GTK+ or KDE): The 100% cross platform compatible GUI definition and the savings of code lines to create the GUI.

The use of user controls can divide complex GUIs into more simple and maintainable peaces, saves code repitition and can be applied for X11 and Windows (cross platform).

History

The first version of this article is from 30. September 2015.

License

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