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

为 X11 和 Windows 编写 XAML 应用程序以创建程序集的 HTML 帮助

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2017 年 3 月 17 日

CPOL

15分钟阅读

viewsIcon

14627

downloadIcon

427

本文介绍了一个小型工具(远比 Sandcastle 帮助文件生成器简单),用于为 .NET/MONO 程序集创建 HTML 帮助,并回顾了使用 Roma Widget Set XrwXAML 创建 GUI 应用程序的 XAML 方法。

下载完整的 X11_32 项目,旧版本:0.100.20
下载完整的 X11_64 项目,旧版本:0.100.20
下载完整的 Win10_64 项目,旧版本:0.100.20

 下载 XamlDocer_V0.30_X11_32.zip 包含完整源代码和可执行文件的 Mono 解决方案
 下载 XamlDocer_V0.30_X11_64.zip 包含完整源代码和可执行文件的 Mono 解决方案
 下载 XamlDocer_V0.30_Win10.zip 包含完整源代码和可执行文件的 Visual Studio 2015 解决方案

引言

激励

由于 Sandcastle 仅限于 Windows® 平台,对于使用 MONO 进行编程的 Linux/Unix 用户来说,缺少一个可以从 .NET/MONO 程序集的反射信息和源代码中的 XML 文档注释自动生成代码文档的文档生成器。

这款名为 **XamlDocer** 的应用程序可能会弥补这一不足。我将尝试逐步增强 XamlDocer,使其成为一个功能齐全的工具——如果您感兴趣,请继续关注更新。

这里还有一些支持 Unix/Linux 和 C# 的替代方案,不应在此省略:

  • Doxygen(使用不同的注释语法)
  • Natural Docs(使用不同的注释语法)
  • NDoc(似乎已不活跃一段时间)

如果您只是想为您的目的测试/使用这个小工具,请转到 “用法” 章节。

除了协助您的日常开发

本文也是一个案例研究,介绍了如何使用 XAML 和 **Roma Widget Set** (Xrw) 编写一个基于 MVVM(模型-视图-视图模型)设计模式的 X11/Windows(跨平台)应用程序(利用 .NET 反射为 .NET/MONO 程序集生成 HTML 帮助)。Roma Widget Set 是一个零依赖的 GUI 应用程序框架,适用于 X11(它只需要免费的 Mono 标准安装程序集和免费的 X11 发行版库;它不特别需要 GNOME、KDE 或商业库),并且完全用 C# 实现。

本文延续了以下作品:为 X11 编写 XAML 对话框应用程序为 X11 编写 XAML 功能区应用程序为 X11 编写具有大量数据绑定和零代码的 XAML 应用程序为 X11 编写 XAML 计算器应用程序为 X11 编写具有几何对象(形状)的 XAML 应用程序为 X11 编写带 UserControls 的 XAML 应用程序为 X11 和 Windows 编写 XAML 7 段 LCD 显示屏 UserControl为 X11 和 Windows 编写 XAML 扫雷游戏为 X11 和 Windows 编写 XAML 应用程序,绘制棋盘以演示 DrawingBrush。据我所知,这是在 Moonlight 被放弃后,首次尝试使用 XAML 进行 X11 应用程序开发(利用 Xrw)。

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

鉴于这是第十次尝试使用 XAML 进行 X11 应用程序开发并取得了成功,后续关于使用 Roma Widget Set 在 X11 上使用 XAML 的文章将源源不断地出现。

背景

XamlDocer 的**动机**和一般**概念**已在“为 X11 编写 XAML 对话框应用程序”一文中进行了说明。

**RelayCommand** 方法和 **ViewModel** 类的使用已在“为 X11 编写 XAML 功能区应用程序”一文中介绍。

自 Xrw 版本 0.9 发布以来,`DependencyProperty` 绑定已完全重构,并且此演示应用程序利用新绑定来实现 View 和 ViewModel 之间的完整通信。与旧的 Xrw XAML 演示应用程序不同,View 和 ViewModel 仅通过 XAML 绑定进行通信——`XrwXAML.ViewModelCore<MainView>` 类已从 Xrw 版本 1.0 中移除,并且 ViewModel 不再回溯到 View。

