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

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.43/5 (3投票s)

2015 年 8 月 16 日

CPOL

11分钟阅读

viewsIcon

18640

downloadIcon

433

目前,主流的 Linux/Unix (X11) GUI 应用程序框架(GTK+, KDE)都不支持 XAML 应用程序开发。Moonlight 项目(包括 XAML 支持)已于 2012 年 5 月 29 日停止开发。本文回顾了一个利用 WPF UserControls 的 XAML 应用程序。

引言

本文是一篇案例研究,介绍如何使用 **Roma Widget Set** (Xrw) 来编写一个基于 MVVM (Model View ViewModel) 设计模式的 X11 应用程序(利用 WPF UserControls),并使用 XAMLRoma 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 将三个用户控件组织起来以进行演示。它们是:

  1. ChessBoardControl 用户控件,定义在 ChessBoardControl.xaml 中,显示一个空棋盘,周围有一个显示字段名称的边框。棋盘和边框由几个 StackPanelUniformGridGrid 布局控件实现,从而使棋盘可以根据当前窗口大小调整大小。棋盘用户控件通过 Viewbox 嵌入到应用程序中,该 Viewbox 向两个方向均匀拉伸。这确保了无论应用程序窗口具有何种特定几何形状,棋盘始终显示为正方形。
  2. MoveViewer 用户控件,定义在 MoveViewer.xaml 中,显示当前比赛已完成的走子列表。此列表使用数据绑定到 MoveListData 类。
  3. 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 文件,其中包含对四个用户控件(ChessBoardControlMoveViewer 和 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 支持 StatusBarItemSeparator 子控件。

StatusBarItem 将由以下方式定义:

  • Name 属性定义了类实例的名称,可用于唯一标识该类实例。此属性是推荐的,如果该类实例需要通过 C# 代码访问,则是必需的。
  • Content 属性定义要显示的信息。*此属性是可选的*。属性的语法目前仅支持字符串。

Separator 不支持任何属性。当前实现不创建单独的控件,而是检查最后一个 StatusBarItem 并将其 Xrw 边框类型设置为 TFrameType.VertRigtDelimiter

ViewBox 将由以下方式定义:

  • Name 属性定义了类实例的名称,可用于唯一标识该类实例。此属性是推荐的,如果该类实例需要通过 C# 代码访问,则是必需的。

my:LocalChessBoardControlmy:MoveViewermy:LostPiecesControl 节点引用相应的用户控件。

XAML 语法的局限性

X11/Xrw 实现

  • StatusBarItemContent 属性仅限于字符串。
  • 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 日。

© . All rights reserved.