65.9K
CodeProject 正在变化。 阅读更多。
Home

为 X11 和 Windows 编写 XAML 7 段 LCD 显示器 UserControl

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2015年9月30日

CPOL

7分钟阅读

viewsIcon

21805

downloadIcon

909

目前,没有哪个主要的 Linux/Unix (X11) GUI 应用程序框架(GTK+、KDE)支持基于 XAML 的应用程序开发。Moonlight 项目(包括 XAML 支持)已于 2012 年 5 月 29 日被放弃。本文介绍了一个基于 XAML 的 7 段 LCD 显示器,它利用了一个 WPF UserControl。

 下载 XamlSevenSegment_X11_32.zip Mono 解决方案,包含完整的源代码和可执行文件
 下载 XamlSevenSegment_X11_64.zip Mono 解决方案,包含完整的源代码和可执行文件
 下载 XamlSevenSegment_Win8.zip Visual Studio 2013 解决方案,包含完整的源代码和可执行文件

引言

本文是一个案例研究,介绍如何使用 **Roma Widget Set** (Xrw) 编写一个基于 MVVM (Model View ViewModel) 设计模式的 X11/Windows(跨平台)7 段 LCD 显示器(利用 WPF UserControls)并使用 XAML。 **Roma Widget Set** 是一个零依赖的 X11 GUI 应用程序框架(它只需要免费的 Mono 标准安装程序集和免费的 X11 发行版库;它不特别需要 GNOME、KDE、cairo、pango 或商业库),并且完全用 C# 实现。

本文是接续之前的文章 《为 X11 编写 XAML 对话框应用程序》《为 X11 编写 XAML 应用程序》《为 X11 编写 XAML 功能区应用程序》《使用大量数据绑定和零代码为 X11 编写 XAML 应用程序》《为 X11 编写 XAML 计算器应用程序》《编写带有几何对象(形状)的 XAML 应用程序以用于 X11》《使用 UserControls 为 X11 编写 XAML 应用程序》 等作品。据我所知,这是在 Moonlight 被放弃后,利用 Xrw 进行 X11 应用程序开发的首次尝试。

Roma Widget Set 和 XAML 实现都未完成。这个示例应用程序旨在作为另一个“概念验证”,并检查是否以及如何能够使用 XAML 为 X11/Windows(跨平台)应用程序创建基于 MVVM 设计模式的应用程序。

由于这是第七次尝试使用 XAML 进行 X11 应用程序开发,并且已经成功,因此关于使用 Roma Widget Set 在 X11 上进行 XAML 的后续文章肯定会接踵而至。

背景

《为 X11 编写 XAML 对话框应用程序》 文章中已经解释了使用 XAML 进行 X11 应用程序开发的 **动机** 和总体 **概念**。

XAML 解释器已为 Xrw 版本 0.9 进行了几乎完全的重构,以便更通用地工作。XAML 预处理器现在可以处理项目子文件夹。这两个更改都使使用 WPF UserControls 更加舒适。

焦点

由于之前的六篇文章《编写 XAML...》已经演示了多种 MVVM 技术,因此本文将演示使用 XAML 为 X11

  • 可以轻松创建兼容 Windows 的、外观漂亮的 7 段 LCD 显示器 UserControls。

使用代码

UserControl 和示例应用程序是用 Mono Develop 2.4.1 为 Mono 2.8.1 在 OPEN SUSE 11.3 Linux 32 位 EN 和 GNOME 桌面上编写的。移植到任何旧版本或新版本都不应该存在问题。示例应用程序的解决方案是用 MONO/.NET 3.5 构建的。它包含两个项目(提供完整的源代码供下载)

  • **XamlSevenSegment** 包含 UserControl 和示例应用程序的源代码。
  • XamlPreprocessor 包含 XAML 预处理器的源代码。

UserControl 和示例应用程序也已在 OPEN SUSE 12.3 Linux 64 位 DE 和 GNOME 桌面、IceWM、TWM 和 Xfce 上使用 Mono Develop 3.0.6 为 Mono 3.0.4 进行了测试。

32 位和 64 位解决方案之间唯一的区别是某些 X11 特定数据类型的定义,这已在 《使用 Mono Develop 编写 Xlib - 第一部分:底层(概念验证)》 文章中有所描述。

