为 X11 编写具有海量数据绑定和零代码的 XAML 应用程序






4.88/5 (5投票s)
目前,所有主要的 Linux/Unix (X11) GUI 应用程序框架 (GTK+, KDE) 都不支持基于 XAML 的应用程序开发。Moonlight 项目 (包括 XAML 支持) 已于 2012 年 5 月 29 日被放弃。本文评述了一个利用大量数据绑定和零代码的 XAML 应用程序。
下载 XamlFontApp_X11_32.zip
下载 XamlFontApp_X11_64.zip
下载 XamlFontApp_Win7.zip
引言
本文是一个案例研究,介绍如何使用 Roma Widget Set (Xrw) 编写一个基于 MVVM (Model View ViewModel) 设计模式的 X11 应用程序 (利用大量数据绑定和零代码),并使用 XAML。 Roma Widget Set 是一个零依赖的 X11 GUI 应用程序框架 (仅需要免费的 Mono 标准安装程序集和免费 X11 发行版的库;它不特别需要 GNOME、KDE 或商业库),并且完全用 C# 实现。
本文延续了“为 X11 编写 XAML 对话框应用程序”和“为 X11 编写 XAML 功能区应用程序”的工作。据我所知,这是在 Moonlight 被放弃后,首次尝试使用 XAML 进行 X11 应用程序开发 (利用 Xrw)。
Roma Widget Set 和 XAML 实现都未完成。本示例应用程序旨在作为更复杂的“概念验证”,并检查是否以及如何可能使用 XAML 创建基于 MVVM 设计模式的 X11 应用程序。
由于这是第三次尝试使用 XAML 进行 X11 应用程序开发并已成功,未来将有更多关于使用 Roma Widget Set 在 X11 上进行 XAML 开发的文章。
背景
使用 XAML 进行 X11 应用程序开发的动机和一般概念已在“为 X11 编写 XAML 对话框应用程序”一文中解释。
焦点
第一个文章 为 X11 编写 XAML 对话框应用程序 演示了使用 XAML
- 定义一个包含一些控件的窗口,并且
- 将点击事件连接到按钮上,
第二篇文章“为 X11 编写 XAML 功能区应用程序”演示了使用 XAML 如何
- 定义一个包含 Ribbon 命令界面的窗口,
- 定义静态窗口资源(本示例有一个资源转换器和一个 ModelView),
- 将 ModelView 作为静态资源分配给控件的数据上下文,
- 将资源转换器作为静态资源应用于控件的属性,
- 通过“RelayCommand”方法将命令绑定到按钮,
- 将控件属性绑定到数据上下文,并且
- 可以通过
INotifyPropertyChanged
接口更新控件,
本文将演示 XAML 可以实现:
- 海量数据绑定可以提供有用的功能,并且
- 可以定义一个零代码后端的应用程序。
使用代码
本示例应用程序是使用 Mono Develop 2.4.1 为 Mono 2.8.1 在 OPEN SUSE 11.3 Linux 32 位 EN 和 GNOME 桌面环境下编写的。移植到任何更早或更新版本应该都不会有问题。示例应用程序的解决方案包含两个项目 (完整的源代码均可下载)
- XamlFontApp 包含示例应用程序的源代码。
- XamlPreprocessor 包含 XAML 预处理器的源代码。
该示例应用程序还通过了 Mono Develop 3.0.6 在 OPEN SUSE 12.3 Linux 64 位 DE 和 GNOME 桌面、IceWM、TWM 和 Xfce 上针对 Mono 3.0.4 的测试。
32 位和 64 位解决方案之间的唯一区别在于一些 X11 特定数据类型的定义,正如在“使用 Mono Develop 编程 Xlib - 第一部分:底层 (概念验证)”一文中所述。
Xlib/X11 窗口处理基于 X11Wrapper 程序集版本 0.7,该程序集定义了 libX11.so 调用的函数原型、结构和类型。该程序集是为 使用 Mono Develop 编程 Xlib - 第一部分:底层 (概念验证) 项目开发的,并在 编程 Roma Widget Set (C# X11) - 一个零依赖 GUI 应用程序框架 - 基础 项目中得到改进。
GUI 框架基于 Xrw 程序集版本 0.7,该程序集定义了 XAML 代码中使用的控件/小部件及其包装类 (应尽可能接近 Microsoft® 原版)。该程序集是在 编程 Roma Widget Set (C# X11) - 一个零依赖 GUI 应用程序框架 - 基础 项目中开发的。
建议:要使用 MonoDevelop 的类库文档快捷键 (F1),必须安装“mono-tools”软件包。
第一张图片显示了示例应用程序在 OPEN SUSE 11.3 Linux 32 位 EN 和 GNOME 桌面上的样子。
第二张图片显示了示例应用程序在 OPEN SUSE 12.3 Linux 64 位 DE 和 Xfce 上的样子。
第三张图片显示了示例应用程序在 Windows® 7 64 位版本上的样子。
本示例应用程序提供了一个字体系列选择列表、一个字形选择列表和一个字体大小滑块。任何字体更改将立即在预览文本中显示。
本示例应用程序基于 Norris Cheng 的文章 Pure XAML Font Choose 中的思想。具体的挑战在于为 X11 实现零代码后端的完整功能。
逐步演示
项目设置、应用程序文件上下文 (除主题外) 和预处理器代码生成步骤与“为 X11 编写 XAML 对话框应用程序”中的完全相同。如果需要从头开始创建新解决方案,请参考该文章。
主视图文件上下文
XAML (MainView.xaml)
首先是窗口的 XAML 文件。
<Window x:Class="XamlFontApp.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:XamlFontApp"
DataContext="src:MainViewModel"
Name="MainWindow" Title="XAML font application"
Width="750" Height="400" Icon="XrwIcon16.bmp">
<Window.Resources>
</Window.Resources>
<Grid Name="MainGrid" Background="#FFEBEBEB">
<Grid.Resources>
<!-- <src:MainViewModel x:Key="mainViewDataSource" /> -->
</Grid.Resources>
<Grid.Datacontext>
<!-- <Binding Source="{StaticResource mainViewDataSource}"/> -->
</Grid.Datacontext>
<Grid.RowDefinitions>
<RowDefinition Height="10"/>
<RowDefinition Height="28"/>
<RowDefinition Height="*"/>
<RowDefinition Height="28"/>
<RowDefinition Height="10"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="10"/>
</Grid.ColumnDefinitions>
<Label Name="FontFamiliesLabel" Grid.Column="1" Grid.Row="1" FontSize="14"
FontWeight="Bold" Content="Font Family" Background="#FFEBEBEB" />
<ListView Name="FontFamiliesListView" Grid.Column="1" Grid.Row="2"
ItemsSource="{x:Static Fonts.SystemFontFamilies}" >
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="Source"
DisplayMemberBinding="{Binding Source}" />
<GridViewColumn Header="Baseline"
DisplayMemberBinding="{Binding Baseline}" />
<GridViewColumn Header="LineSpacing"
DisplayMemberBinding="{Binding LineSpacing}" />
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
<Label Name="SelectedFontFamilyLabel" Grid.Column="1" Grid.Row="3"
Content="{Binding SelectedItem.Source, ElementName=FontFamiliesListView, Mode=OneWay}"
Background="#FFEBEBEB" />
<Label Name="FamilyTypefacesLabel" Grid.Column="3" Grid.Row="1"
FontSize="14" FontWeight="Bold" Content="Family Typefaces" Background="#FFEBEBEB" />
<ListView Name="FamilyTypefacesListView" Grid.Column="3" Grid.Row="2"
ItemsSource="{Binding SelectedItem.FamilyTypefaces,
ElementName=FontFamiliesListView, Mode=OneWay}" >
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="Style" DisplayMemberBinding="{Binding Style}" />
<GridViewColumn Header="Weight" DisplayMemberBinding="{Binding Weight}" />
<GridViewColumn Header="Stretch" DisplayMemberBinding="{Binding Stretch}" />
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
<Label Name="SelectedFamilyTypefacesLabel" Grid.Column="3" Grid.Row="3"
Content="{Binding SelectedItem.Style, ElementName=FamilyTypefacesListView, Mode=OneWay}"
Background="#FFEBEBEB" />
<Slider Name="FontSizeSlider" Grid.Column="5" Grid.Row="1" Orientation="Horizontal"
Minimum="6" Value="12" Maximum="72" SmallChange="1" LargeChange="8" />
<TextBlock Name="TextSimulation" Grid.Column="5" Grid.Row="2"
Text="The quick brown fox jumps over the lazy dog."
FontFamily="{Binding SelectedItem.Source, ElementName=FontFamiliesListView}"
FontWeight="{Binding SelectedItem.Weight, ElementName=FamilyTypefacesListView}"
FontStretch="{Binding SelectedItem.Stretch, ElementName=FamilyTypefacesListView}"
FontStyle="{Binding SelectedItem.Style, ElementName=FamilyTypefacesListView}"
FontSize="{Binding Value, ElementName=FontSizeSlider}" TextWrapping="Wrap"
Background="#FFF8F8F8" />
<Label Name="FontSizeLabel" Grid.Column="5" Grid.Row="3" Background="#FFEBEBEB"
Content="{Binding Value, ElementName=FontSizeSlider, Mode=OneWay}" />
</Grid>
</Window>
完整的 XAML 代码与 Microsoft® 完全兼容。
与“为 X11 编写 XAML 功能区应用程序”相比,仅讨论增强功能和差异。
Label
将 (除已知功能外) 由以下方式定义:
Background
属性定义背景颜色。此属性是可选的。该属性的语法是十六进制值#AARRGGBB
,其中AA
代表 alpha,RR
代表红色,GG
代表绿色,BB
代表蓝色。目前 alpha 值尚未实现。FontSize
属性定义字体大小。此属性是可选的。该属性的语法是十进制值。位图字体 (与矢量字体相对) 不支持任何字体大小。如果指定了任何不受支持的字体大小,其字体大小将回退到最近支持的字体大小。FontWeight
属性定义字体粗细。此属性是可选的。该属性的语法是任何System.Windows.FontWeight
值的字符串表示。典型值包括Light
、Regular
和Bold
。回退值为Regular
。请注意,Windows® 字体通常使用Normal
值表示字体粗细,而 X11 字体通常使用Regular
值表示等效的字体粗细。大多数默认字体系列仅支持少数字体粗细,并且某些字体粗细不被任何默认字体系列支持。
ListView
将 (除已知功能外) 由以下方式定义:
ItemSource
属性定义用于创建显示项的数据源。此属性是可选的。该属性的语法可以是{x:Static <资源名称>}
用于静态资源,或者{Binding <路径>, ElementName=<控件名称>, Mode=<模式>}
用于动态资源。数据源必须实现System.Collections.IEnumerable
。动态数据源还应实现INotifyPropertyChanged
。目前Mode
尚未实现。
Slider
将由以下方式定义:
Name
属性定义类实例的名称,可用于唯一标识该类实例。如果此类实例需要通过 C# 代码访问,则此属性是推荐的或必需的。Grid.Column
属性定义控件在网格内应该放置的零基列索引。默认值为 0。对于网格的子元素,此属性是推荐的,但对于放置在非第 0 列的控件,它是必需的。索引不得超过可用的网格列数。Grid.Row
属性定义控件在网格内应该放置的零基行索引。默认值为 0。对于网格的子元素,此属性是推荐的,但对于放置在非第 0 行的控件,它是必需的。索引不得超过可用的网格行数。Orientation
属性定义方向。此属性是可选的。默认值为水平。Minimum
属性定义可能的最小值。此属性是可选的。默认值为0.0F。Maximum
属性定义可能的最大值。此属性是可选的。默认值为100.0F。Value
属性定义当前选定的值。此属性是可选的。默认值为Minimum
。SmallChange
属性当前未实现。LargeChange
属性当前未实现。
TextBlock
(类似于 Label
,但支持自动换行) 将由以下方式定义:
Name
属性定义类实例的名称,可用于唯一标识该类实例。如果此类实例需要通过 C# 代码访问,则此属性是推荐的或必需的。Grid.Column
属性定义控件在网格内应该放置的零基列索引。默认值为 0。对于网格的子元素,此属性是推荐的,但对于放置在非第 0 列的控件,它是必需的。索引不得超过可用的网格列数。Grid.Row
属性定义控件在网格内应该放置的零基行索引。默认值为 0。对于网格的子元素,此属性是推荐的,但对于放置在非第 0 行的控件,它是必需的。索引不得超过可用的网格行数。Text
属性定义要显示的文本。此属性是可选的。默认值为空字符串。FontFamily
属性定义字体系列名称。此属性是可选的。该属性的语法是<字体系列名称>,例如 Arial,或者 <字体铸造厂名称>-<字体系列名称>,例如 Adobe-Utopia。第一种语法完全兼容 Microsoft®,第二种语法是 X11 字体规范所必需的。FontWeight
属性定义字体粗细。此属性是可选的。该属性的语法是任何System.Windows.FontWeight
值的字符串表示。典型值包括Light
、Regular
和Bold
。回退值为Regular
。请注意,Windows® 字体通常使用Normal
值表示字体粗细,而 X11 字体通常使用Regular
值表示等效的字体粗细。大多数默认字体系列仅支持少数字体粗细,并且某些字体粗细不被任何默认字体系列支持。FontStretch
属性定义字体伸展度。此属性是可选的。该属性的语法是任何System.Windows.FontStretch
值的字符串表示。典型值包括SemiCondensed
、Medium
和SemiExpanded
。回退值为Medium
。大多数默认字体系列仅支持少数字体伸展度,并且某些字体伸展度不被任何默认字体系列支持。FontStyle
属性定义字体样式。此属性是可选的。该属性的语法是任何System.Windows.FontStyle
值的字符串表示。典型值包括Normal
、Italic
和Oblique
。回退值为Normal
。并非所有默认字体系列都支持每种字体样式。FontSize
属性定义字体大小。此属性是可选的。该属性的语法是十进制值。位图字体 (与矢量字体相对) 不支持任何字体大小。如果指定了任何不受支持的字体大小,其字体大小将回退到最近支持的字体大小。
代码隐藏 (MainView.xaml.cs)
主视图对应的 C# 代码文件是 MainView.xaml.cs
。它处于初始状态,因此几乎是空的。
using System;
using X11;
using Xrw;
using XrwXAML;
namespace XamlFontApp
{
/// <summary>The main window of the application. This class must be derived from XrwXAML.Window.
/// It must be a partial class.
/// The second part of the class will be autogenerated and named '*.generated.cs'.</summary>
public partial class MainView : XrwXAML.Window, IView
{
/// <summary>The default constructor.</summary>
public MainView ()
: base (-1, -1)
{
InitializeComponent ();
// Will be called after construction by generated code!
}
}
}
主视图模型文件上下文
MainWindowViewModel.cs
文件保持初始状态。ViewModel 未向应用程序提供任何功能。
主模型文件上下文
MainModel.cs
文件保持初始状态。Model 未向应用程序提供任何功能。
关注点
无需一行代码即可创建如此多的功能,这真是令人惊叹!(并且完全兼容 Microsoft®)。
前三篇关于“为 X11 编写 XAML 应用程序”的文章已经展示了如何编写可以完全移植到 Windows® 和 X11 的单视图应用程序。下一步应该是证明这一点可以用于多视图应用程序。
自2015年2月2日起,文章为 X11 编写 XAML 计算器应用程序延续了 XAML 主题。
历史
本文的第一个版本发布于 2014 年 11 月 22 日。
第一个审阅版本发布于 2015 年 2 月 19 日。
第二个审阅版本发布于 2015 年 2 月 19 日。