带实时预览的代码项目文章编辑器






4.84/5 (58投票s)
帮助作者在 CodeProject 上撰写文章的工具

点击图片查看完整截图 - 306 KB
目录
{ 自动生成的目录 }
引言
这是一篇关于我用特别支持来撰写 Code Project 文章的 HTML 编辑器的短文。
背景
Office 2007 中没有 Frontpage 的版本,我以前曾用它来写文章。我不想在 Office 2007 上安装 Frontpage 2003“以防万一!”。所以,我找了一个可以一边显示 HTML 源代码一边进行预览的 HTML 编辑器。Visual Studio 是我找到的最好的,但预览必须在 HTML 的下方,这不适合我的宽屏显示器。所以我决定自己写一个。这是我的作品,希望您觉得它有用。
注意: 正如 Derek Bartram 在下面的讨论区指出的,Visual Studio 2008 可以将源代码和设计视图垂直分割。设置如下:
Tools | Options | HTML Designer | General | Split views vertically
组装说明
这是一个基础的 Windows Forms 应用程序,使用了 Weifen Luo 的 DockPanel Suite [^],所以您可以按照自己的意愿排列窗格。编辑器是来自 ic#code [^] 的 #develop
项目的一部分。我也使用了他们的 #ziplib
。预览窗格是一个普通的 WebBrowser 控件。
使用 BobBuilder
窗口类型
BobBuilder 中有两种类型的窗口:编辑器和预览。每个文档必须正好有一个编辑器窗口打开,但是您可以任意数量的预览窗口打开。
编辑器窗口
来自 #develop
的 TextEditor
控件有很多不错的特性,如撤销/重做和语法高亮。它还提供了许多选项,我在“工具 | 选项”对话框中公开了它们。我为一些 HTML 标签添加了快捷方式,例如 <code>
和 <pre>
,这些标签在 Code Project 的文章中很常见。另外,当您在开标签内时,按下 TAB
键会自动关闭标签并把光标放在中间。
自动完成
为了防止渲染非法 HTML,有一些“自动完成”的字符。首先,如果您输入 <
,您将得到 <>
。然后,如果您在标签内,引号 "
和单引号 '
会成对出现。
预览窗口
预览窗口在您使用编辑器时会不断更新。如果您为“导航开始”事件设置了系统声音,您可能想禁用它!预览窗口会尝试在每次更新时记住它们的滚动位置,但如果渲染了非法 HTML,这是不可能的。
工具条
有用于标准格式化(如粗体和斜体)的按钮。还有一个用于插入“clickety”的 Action
。
目录
有一个菜单项和一个工具条按钮用于插入目录。此命令使用 Regex
解析 HTML 中的所有 <hN>
标签,并在它们周围插入锚点。然后它会添加链接的无序列表来构成目录。如果目录已存在,则会被替换。否则,新目录将被插入到当前光标位置。
已知问题
链接
任何相对超链接,例如到锚点的链接,在预览窗口中都无法工作。这是因为 HTML 是通过它们的 DocumentText
属性传递给 WebBrowser 控件的。发生这种情况时,WebBrowser 会将 URI 设置为 about:blank
,然后问题就来了……
但是,在 HTML 传递给 WebBrowser 控件之前,它会被稍作修改。会添加一些 JavaScript 来帮助维护滚动位置,并添加一个 <base>
标签来使诸如图像之类的内联引用工作正常。您可以随时保存文档并使用“工具 | 外部浏览器”来快速检查您的相对链接是否正常工作。
注意: 当您上传文章时,HTML 会被放在主目录之一,但所有其他文件(图片、ZIP 等)都会被放在与文章基本名称同名的子目录中。例如,BobBuilder.aspx 位于 /KB/cs/,而所有其他文件都位于 /KB/cs/BobBuilder/。在撰写文章时,您可能希望在本地复制这种结构。
关注点
有几点需要强调
设计模式
我实现了 MVC 模式,但有所变通:我使用了 Observer 模式,视图订阅(Singleton
)控制器作为发布者。当一个动作被触发时,一个命令通过普通的函数调用发送到控制器。控制器完成其工作,触发事件来与任何参与的视图进行通信。
Observer 的 .NET event
实现的一个很棒的特性是订阅者可以修改 EventArgs
派生参数,从而将信息传递回发布者。这在 CurrentDocument
属性的实现中得到了体现。
partial class Controller
{
public static Document CurrentDocument
{
get
{
return Instance
.ExecuteCore( null, new Command.GetCurrentDocument() )
.Document;
}
}
}
Instance
获取 Singleton
实例,而 ExecuteCore
是一个返回其 Command
参数的成员方法。对于 GetCurrentDocument
命令,所有 ExecuteCore
所做的就是触发静态 Execute
事件。所有视图都订阅了这个事件,当当前活动的视图处理它时,它会用自己的(当前)文档填充命令的 Document
属性。因此,当 event
在调用其调用列表中的所有视图后返回时,Document
属性已经被当前视图设置,并返回给任何需要它的地方。
这种模式比让控制器维护一个视图接口集合更容易。现在所有这些都由内置的 event
处理来完成,同时减少了控制器和视图之间的耦合。
System.Windows.Forms.Keys
我找不到一种 .NET 方法来为给定的 char
确定该枚举的正确成员。最后,我写了这段代码片段。
[System.Runtime.InteropServices.DllImport( "user32.dll" )]
static extern short VkKeyScan( char ch );
static Keys ConvertToKeys( char c )
{
short vk = VkKeyScan( c );
int key = vk & 0x00FF;
int mod = vk & 0xFF00;
int iKeys = ( mod << 8 ) | key;
Keys eKeys = ( Keys ) iKeys;
return eKeys;
}
待办事项
这是一个可能进行增强的简短列表。如果您有任何意见或想法,请在下方留言。
- 拼写检查器
- 更多常用格式
- 可配置的快捷键
- 宏编译器
- 记住工具条位置
- 更好地处理异常
参考文献
- Weifen Luo 的 DockPanel Suite [^]
- 用于
#develop
和#ziplib
的 ic#code [^]
历史
2008 年 2 月 1 日 |
首次发布 |
|
2008 年 2 月 1 日 | 移除了表情符号 | |
2008 年 2 月 3 日 | 添加了目录的自动生成 | |
2008 年 2 月 7 日 | 修复了目录的自动生成 | |
2009 年 8 月 5 日 | 修复了模板中新 DOCTYPE 的预览放置问题 |