Xlib/X11 窗口处理基于 **X11Wrapper** 程序集版本 0.9(此解决方案中包含一个版本 0.9 的预览版库),它为 libX11.so 的 Xlib/X11 调用定义了函数原型、结构和类型。该程序集是为 《使用 Mono Develop 编写 Xlib - 第一部分:底层(概念验证)》 项目开发的,并在 《编程 Roma Widget Set (C# X11) - 一个零依赖的 GUI 应用程序框架 - 基础》 项目中得到了改进。

GUI 框架基于 **Xrw** 程序集版本 0.9(此解决方案中包含一个版本 0.9 的预览版库),它定义了 XAML 代码中使用的控件/小工具及其包装类(这些代码应尽可能接近 Microsoft® 的原始代码)。该程序集是在 《编程 Roma Widget Set (C# X11) - 一个零依赖的 GUI 应用程序框架 - 基础》 项目中开发的。

建议:要使用 MonoDevelop 的类库文档快捷键 (F1),必须安装“mono-tools”软件包。

示例应用程序的灵感来自 Liu Xia 的精彩文章 《一款外观精美的分段 LED 控件》 (这篇文章非常好且详细,控件提供了很高的灵活性,但使用了 Windows.Forms)和 Mohammad Dayyan 的文章 《WPF 中的七段 LCD 控件》

示例应用程序的图片显示了一个可调整大小的数字时钟,带有自动缩放的 7 段 LCD 显示屏。

第一张图片显示了示例应用程序在 OPEN SUSE 11.3 Linux 32 位 EN 和 GNOME 桌面上的样子。

第二张图片显示了示例应用程序在 OPEN SUSE 12.3 Linux 64 位 DE 和 Xfce 上的样子。

第三张图片显示了示例应用程序在 Windows® 8.1 64 位版本上的运行效果。

应用程序窗口使用 System.Windows.Controls.Grid 作为其根布局管理器。它实现了六个 System.Windows.ExtControls.SimpleSevenSegment UserControls 和两个 System.Windows.ExtControls.ColumnForSevenSegment UserControls 的边距和位置。所有 UserControls 都能自动调整大小。一个 System.Windows.Forms.Timer 每 50 毫秒更新一次时钟。

X11 和 Windows 8.1 版本的示例应用程序在功能上没有任何区别,它们完全共享相同的代码。

逐步演示

主视图文件上下文

XAML (MainWindow.xaml)

这是带有对八个用户控件(6 个 SimpleSevenSegment 和 2 个 ColumnForSevenSegment)引用的 XAML 文件。

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

完整的 XAML 代码与 Microsoft® 完全兼容。

《使用 UserControls 为 X11 编写 XAML 应用程序》 及之前的文章相比,没有新的改进或区别需要讨论。

后端代码 (MainWindow.xaml.cs)

主视图对应的 C# 代码文件是 MainWindow.xaml.cs,内容如下:

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

主视图模型文件上下文

主视图没有 ModelView,因为不需要 Model。

主模型文件上下文

主视图没有 Model,因为没有数据需要处理。

SimpleSevenSegment UserControl

XAML (SimpleSevenSegment.xaml)

UserControl 的 XAML 文件是:

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

UserControl 只包含一个 System.Windows.Controls.Canvas。所有七段形状都由代码隐藏动态创建。

代码隐藏 (SimpleSevenSegment.xaml.cs)

UserControl 对应的 C# 代码文件是 SimpleSevenSegment.xaml.cs。它包含几个实用方法:

  • CreateSegments() 用于创建显示在 System.Windows.Controls.Canvas 顶部的七个形状(System.Windows.Shapes.Polygon)。
  • UpdateSegmentGeometry() 根据父 System.Windows.Controls.Canvas 的大小动态重新计算七个形状的大小。
  • UpdateSegmentCharacter() 根据由 DisplayCharacter 控件属性设置的当前要显示的字符来配置七个形状的开/关标志。
  • UpdateSegmentColors() 根据当前
    • 基于要显示的字符设置的开/关标志(由 DisplayCharacter 控件属性设置),以及
    • SegmentStrokeSegmentFillOnSegmentFillOff 控件属性设置的颜色,来配置七个形状的描边颜色和填充颜色。

UpdateSegmentColors() 方法始终调用 UpdateSegmentCharacter(),以确保七个形状的开/关标志是最新的。

SimpleSevenSegment UserControl 支持的依赖属性是:

/// <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;

相应的属性实现如下:

/// <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

XAML (ColumnForSevenSegment.xaml)

UserControl 的 XAML 文件是:

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

UserControl 包含一个 System.Windows.Controls.Grid 作为其布局管理器。两个 System.Windows.Controls.ViewBox 控件实现了两个椭圆(x 半径 == y 半径)的二次大小/缩放。两个 System.Windows.Shapes.Ellipse 形状实现了列点。

代码隐藏 (ColumnForSevenSegment.xaml.cs)

UserControl 对应的 C# 代码文件是 ColumnForSevenSegment.xaml.cs,它尽可能地精简。

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

关注点

这是另一个用于 X11 的 XAML 应用程序,完全兼容 Microsoft®,展示了这种方法的 M M M (compared to an implementation with GTK+ or KDE):100% 跨平台兼容的 GUI 定义以及在创建 GUI 时节省的代码行数。

使用用户控件可以将复杂的 GUI 分解为更简单、更易于维护的块,节省代码重复,并且可以应用于 X11 和 Windows(跨平台)。

历史

本文的第一个版本发布于 2015 年 9 月 30 日。

© . All rights reserved.