为 X11 编写带 UserControls 的 XAML 应用程序






4.43/5 (3投票s)
目前,主流的 Linux/Unix (X11) GUI 应用程序框架(GTK+, KDE)都不支持 XAML 应用程序开发。Moonlight 项目(包括 XAML 支持)已于 2012 年 5 月 29 日停止开发。本文回顾了一个利用 WPF UserControls 的 XAML 应用程序。
- 下载 XamlUserControls_X11_32.zip 包含完整源代码和可执行文件的 Mono 项目
- 下载 XamlUserControls_X11_64.zip 包含完整源代码和可执行文件的 Mono 项目
- 下载 XamlUserControls_Win81.zip 包含完整源代码和可执行文件的 Visual Studio 2013 项目
引言
本文是一篇案例研究,介绍如何使用 **Roma Widget Set** (Xrw) 来编写一个基于 MVVM (Model View ViewModel) 设计模式的 X11 应用程序(利用 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 应用程序》等文章的工作。据我所知,在 Moonlight 停止开发后,这是(利用 Xrw)首次尝试使用 XAML 进行 X11 应用程序开发。
Roma Widget Set 和 XAML 实现都不是完整的。本示例应用程序旨在作为又一个“概念验证”,并检查是否以及如何可能使用 XAML 为 X11 应用程序创建基于 MVVM 设计模式的应用程序。
由于这是第六次尝试使用 XAML 进行 X11 应用程序开发,并且已成功,因此肯定会有更多关于使用 Roma Widget Set 在 X11 上开发 XAML 的文章。
背景
使用 XAML 进行 X11 应用程序开发的**动机**和总体**概念**已在《为 X11 编写 XAML 对话框应用程序》一文中进行了介绍。
XAML 解释器已进行了近乎完整的重构。XAML 预处理器现在负责处理项目子文件夹。这两项更改都使得处理 WPF UserControls 更加方便。
焦点
鉴于之前的五篇《编写 XAML ...》文章已经展示了多种 MVVM 技术,本文将演示如何使用 XAML 在 X11 上创建和使用 UserControls。
- 用户控件可以被创建和使用。
使用代码
本示例应用程序使用 Mono Develop 2.4.1 为 OPEN SUSE 11.3 Linux 32 位 EN 和 GNOME 桌面上的 Mono 2.8.1 编写。移植到任何旧版本或新版本都不应该成问题。本示例应用程序的解决方案包含两个项目(提供完整源代码下载)。
- XamlUserControls 包含示例应用程序的源代码。
- 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 - 第 1 部分:底层(概念验证)》一文中已经描述的那样。
Xlib/X11 窗口处理基于 **X11Wrapper** 程序集版本 0.9(此示例项目包含一个版本 0.9 的预览版库),该程序集定义了 libX11.so.Xlib/X11 调用的函数原型、结构和类型。该程序集是为《使用 Mono Develop 编程 Xlib - 第 1 部分:底层(概念验证)》项目开发的,并在《使用 Roma Widget Set (C# X11) 编程 - 一个零依赖的 GUI 应用程序框架 - 基础》项目期间得到了改进。
GUI 框架基于 **Xrw** 程序集版本 0.9(此示例项目包含一个版本 0.9 的预览版库),该程序集定义了 XAML 代码中使用的控件/小部件及其包装类(应尽可能接近 Microsoft® 原版)。该程序集是在《使用 Roma Widget Set (C# X11) 编程 - 一个零依赖的 GUI 应用程序框架 - 基础》项目期间开发的。
建议:要使用 MonoDevelop 的类库文档快捷键 (F1),必须安装“mono-tools”软件包。
本示例应用程序的创意和代码源自 Jacques Fournier 的精彩作品 C# 中的国际象棋程序。 感谢 Jacques Fournier,我可能会将完整的应用程序移植到 X11。这就是命名空间和其他内容为 **SrcChess2** 的原因。
所有图像都显示了示例应用程序的相同状态:从左到右,(菜单栏下方和状态栏上方)显示的是:
- - 空的棋盘用户控件(显示一个带有字段名称的棋盘),
- - 走子查看器用户控件(显示已完成的走子,带有示例输出),以及
- - 丢失的棋子用户控件(显示一些图标代表丢失的国际象棋棋子)。
第一张图片显示了示例应用程序在 OPEN SUSE 11.3 Linux 32 位 EN 和 GNOME 桌面上的样子。
第二张图片显示了示例应用程序在 OPEN SUSE 12.3 Linux 64 位 DE 和 Xfce 上的样子。
第三张图片显示了 Windows® 8.1 64 位版上的示例应用程序。
应用程序窗口使用 System.Windows.Controls.DockPanel
作为其根布局管理器。一个带有单层菜单 **File** 和 **?** 的简单菜单栏停靠在顶部,状态栏停靠在底部;两者都占据了可用的全部宽度。一个停靠在“填充”的 System.Windows.Controls.Grid
将三个用户控件组织起来以进行演示。它们是:
ChessBoardControl
用户控件,定义在ChessBoardControl.xaml
中,显示一个空棋盘,周围有一个显示字段名称的边框。棋盘和边框由几个StackPanel
、UniformGrid
和Grid
布局控件实现,从而使棋盘可以根据当前窗口大小调整大小。棋盘用户控件通过Viewbox
嵌入到应用程序中,该Viewbox
向两个方向均匀拉伸。这确保了无论应用程序窗口具有何种特定几何形状,棋盘始终显示为正方形。MoveViewer
用户控件,定义在MoveViewer.xaml
中,显示当前比赛已完成的走子列表。此列表使用数据绑定到MoveListData
类。LostPiecesControl
用户控件,定义在LostPiecesControl.xaml
中,显示当前比赛中丢失的国际象棋棋子的图标,以供演示。此控件使用两次,分别代表白方和黑方。它提供了 4 x 4 张图片的存放空间,足以容纳一个玩家拥有的全部 16 个国际象棋棋子。
X11 和 Windows 8.1 版本的示例应用程序在功能上没有区别,它们完全共享相同的代码。
逐步演示
项目设置、应用程序文件上下文(主题除外)和预处理器代码生成步骤与《为 X11 编写 XAML 对话框应用程序》中的完全相同。如果需要从头开始创建新解决方案,请参考该文章。
尽管 X11 和 Windows 8.1 版本的示例应用程序完全共享相同的代码,但我还是想指出 C# 项目的 X11 和 Windows 8.1 版本之间的一些差异。
Visual Studio 集成了一个过程,该过程可以在运行时将 XAML
文件编译为 BAML
文件(二进制等效文件,用于实现语法检查,从而提前加快初始化速度并缩短应用程序启动时的初始化时间),并在应用程序启动时解释 BAML 文件以绑定控件和本机 C# 代码,而 MonoDevelop 仅提供在特定时间点运行用户定义任务的功能。
因此,MonoDevelop 项目在应用程序编译过程之前,会使用项目中的 **XamlPreprocessor** 和一些 **Xrw** 类扩展,将 XAML
文件预编译为本机 C# 代码。
这需要 MonoDevelop 项目与 Visual Studio 项目的源文件属性有所不同。
而 MonoDevelop
- 通过预编译用户任务处理所有 XAML 文件,
- 在构建过程中不执行任何操作(甚至不编译),并且
- 必须将生成的 C# 文件包含到应用程序构建过程中(在 *.xaml 文件首次预编译后,必须手动将生成的 *.generated.cs 文件包含到 C# 项目中),
Visual Studio 项目
- 在构建过程中将 XAML 文件视为 Page,通过外部编译器调用(MSBuild: Compile)来处理,
- 在构建过程中像任何 C# 文件一样编译代码隐藏文件,并且
- 要求 XAML 文件和关联的代码隐藏文件以层级结构组织。
主视图文件上下文
XAML (MainView.xaml)
首先是 XAML 文件,其中包含对四个用户控件(ChessBoardControl
、MoveViewer
和 2 个 MoveViewer
)的引用。
<Window x:Class="SrcChess2.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:SrcChess2"
xmlns:my="clr-namespace:SrcChess2"
Name="SrcChess" Title="SrcChess for WPF"
Width="650" Height="450" Icon="XrwIcon16.bmp"
Background="SkyBlue">
<!-- Idea based on: "Chess Program in C#" by Jacques Fournier on
https://codeproject.org.cn/Articles/36112/Chess-Program-in-C -->
<Window.Resources>
<src:MainWindowViewModel x:Key="MainViewModel" />
<!-- MainWindowViewModel : ViewModelCore<T>.ctr()
automatically registeres itself as the initial Window.DataContext. -->
</Window.Resources>
<DockPanel Name="m_dockPanelMain" DataContext="{StaticResource MainViewModel}">
<Menu Name="MainMenu" DockPanel.Dock="Top" >
<MenuItem Name="MenuItemFile" Header="_File ">
<MenuItem Name="MenuItemFileCopy" Header="Copy">
<MenuItem.Icon>
<Image Source="Images/copy16.bmp"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Name="MenuItemFilePaste" Header="Paste">
<MenuItem.Icon>
<Image Source="Images/paste16.bmp"/>
</MenuItem.Icon>
</MenuItem>
<Separator/>
<MenuItem Name="MenuItemFileEdit" Header="Exit" Click="MenuItemFileExit_Click">
<MenuItem.Icon>
<Image Source="Images/exit16.bmp"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem>
<MenuItem Name="MenuItemQmark" Header="_?">
<MenuItem Name="MenuItemQmarkHelp" Header="Help" Click="MenuItemQmarkHelp_Click">
<MenuItem.Icon>
<Image Source="Images/quest16.bmp"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Name="MenuItemQmarkAbout" Header="About" Click="MenuItemQmarkAbout_Click">
<MenuItem.Icon>
<Image Source="Images/info16.bmp"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem>
</Menu>
<StatusBar Name="m_statusBar" DockPanel.Dock="Bottom">
<StatusBarItem Name="m_statusLabelSearchMode" Content="Alpha-Beta (6 Ply)" />
<Separator />
<StatusBarItem Name="m_statusLabelMove" Content="Move" />
<Separator />
<StatusBarItem Name="m_statusLabelPermutation" Content="Permutation" />
</StatusBar>
<Grid Name="m_chessBoardAndMoveViewer">
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5*"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Viewbox Name="m_viewboxChessBoardControl" Grid.Column="0" Grid.Row="0" >
<my:LocalChessBoardControl x:Name="m_chessCtl" LiteCellColor="Moccasin" />
</Viewbox>
<my:MoveViewer x:Name="m_moveViewer" Grid.Column="1" Grid.Row="0"
Margin="0,10" Width="210"></my:MoveViewer>
<StackPanel Name="m_stackPanelLostPieces" Grid.Column="2" VerticalAlignment="Center">
<my:LostPiecesControl x:Name="m_lostPieceBlack" Margin="4,2,4,4"
BorderBrush="LightGray" BorderThickness="1" />
<my:LostPiecesControl x:Name="m_lostPieceWhite" Margin="4,4,4,0"
BorderBrush="LightGray" BorderThickness="1" />
</StackPanel>
</Grid>
</DockPanel>
</Window>
完整的 XAML 代码与 Microsoft® 完全兼容。
与《为 X11 编写具有几何对象(形状)的 XAML 应用程序》及之前的文章相比,这里仅讨论增强功能和差异。
StatusBar
将由以下方式定义:
Name
属性定义了类实例的名称,可用于唯一标识该类实例。此属性是推荐的,如果该类实例需要通过 C# 代码访问,则是必需的。
StatusBar
支持 StatusBarItem
和 Separator
子控件。
StatusBarItem
将由以下方式定义:
Name
属性定义了类实例的名称,可用于唯一标识该类实例。此属性是推荐的,如果该类实例需要通过 C# 代码访问,则是必需的。Content
属性定义要显示的信息。*此属性是可选的*。属性的语法目前仅支持字符串。
Separator
不支持任何属性。当前实现不创建单独的控件,而是检查最后一个 StatusBarItem
并将其 Xrw 边框类型设置为 TFrameType.VertRigtDelimiter
。
ViewBox
将由以下方式定义:
Name
属性定义了类实例的名称,可用于唯一标识该类实例。此属性是推荐的,如果该类实例需要通过 C# 代码访问,则是必需的。
my:LocalChessBoardControl
、my:MoveViewer
和 my:LostPiecesControl
节点引用相应的用户控件。
XAML 语法的局限性
X11/Xrw 实现
StatusBarItem
的Content
属性仅限于字符串。Separator
不是一个单独的控件。
代码隐藏 (MainView.xaml.cs)
主视图对应的 C# 代码文件是 MainView.xaml.cs
。它保持初始状态。没有为 MainView 提供额外的功能。
主视图模型文件上下文
MainWindowViewModel.cs
文件保持初始状态。ViewModel 未向应用程序提供任何额外功能。
主模型文件上下文
MainModel.cs
文件保持初始状态。Model 未向应用程序提供任何额外功能。
最简单的用户控件 - LostPiecesControl
XAML (LostPiecesControl.xaml)
用户控件的 XAML 文件是:
<UserControl x:Class="SrcChess2.LostPiecesControl"
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">
<Viewbox Stretch="UniformToFill">
<Border Name="MainBorder">
<UniformGrid Name="CellContainer" Rows="4" Columns="4" MinWidth="0" MinHeight="0">
</UniformGrid>
</Border>
</Viewbox>
</UserControl>
UserControl
的 XML 文件格式与标准 Windows
XAML 文件的格式非常相似。XrwXAML 控件在此用户控件中使用时没有新内容。
代码隐藏 (LostPiecesControl.xaml.cs)
用户控件对应的 C# 代码文件是 LostPiecesControl.xaml.cs
。类构造函数调用 InitializeComponent()
(就像 Windows
控件一样)和 FillCellContainer()
,其中包含创建一些图标(代替丢失的国际象棋棋子)用于演示的代码。
/// <summary>Interaction logic for LostPiecesControl.xaml.</summary>
public partial class LostPiecesControl : UserControl
{
public LostPiecesControl()
{
InitializeComponent();
FillCellContainer();
}
public void FillCellContainer()
{
System.Windows.Media.Imaging.BitmapImage imageSource = null;
System.Windows.Controls.Image imageControl = null;
for (int counter = 0; counter < 5; counter++)
{
string sourceImagePath;
sourceImagePath = System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().Location);
if (counter == 0)
sourceImagePath += System.IO.Path.DirectorySeparatorChar + "Images" +
System.IO.Path.DirectorySeparatorChar + "copy16.bmp";
else if (counter == 1)
sourceImagePath += System.IO.Path.DirectorySeparatorChar + "Images" +
System.IO.Path.DirectorySeparatorChar + "exit16.bmp";
else if (counter == 2)
sourceImagePath += System.IO.Path.DirectorySeparatorChar + "Images" +
System.IO.Path.DirectorySeparatorChar + "info16.bmp";
else if (counter == 3)
sourceImagePath += System.IO.Path.DirectorySeparatorChar + "Images" +
System.IO.Path.DirectorySeparatorChar + "paste16.bmp";
else
sourceImagePath += System.IO.Path.DirectorySeparatorChar + "Images" +
System.IO.Path.DirectorySeparatorChar + "quest16.bmp";
imageSource = new System.Windows.Media.Imaging.BitmapImage(new Uri(sourceImagePath));
imageControl = new System.Windows.Controls.Image();
imageControl.Source = imageSource;
CellContainer.Children.Add(imageControl);
}
}
}
稍复杂的 UserControl - MoveViewer
XAML (MoveViewer.xaml)
用户控件的 XAML 文件是:
<UserControl x:Class="SrcChess2.MoveViewer"
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"
xmlns:my="clr-namespace:SrcChess2">
<UserControl.Resources>
<my:MoveItemList x:Key ="MoveListData" />
</UserControl.Resources>
<Grid Name="gridMoveList" >
<ListView Name="listViewMoveList" HorizontalAlignment="Stretch" VerticalAlignment="Top"
SelectionMode="Single"
ItemsSource="{Binding Source={StaticResource MoveListData}}" IsEnabled="True">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="Step" DisplayMemberBinding="{Binding Path=Step}" Width="60" />
<GridViewColumn Header="Who" DisplayMemberBinding="{Binding Path=Who}" Width="60" />
<GridViewColumn Header="Move" DisplayMemberBinding="{Binding Path=Move}" Width="60" />
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
</Grid>
</UserControl>
此 UserControl
的 XML 文件包含一个 Resource
,就像标准 Windows
XAML 文件一样。XrwXAML 控件在此用户控件中使用时没有新内容。
代码隐藏 (MoveViewer.xaml.cs)
用户控件对应的 C# 代码文件是 LostPiecesControl.xaml.cs
。类构造函数调用 InitializeComponent()
(就像 Windows
控件一样)。
该文件还包含对 XAML 文件 Resource
节点引用的 MoveItemList
类的最小化实现。该类提供了当前比赛已完成的走子列表,并初始化了四个示例走子以供演示。
/// <summary>Move Item.</summary>
public class MoveItem
{
/// <summary>
/// Class Ctor
/// </summary>
/// <param name="step"> Move step</param>
/// <param name="who"> Who did the move</param>
/// <param name="move"> Move</param>
public MoveItem(string step, string who, string move) { Step = step; Who = who; Move = move; }
/// <summary>Step</summary>
public string Step { get; set; }
/// <summary>Who did the move</summary>
public string Who { get; set; }
/// <summary>Move</summary>
public string Move { get; set; }
}
/// <summary>List of moves</summary>
public class MoveItemList : ObservableCollection<MoveItem>
{
public MoveItemList()
{
this.Add(new MoveItem("1a", "White pawn", "D2-D4"));
this.Add(new MoveItem("1b", "Black pawn", "D7-D5"));
this.Add(new MoveItem("2a", "White knight", "B1-C3"));
this.Add(new MoveItem("2b", "Black knight", "B8-C6"));
}
}
/// <summary>User interface displaying the list of moves.</summary>
public partial class MoveViewer : UserControl
{
/// <summary>Initialize a new instance of the SrcChess2.MoveViewer class.</summary>
public MoveViewer()
{
InitializeComponent();
}
}
最后一个 UserControl - ChessBoardControl
XAML (ChessBoardControl.xaml)
用户控件的 XAML 文件是:
<UserControl x:Class="SrcChess2.ChessBoardControl"
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:SrcChess2">
<StackPanel Name="m_stackPanelOuter">
<StackPanel Name="m_stackPanelTop" Height="20" Orientation="Horizontal">
<Border Name="m_placeHolderLeftTop" Width="20" />
<UniformGrid Name="m_uniformGridColumnsTop" Rows="1" Columns="8" Height="16">
<Label Content="a" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
<Label Content="b" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
<Label Content="c" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
<Label Content="d" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
<Label Content="e" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
<Label Content="f" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
<Label Content="g" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
<Label Content="h" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
</UniformGrid>
<Border Name="m_placeHolderRightTop" Width="20" />
</StackPanel>
<StackPanel Name="m_stackPanelCenter" Orientation="Horizontal">
<UniformGrid Name="m_uniformGridRowsLeft" Rows="8" Columns="1" Width="16" >
<Label Content="8" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
<Label Content="7" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
<Label Content="6" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
<Label Content="5" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
<Label Content="4" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
<Label Content="3" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
<Label Content="2" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
<Label Content="1" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
</UniformGrid>
<Grid Name="m_gridCellContainer" MinWidth="100" MinHeight="100">
<Grid.RowDefinitions>
<RowDefinition Height="*" SharedSizeGroup="A" />
<RowDefinition Height="*" SharedSizeGroup="A" />
<RowDefinition Height="*" SharedSizeGroup="A" />
<RowDefinition Height="*" SharedSizeGroup="A" />
<RowDefinition Height="*" SharedSizeGroup="A" />
<RowDefinition Height="*" SharedSizeGroup="A" />
<RowDefinition Height="*" SharedSizeGroup="A" />
<RowDefinition Height="*" SharedSizeGroup="A" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" SharedSizeGroup="A" />
<ColumnDefinition Width="*" SharedSizeGroup="A" />
<ColumnDefinition Width="*" SharedSizeGroup="A" />
<ColumnDefinition Width="*" SharedSizeGroup="A" />
<ColumnDefinition Width="*" SharedSizeGroup="A" />
<ColumnDefinition Width="*" SharedSizeGroup="A" />
<ColumnDefinition Width="*" SharedSizeGroup="A" />
<ColumnDefinition Width="*" SharedSizeGroup="A" />
</Grid.ColumnDefinitions>
</Grid>
<UniformGrid Name="m_uniformGridRowsRight" Rows="8" Columns="1" Width="16" >
<Label Content="8" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
<Label Content="7" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
<Label Content="6" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
<Label Content="5" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
<Label Content="4" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
<Label Content="3" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
<Label Content="2" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
<Label Content="1" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
</UniformGrid>
</StackPanel>
<StackPanel Name="m_stackPanelBottom" Height="20" Orientation="Horizontal">
<Border Name="m_placeHolderLeftBottom" Width="20" />
<UniformGrid Name="m_uniformGridColumnsBottom" Rows="1" Columns="8" Height="16">
<Label Content="a" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
<Label Content="b" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
<Label Content="c" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
<Label Content="d" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
<Label Content="e" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
<Label Content="f" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
<Label Content="g" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
<Label Content="h" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Padding="0" />
</UniformGrid>
<Border Name="m_placeHolderRightBottom" Width="20" />
</StackPanel>
</StackPanel>
</UserControl>
XrwXAML 控件在此用户控件中使用时没有新内容。
代码隐藏 (ChessBoardControl.xaml.cs)
用户控件对应的 C# 代码文件是 ChessBoardControl.xaml.cs
。类构造函数调用 InitializeComponent()
(就像 Windows
控件一样)和 InitCell()
,其中包含使用 64 个 Border
控件和交替背景色创建 64 个棋盘格的代码。
该文件还包含大量代码,用于提供依赖属性和定义当前未使用的类属性。因此,我将重点介绍 InitCell()
。
/// <summary>Chess board for the chess program.</summary>
public partial class ChessBoardControl : UserControl
{
...
/// <summary>Initializing constructor.</summary>
public ChessBoardControl ()
{
InitializeComponent();
//...
InitCell();
}
...
/// <summary>
/// Initialize the cell
/// </summary>
private void InitCell()
{
int iPos = 36;
Border border;
Brush brushDark;
Brush brushLite;
brushDark = new SolidColorBrush(DarkCellColor); // m_colorInfo.m_colDarkCase);
brushLite = new SolidColorBrush(LiteCellColor); // m_colorInfo.m_colLiteCase);
for (int y = 0; y < 8; y++)
{
for (int x = 0; x < 8; x++)
{
border = new Border ();
border.Name = "Cell" + ("00" + iPos.ToString()).Right (2);
border.BorderThickness = new Thickness(0);
border.Background = (((x + y) & 1) == 0) ? brushLite : brushDark;
border.BorderBrush = border.Background;
m_gridCellContainer.Children.Add(border);
#if WINDOWS
// Microsoft requires any initial size,
// big enough to span the grid over the complete free space...
border.Width = 40;
#else
// Linux needs to set child constraints.
m_gridCellContainer.SetChildConstraints (border, "0", x.ToString(), y.ToString(), "0", "0");
#endif
iPos--;
}
}
}
}
条件编译符号 WINDOWS 仅为 Windows 8.1 项目设置。它排除了 X11 和 Windows 8.1 版本示例应用程序之间目前唯一的差异。
关注点
这是另一个适用于 X11 的 XAML 应用程序,几乎完全兼容 Microsoft®,展示了这种方法的最大优点:100% 兼容的 GUI 定义和用于创建 GUI 的代码行数节省。
用户控件的使用可以将复杂的 GUI 分割成更简单、更易于维护的部分,并且同样适用于 X11。
历史
本文初稿写于 2015 年 7 月 16 日。