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





5.00/5 (4投票s)
本文介绍了一个小型工具(远比 Sandcastle 帮助文件生成器简单),用于为 .NET/MONO 程序集创建 HTML 帮助,并回顾了使用 Roma Widget Set XrwXAML 创建 GUI 应用程序的 XAML 方法。
下载完整的 X11_32 项目,旧版本:0.10,0.20
下载完整的 X11_64 项目,旧版本:0.10,0.20
下载完整的 Win10_64 项目,旧版本:0.10,0.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 版本的早期预览版。已进行一些根本性更改:
|
示例应用程序大量利用反射来确定要记录的程序集提供的 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` 没有子项,它将无法编译访问限定符(例如:`
- 即将发布的 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 文档的创建分三个阶段进行:
- 从程序集 DLL 文件读取成员。
- 从程序集的 XML 文档文件中读取其他成员信息。
- 编写 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 帮助
- 准备一个工作文件夹。
- 将程序集文件和 XML 文档文件复制到文件夹中。
- 确保您已为正确的平台 x32/x64/AnyCPU 编译/选择了 XamlDocer(由于 XamlDocer 将要为其创建 HTML 文档的程序集文件加载到内存中,因此平台必须匹配)。
- 启动 XamlDocer 应用程序并选择程序集文件(XML 文档文件将自动识别)。
- 如果 XML 文档文件未自动识别,则可以选择它。
- 应用设置(从 0.20 版本开始,有包含/排除非公共类型/属性/方法/字段/事件的设置)。
- 运行 HTML 文档的创建。
下图显示了 XamlDocer 的当前版本。
![]() |
|
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 支持命名空间文档,就像 NDoc 和 Sandcastle 一样:通过向命名空间添加一个注释过的 `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 日。