Download XamlSevenSegment_X11_32.zip Mono solution including full source and executable
Download XamlSevenSegment_X11_64.zip Mono solution including full source and executable
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.
The second image shows the sample application on OPEN SUSE 12.3 Linux 64 bit DE and Xfce.
The third image shows the sample application on Windows® 8.1 64 Bit Edition.
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
).
<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:
using System;
using System.Windows;
using System.Windows.Controls;
using X11;
using Xrw;
using XrwXAML;
namespace XamlSevenSegment
{
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:
<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:
/// <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:
[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);
}
}
[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);
}
}
[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);
}
}
[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:
<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:
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
{
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.