此演示应用程序是“编写 XAML...”系列文章中第一个产生有意义结果的应用程序。正是因为这个原因,以及今后添加许多精彩功能的可能性,我认为本文在可预见的未来会得到一些更新,例如:

  • 检测程序集平台是否与运行平台匹配(Win32/x64/AnyCPU),因为程序集必须可加载。
  • 正确处理运算符 (op_) 和类构造函数 (cctor)。
  • 记忆应用程序设置
  • ...

焦点

鉴于前九篇“编写 XAML...”文章已经演示了多种 MVVM 技术,本文将不提供一个通用示例,而是提供一个利用 XAML 为 X11 的有用工具,用于

  • 为 .NET/MONO 程序集生成 HTML 帮助。

使用代码

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

  • **XamlDocer** 包含示例应用程序的源代码。
  • **XamlDocerTestAssembly** 包含测试程序集的源代码(从 0.20 版本开始)。
  • 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** 程序集版本 1.0(此解决方案中包含一个 1.0 RC 版本),它定义了 Xlib/X11 调用 libX11.so 的函数原型、结构和类型。该程序集是为“使用 Mono Develop 编程 Xlib - 第 1 部分:低级(概念验证)”项目开发的,并在“使用 Roma Widget Set(C# X11)- 一个零依赖的 GUI 应用程序框架 - 基础”项目期间得到改进。

GUI 框架基于 **Xrw** 程序集版本 1.0(此解决方案中包含一个 1.0 RC 版本),它定义了 XAML 代码中使用的控件/小部件及其包装类(应尽可能接近 Microsoft® 原版)。该程序集是在“使用 Roma Widget Set(C# X11)- 一个零依赖的 GUI 应用程序框架 - 基础”项目期间开发的。

 

0.30 版本更新

**X11Wrapper** 程序集现在是即将发布的 1.1 版本的早期预览版。已添加一些 X11 API 包装器,但未进行重大更改。

**Xrw** 程序集现在是即将发布的 1.1 版本的早期预览版。已进行一些根本性更改:

  • XrwXAML 控件不再基于 Xrw 小部件。
  • 每个应用程序窗口仅基于一个 X11 窗口,XrwXAML 控件避免使用 X11 窗口。
  • 渲染现在基于 Cairo,而不是 X11。
 

示例应用程序大量利用反射来确定要记录的程序集提供的 API,并从程序集的 XML 文档文件中添加其他文档。

Code Project 上有一篇由 keesari_anjaiah 撰写的优秀文章《.NET 中的反射》,介绍了此应用程序中使用的所有技术。

XML 文档文件的创建是 MonoDevelop 和 Visual Studio 的标准功能,必须显式启用。

为了使用 HTML 文档生成器获得目前最好的结果,应同时提供程序集文件和 XML 文档文件。

平台独立性

下面几张示例应用程序的截图显示了 XamlDocer 0.10 版本在多个平台上的运行情况。

注意:示例应用程序的后期版本可能具有更高级的界面。

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

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

第三张图片显示了该示例应用程序在 Windows® 10 64 位版本上的运行情况。

应用程序窗口使用 `System.Windows.Controls.DockPanel` 作为其根布局管理器。它包含子元素 `System.Windows.Controls.Menu`(菜单,停靠:顶部)、`System.Windows.Controls.TextBlock`(用于输出,停靠:底部)和 `System.Windows.Controls.Grid`(用于操作区域,停靠:中心)。

示例应用程序的 X11 版本和 Windows® 10 版本之间没有功能上的差异,它们共享相同的 XAML 代码和后端代码。

逐步演示

主视图文件上下文

XAML (MainWindow.xaml)

这是 XAML 文件(X11 32 位 MonoDevelop 解决方案)的完整代码。

<Window x:Class="XamlDocer.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:XamlDocer"
    mc:Ignorable="d"
    Title="Documentation processor" Height="350" Width="525">
  <Window.Resources>
    <local:MainWindowViewModel x:Key="MainViewModel" />
  </Window.Resources>
  <DockPanel Name="MainDock" DataContext="{StaticResource MainViewModel}">
    <Menu x:Name="menuMain" DockPanel.Dock="Top">
      <MenuItem x:Name="menuitemFile" Header="_File">
        <MenuItem x:Name="menuitemFileExit" Header="Exit"
                  Command="{Binding MenuitemFileExit_Command}" CommandParameter="null" >
          <MenuItem.Icon>
            <Image Source="Images/exit16.bmp"/>
          </MenuItem.Icon>
        </MenuItem>
      </MenuItem>
      <MenuItem x:Name="menuitemConfig" Header="_Config"></MenuItem>
      <MenuItem x:Name="menuitemHelp" Header="_?">
        <MenuItem x:Name="menuitemQmarkHelp" Header="Help"
                  Command="{Binding MenuitemQmarkHelp_Command}" CommandParameter="null" >
          <MenuItem.Icon>
            <Image Source="Images/quest16.bmp"/>
          </MenuItem.Icon>
        </MenuItem>
        <MenuItem x:Name="menuitemQmarkAbout" Header="About"
                  Command="{Binding MenuitemQmarkAbout_Command}" CommandParameter="null" >
          <MenuItem.Icon>
            <Image Source="Images/info16.bmp"/>
          </MenuItem.Icon>
        </MenuItem>
      </MenuItem>
    </Menu>
    <TextBlock Name="textblockStatus" DockPanel.Dock="Bottom" Height="18" Background="Silver"
               Text="{Binding StatusText}" />
    <Grid Name="gridActionArea" Background="#FFF0F0F0" >
      <Grid.ColumnDefinitions>
        <ColumnDefinition Width="6"/><!-- Left padding. -->
        <ColumnDefinition Width="3.0*"/>
        <ColumnDefinition Width="10"/><!-- Col 1-2 padding. -->
        <ColumnDefinition Width="1.0*"/>
        <ColumnDefinition Width="6"/><!-- Right padding. -->
      </Grid.ColumnDefinitions>
      <Grid.RowDefinitions>
        <RowDefinition Height="4"/><!-- Row padding. -->
        <RowDefinition Height="24"/>
        <RowDefinition Height="8"/>
        <RowDefinition Height="24"/>
        <RowDefinition Height="16"/><!-- Row padding. -->
        <RowDefinition Height="24"/>
        <RowDefinition Height="8"/>
        <RowDefinition Height="24"/>
        <RowDefinition Height="16"/><!-- Row padding. -->
        <RowDefinition Height="24"/>
        <RowDefinition Height="8"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="4"/><!-- Row padding. -->
      </Grid.RowDefinitions>
      <Label x:Name="labelDllFile" Content="Assembly DLL file (mandatory):"
             Grid.Column="1" Grid.Row="1" />
      <Button Name="buttonDllFile" Grid.Row="1" Grid.Column="3" Content="..."
              Command="{Binding ActionareaDllFile_Command}" CommandParameter="null" ></Button>
      <TextBlock x:Name="textblockDllFile" Grid.Column="1" Grid.Row="3" Grid.ColumnSpan="3"
              TextWrapping="NoWrap" Text="{Binding DllFilePath}" Background="#FFF6F6F6" />
      <Label x:Name="labelXmlFile" Content="Assembly XML file (optional):"
             Grid.Column="1" Grid.Row="5" />
      <Button Name="buttonXmlFile" Grid.Row="5" Grid.Column="3" Content="..."
              Command="{Binding ActionareaXmlFile_Command}" CommandParameter="null" ></Button>
      <TextBlock x:Name="textblockXmlFile" Grid.Column="1" Grid.Row="7" Grid.ColumnSpan="3"
                 TextWrapping="NoWrap" Text="{Binding XmlFilePath}" Background="#FFF6F6F6" />
      <Button Name="buttonCompile" Grid.Row="9" Grid.Column="3" Content="Compile" FontSize="14"
              FontWeight="Bold" Command="{Binding ActionareaCompile_Command}" CommandParameter="null"
              IsEnabled="{Binding EnableCompile}"></Button>
      <Label x:Name="labelCompile" Content="Compile results:" Grid.Column="1" Grid.Row="9" />
      <TextBlock Name="textboxCompile" Grid.Row="11" Grid.Column="1" Grid.ColumnSpan="3"
                 Background="#FFF6F6F6" Text="{Binding Output}" />
    </Grid>
  </DockPanel>
</Window>

GUI 和后端代码之间的所有交互都通过绑定实现。`MenuItem` 和两个 `Button` 使用 `RelayCommand` 实例,`TextBlock` 绑定到 `String` 属性。绑定源通常定义为

<DockPanel Name="MainDock" DataContext="{StaticResource MainViewModel}">

XAML 语法的局限性

X11/Xrw 实现

  • 目前,如果 `MenuItem` 没有子项,它将无法编译访问限定符(例如:``),因此会显示例如 **_Config** 而不是 **Config**。
  • 即将发布的 Xrw 程序集版本 1.1 的早期预览版不支持带图标的菜单项。

后端代码 (MainWindow.xaml.cs)

主视图对应的 C# 代码文件是 `MainWindow.xaml.cs`,其中只包含最少的代码。

using System;
using System.Diagnostics;

using X11;
using Xrw;
using XrwXAML;

// Replica
using Microsoft.Win32;
using System.Windows;
using System.Windows.Controls;

namespace XamlDocer
{
  public partial class MainWindow : XrwXAML.Window
  {

    /// <summary>The default constructor.</summary>
    public MainWindow ()
      : base (-1, -1)
    {
      InitializeComponent ();
    }

  }

}

主视图模型文件上下文

`MainWindowViewModel.cs` 文件包含所有连接到应用程序菜单项和按钮的“RelayCommands”以及所有连接到文本块的“String”属性。完整的源代码包含近 2000 行代码和注释。我只会挑选并讨论一些片段……

第一:绑定到菜单项和按钮的 RelayCommands。

#region MenuItem relay properties

/// <summary>Provide the relay command for MenuitemFileExit.</summary>
public System.Windows.Input.ICommand MenuitemFileExit_Command
{ get { return _menuitemFileExit_Command; } }

/// <summary>Provide the relay command for MenuitemQmarkHelp.</summary>
public System.Windows.Input.ICommand MenuitemQmarkHelp_Command
{ get { return _menuitemQmarkHelp_Command; } }

/// <summary>Provide the relay command for MenuitemQmarkAbout.</summary>
public System.Windows.Input.ICommand MenuitemQmarkAbout_Command
{ get { return _menuitemQmarkAbout_Command; } }

#endregion MenuItem relay properties

#region ActionArea relay properties

/// <summary>Provide the relay command for ActionareaDllFile.</summary>
public System.Windows.Input.ICommand ActionareaDllFile_Command
{ get { return _actionareaDllFile_Command; } }

/// <summary>Provide the relay command for ActionareaXmlFile.</summary>
public System.Windows.Input.ICommand ActionareaXmlFile_Command
{ get { return _actionareaXmlFile_Command; } }

/// <summary>Provide the relay command for ActionareaCompile.</summary>
public System.Windows.Input.ICommand ActionareaCompile_Command
{ get { return _actionareaCompile_Command; } }

#endregion ActionArea relay properties               

第二:绑定到文本块的属性。

#region View bindable properties

/// <summary>Get or set the path of the DLL file to compile XML documentation from.</summary>
/// <value>The path of the DLL file to compile XML documentation from.</value>
/// <remarks>Since the MVVM ViewModel doesn't know about the view -
/// view interaction is realized via binding.</remarks>
public string DllFilePath
{   get    {    return _dllFile;        }
    set    {    _dllFile = value;        OnPropertyChanged("DllFilePath");    }
}

/// <summary>Get or set the path of the XML file to compile XML documentation from.</summary>
/// <value>The path of the XML file to compile XML documentation from.</value>
/// <remarks>Since the MVVM ViewModel doesn't know about the view -
/// view interaction is realized via binding.</remarks>
public string XmlFilePath
{   get    {    return _xmlFile;        }
    set    {    _xmlFile = value;        OnPropertyChanged("XmlFilePath");    }
}

/// <summary>Get or set the flag defining whether all prereqisits to compile are met.</summary>
/// <value>The flag defining whether all prereqisits to compile are met.</value>
/// <remarks>Since the MVVM ViewModel doesn't know about the view -
/// view interaction is realized via binding.</remarks>
public bool EnableCompile
{   get    {    return _enableCompile;    }
    set    {    _enableCompile = value;    OnPropertyChanged("EnableCompile");    }
}

/// <summary>Get or set the log output.</summary>
/// <value>The log output.</value>
/// <remarks>Since the MVVM ViewModel doesn't know about the view -
/// view interaction is realized via binding.</remarks>
public string Output
{   get    {    return _output;        }
    set    {    _output = value;            OnPropertyChanged("Output");    }
}

/// <summary>Get or set the status bar text.</summary>
/// <value>The status bar text.</value>
/// <remarks>Since the MVVM ViewModel doesn't know about the view -
/// view interaction is realized via binding.</remarks>
public string StatusText
{   get    {    return _statusText;        }
    set    {    _statusText = value;        OnPropertyChanged("StatusText");    }
}

#endregion View bindable roperties

第三:`MainWindowViewModel` 的 `INotifyPropertyChanged` 实现。

#region INotifyPropertyChanged Members

/// <summary>Notify listeners about property value change.</summary>
/// <param name="propertyName">The <see cref="System.String"/> name
/// of the value changed property.</param>
private void OnPropertyChanged(string propertyName)
{
   if (PropertyChanged != null)
   {
       PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
   }
}

#endregion INotifyPropertyChanged Members

HTML 文档的创建分三个阶段进行:

  1. 从程序集 DLL 文件读取成员。
  2. 从程序集的 XML 文档文件中读取其他成员信息。
  3. 编写 HTML 文档文件。

为了在此期间存储成员信息,使用了 `DocerTree` 类。它被实现为一个单例,可以保存任何类型的 `DocerNode` 来构建树结构,但实际上根节点是 `DocerNodeType.Assembly` 类型。

第一个阶段——从程序集 DLL 文件读取成员——最初会创建一个 `DocerNodeType.Assembly` 类型的 `DocerNode`,并将程序集 DLL 文件中找到的所有成员添加到该 `DocerNode` 的子列表中。通常,第一代子节点是 `DocerNodeType.Namespace` 类型的 `DocerNode`。这些 `DocerNode` 节点反过来包含第二代子节点,通常是 `DocerNodeType.Namespace` 或 `DocerNodeType.Type` 类型。`DocerNodeType.Namespace` 类型的 `DocerNode` 节点总是可以拥有下一代子节点。`DocerNodeType.Type` 类型的 `DocerNode` 节点始终是树结构的叶节点,代表类、枚举等,包括它们的字段、构造函数、属性、方法等。

第四:从程序集 DLL 文件读取成员。

/// <summary>Read the members from the library DLL file into
/// one <see cref="OpfDocer.DocerTree"/> instance.</summary>
/// <param name="assembly">The XML node, that contains the members to read.</param>
private void ReadMembers(Assembly assembly)
{
  DocerTree dt = DocerTree.Instance;

  if (assembly == null)
    return;
 
  try
  {
    DocerNode dnAssembly = dt.FindOrCreateAssemblyDocerNode (assembly.ManifestModule.Name);
    if (dnAssembly != null)
    {
      StatusText = "Read assembly file '" + DllFilePath + "' for type details.";
      foreach(Type type in assembly.GetTypes())
      {   
        if (!type.FullName.Contains ("PrivateImplementationDetails") &&
            !type.FullName.Contains ("$ArrayType$"))
        {
          string beautifiedName = type.FullName.Replace("+", ".");
          beautifiedName = DocerNode.ToDisplayFriendlyName (beautifiedName).Replace("System.", "");
          
          DocerNode dn = dnAssembly.FindOrCreateTypeDocerNode (beautifiedName);
          if (dn != null)
          {
            // Store it...
            
            string interfaces = "";
            foreach (Type interfaceType in type.GetInterfaces())
            {
              if (interfaces == "")
                interfaces += interfaceType.Name;
              else
                interfaces += ", " + interfaceType.Name;
            }
            dn.Interfaces = DocerNode.ToDisplayFriendlyName (interfaces);
              
            foreach (FieldInfo fieldInfo in type.GetFields (
                         BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public))
            {
              // Store it...
            }
            foreach (FieldInfo fieldInfo in type.GetFields (
                         BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic))
            {
              // Store it...
            }
            dn.FieldMember.Sort (delegate(TypeMemberDocerNode d1, TypeMemberDocerNode d2)
            {  return d1.Name.CompareTo(d2.Name);  });
            
            foreach (PropertyInfo propertyInfo in type.GetProperties(
                         BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public))
            {
              // Store it...
            }
            foreach (PropertyInfo propertyInfo in type.GetProperties(
                         BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic))
            {
              // Store it...
            }
            dn.PropertyMember.Sort (delegate(TypeMemberDocerNode d1, TypeMemberDocerNode d2)
                {  return d1.Name.CompareTo(d2.Name);  });
            
            foreach (ConstructorInfo constructorInfo in type.GetConstructors(
                         BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public))
            {
              // Store it...
            }
            foreach (ConstructorInfo constructorInfo in type.GetConstructors(
                         BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic))
            {
              // Store it...
            }
            dn.MethodMember.Sort (delegate(TypeMemberDocerNode d1, TypeMemberDocerNode d2)
                {  return d1.Name.CompareTo(d2.Name);  });
            
            foreach (MethodInfo methodInfo in type.GetMethods (
                         BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public))
            {
              // Store it...
            }
            foreach (MethodInfo methodInfo in type.GetMethods (
                         BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic))
            {
              if (methodInfo.Name.StartsWith ("get_") || methodInfo.Name.StartsWith ("set_"))
                continue;
              
              // Store it...
            }
            dn.MethodMember.Sort (delegate(TypeMemberDocerNode d1, TypeMemberDocerNode d2)
                {  return d1.Name.CompareTo(d2.Name);  });
            
            foreach (EventInfo eventInfo in type.GetEvents (
                         BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public))
            {
              // Store it...
            }
            foreach (EventInfo eventInfo in type.GetEvents (
                         BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic))
            {
              // Store it...
            }
            dn.EventMember.Sort (delegate(TypeMemberDocerNode d1, TypeMemberDocerNode d2)
                {  return d1.Name.CompareTo(d2.Name);  });
          }
        }
      }
    }
  }
  catch (Exception ex)
  {
    if (!string.IsNullOrEmpty (Output))
      Output += "\n" + "ERROR " + ex.Message + "\n" + ex.StackTrace;
    else
      Output = "ERROR " + ex.Message + "\n" + ex.StackTrace;
    Console.WriteLine (ex.Message + "\n" + ex.StackTrace);
  }
}

第二阶段——从程序集的 XML 文档文件中读取其他成员信息——不会创建任何新的 `DocerNode`。它只是在树结构中搜索匹配的节点并向其添加成员信息。这些 XML 注释被评估:

  • 对于类型(枚举、类等):“code”、“example”、“summary”、“remarks”。
  • 对于类型的字段:“code”、“example”、“summary”、“remarks”。
  • 对于类型的属性:“code”、“example”、“exception”、“summary”、“remarks”、“value”。
  • 对于类型的方法:“code”、“example”、“exception”、“param”、“summary”、“remarks”、“returns”。

第三阶段——编写 HTML 文档文件——创建 `/images` 和 `/members` 文件夹并编写文件。

 

0.30 版本更新

项目设置(代表应用程序的当前用户或应用程序/所有应用程序用户存储字符串、图像等)是 Visual Studio 特有的。下图显示了空的“项目设置”页面。

  

为了提供平台无关的实现,应用程序需要为 Mono Develop 提供类似的功能。为了实现这一点,提供了 `SystemSettings` 类。

`SystemSettings` 类用于在设置更改时立即存储实际设置,并在应用程序启动时恢复最近的设置。

`SystemSettings` 类利用 `SystemFolder` 类来访问用户的私有属性路径和应用程序的全局属性路径。

 

用法

页面顶部的 XamlDocer 示例应用程序下载链接支持:

  • X11_32 - 应用程序编译目标和用于创建 XML 文档的程序集文件必须是:Unix/Linux 32 位。
  • X11_64 - 应用程序编译目标和用于创建 XML 文档的程序集文件必须是:Unix/Linux 64 位。
  • Win10 - 应用程序编译目标和用于创建 XML 文档的程序集文件必须是:Windows Win32、x64 和 AnyCPU,由选定的目标平台决定(已包含 x64 的编译)。

使用 XamlDocer 应用程序创建 HTML 帮助

  1. 准备一个工作文件夹。
  2. 将程序集文件和 XML 文档文件复制到文件夹中。
  3. 确保您已为正确的平台 x32/x64/AnyCPU 编译/选择了 XamlDocer(由于 XamlDocer 将要为其创建 HTML 文档的程序集文件加载到内存中,因此平台必须匹配)。
  4. 启动 XamlDocer 应用程序并选择程序集文件(XML 文档文件将自动识别)。
  5. 如果 XML 文档文件未自动识别,则可以选择它。
  6. 应用设置(从 0.20 版本开始,有包含/排除非公共类型/属性/方法/字段/事件的设置)。
  7. 运行 HTML 文档的创建。

下图显示了 XamlDocer 的当前版本。


  4.

  5.

  6.


  7.

 









 

 

0.30 版本更新

“显示/隐藏继承的成员对象”属性按钮现已激活。

 

下图显示了一个示例文件夹,其中包含提供的程序集文件和 XML 文档文件(X11Wrapper.dll 和 X11Wrapper.xml)以及生成的 HTML 文档(_index.html、/members 和 /images),运行环境为 OPEN SUSE 11.3 Linux 32 位 EN 和 GNOME 桌面。

 

X11wrapper.dll 和 X11Wrapper.xml 的 HTML 文档创建结果(从 _index.html 文件开始)如下所示:

HTML 文档语言

Microsoft 的 XML 文档语言 描述 分成多个简短的文章,非常技术性地解释了各个方面。此外,我强烈推荐 Code Project 上 Michael D Elliott 的精彩图文并茂的文章《C# 和 XML 源代码文档》。

尽管如此,我还是想提及一些方面:

  • XamlDocer 支持命名空间文档,就像 NDocSandcastle 一样:通过向命名空间添加一个注释过的 `internal class NamespaceDoc {}`。我通常在此类上使用 `/// <summary></summary>` 和 `/// <remarks></remarks>` 标签。
  • 我通常使用 `</c>` 标签进行注释。(如果手动输入 ``,则 `</c>` 标签足够短,可以手动完全输入。)对于 Mono Develop 项目,将所有用作参数或返回值的类的命名空间包含进来,并引用类而不带命名空间是一个好做法。例如:
using System.Windows;

...

// <param name="hitTest">The <see cref="Point"/> for the hit test.</param>

结果如下:       *Param: hitTest       用于命中测试的 **System.Windows.Point**。*

  • 我通常按照此方案注释参数和返回值:
<code>/// <returns>Returns <see cref="Boolean"/> <c>true</c> if the top-level
/// header has to open it's sub-menu on mouse enter.</returns></code>

结果如下:       *Return                 返回 **System.Boolean** **true**,如果顶级……*

HTML 文档主题

目前只有一个目标主题,HTML 文档是写入到这个主题的。它(与 Microsoft 的标准不同)在一个 HTML 文件(页面)中提供了同一类型的所有成员。这样更紧凑,但可能会重复写入相同的内容,例如,如果一个成员在基类及其所有派生类中都有描述。

当前目标主题创建了 HTML 文档章节“类型标题”、“类型摘要”、“命名空间”、“程序集”、“继承树”、“接口”和“构造函数”,如下所示……

构造函数的参数是文件的一部分——无需跟踪链接即可获取完整信息。

当前目标主题创建了 HTML 文档章节“属性”和“方法”,如下所示……

方法的参数和返回值是文件的一部分——无需跟踪链接即可获取完整信息。

当前目标主题创建了 HTML 文档章节“字段”、“事件”、“备注”和“示例”,如下所示……

已修复的错误

0.2 版本已修复

  • “继承层次结构”部分没有显示继承层次结构,而是显示了包含层次结构。
  • 指向父/子类型的链接已断开。缺少 `.html` 扩展名。
  • 类型成员的可访问性(公共/非公共)曾被“猜测”,现在通过 `BindingFlag`s 正确确定。
  • 类型成员(构造函数和方法)提供了指向详细页面的链接,即使详细页面尚未编写。

0.3 版本已修复

  • 生成帮助中的链接,包含缩略图,现在会抑制图像边框。
  • 显示/隐藏非公共类型的属性错误现已修复。它没有效果。

改进

0.2 版本已改进

  • 切换非公共类型和类型成员(属性、方法、字段和事件)的文档的开/关。

0.3 版本已改进

  • 应用程序会记住上次的选择,并在重新启动后恢复。
  • “显示/隐藏继承的成员对象”属性按钮现已激活。

计划中

  • [已实现] 应用程序会记住上次的选择,并在重新启动后恢复。
  • 支持替代图标图像。
  • 包含非生成页面。

关注点

这是另一个用于 X11 的 XAML 应用程序,完全兼容 Microsoft®,展示了这种方法的主要优势(与使用 GTK+ 或 KDE 实现相比):100% 跨平台兼容的 GUI 定义和创建 GUI 的代码行节省。

我希望您喜欢这个 HTML 文档生成器的早期阶段,并希望,即使它远未达到发布状态,

  • 它可能对您有所帮助(通过调整和完善 XML 注释,以便将来获得出色的文档),
  • 它能激发您对下一步的兴趣,并且
  • 它**能启发您提供反馈**。

历史

本文的第一个版本来自 2017 年 3 月 17 日。

本文的第二个版本来自 2017 年 3 月 24 日。

本文的第三个版本来自 2017 年 6 月 18 日。

© . All rights reserved.