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

CDiagramEditor - DIY 矢量和对话框编辑器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.96/5 (155投票s)

2004年4月5日

公共领域

11分钟阅读

viewsIcon

556534

downloadIcon

36999

一个功能丰富的矢量图编辑器骨架。

Sample Image - diagrameditor.gif

DialogEditorDemo 应用

Sample Image - diagrameditor2.gif

DiagramEditorDemo 应用

引言

所以你想为你的应用程序添加一个窗体编辑器?一个对话框编辑器?一个允许绘制 HTML <div>s 的东西?这里有一个功能丰富的骨架(!)可以帮助你入门。CDiagramEditor 是一个提供基础视觉编辑器(用于矢量对象)的包。虽然可能不足以创建 CAD 应用程序,但你确实能够创建一个对话框编辑器。编辑器本身派生自 CWnd,有一个单独的类处理数据,因此你可以将其用于对话框或文档/视图应用程序。

一些内置功能

  • 完整的鼠标处理。绘制和拖动多个对象,拖动绘制,以及调整单个对象的大小。
  • 纸张大小、边距和网格可轻松配置。
  • 吸附到网格,将移动和调整大小限制在边距或纸张内。或者,如果你愿意,也可以在纸张外绘制。
  • 可配置的键盘界面。
  • 编辑器的插件弹出菜单。
  • 无限缩放。
  • 无限撤销。
  • 支持保存和加载到 CString/CStringArray
  • 对于编程爱好者来说,编辑器中的不同绘图函数是虚拟的,所以如果你对外观不满意,可以派生一个类自己来实现。
  • 矩形和线条的基础对象,你可以从中派生出你需要的绘图对象。
  • 支持特定对象的弹出菜单。
  • 支持特定对象的属性对话框。

还有更多。当然,这个包的使用起来会非常困难?不,只需将 CDiagramEditor 的一个实例添加到你的应用程序中,派生出你需要的绘图类,你就拥有了一个编辑器。

我添加了两个演示应用程序。较小的 DiagramEditorDemo 显示了基础知识,在一个对话框应用程序中添加了一个编辑器。

DialogEditorDemo 是一个 SDI 文档/视图应用程序,展示了如何使用独立的数据容器类,如何插入文档加载和保存,以及如何使用工厂创建绘图对象,如何添加打印和打印预览、自定义背景重绘、导出等等。

该包由以下类组成

  • CDiagramEditor - 编辑器本身。
  • CDiagramEntityContainer - 编辑器数据的容器类。
  • CDiagramEntity - 绘图对象的基础类。
  • CDiagramLine - 一个 CDiagramEntity 派生类,可用作线条对象的基础类。
  • CDiagramMenu - 弹出式编辑器菜单。
  • CDiagramPropertyDlg - 对象属性对话框的纯虚基类。

以及一些小型辅助类。

由于代码量很大,我添加了 HTML 文档,其中包含类的概述,以及一个关于大型类如何使用和类详细信息的页面。代码也进行了注释。

历史

时间的开始

最初发布。

1/5 2004

在密集地处理 CDiagramEditor 时,我对该包进行了一些增强,主要集中在

  1. 方便从 CDiagramEntityContainer 派生。
  2. 使多个编辑器可以使用同一个内部剪贴板。

第二个修改是为了能够在 MDI 应用程序中使用 CDiagramEditor,你可能希望在编辑器之间复制和粘贴对象。这是通过将复制/粘贴功能从 CDiagramEntityContainer 中分离出来,并将其放入一个单独的类 CDiagramClipboardHandler 来实现的。

尤其令人恼火的是,在进行这些修改时,以 MDI 应用程序作为宿主,尽管属性对话框是以编辑器为父窗口创建的,但在 OnOK 处理程序中调用 GetParent 时,父窗口却返回了错误的值。因此,当对象发生更改时,编辑器无法正确更新。我不得不通过为 CDiagramPropertyDlg 添加一个显式的重绘父窗口来解决这个问题,并添加一个新的成员 Redraw,在 MDI 应用程序中,派生类应该使用它而不是 GetParent()->RedrawWindow()

除此之外,我必须承认,在 MSVC++ 6.0 中重构(refactoring)确实是一件非常麻烦的事情,我以前以为只有软弱的人才会抱怨(我生命中至今为止所需的重构量非常少,难道是我运气好吗?)。

文档已相应更新,更改包括

CDiagramEditor
  • SendMessageToObjects 设置 selected-flag 为 TRUE - 仅将消息发送给选定的对象。
  • 使一些消息映射函数成为虚拟函数,以允许在派生类中进行增强。
  • 添加了状态访问器,以允许派生类访问。
  • Ctrl+单击项后将交互模式设置为 MODE_NONE(以避免线条的非预期移动)。
  • Clear 中将滚动条位置设置为零。之前,在加载文档时,滚动条没有重置为 0,0。
  • 将此窗口作为属性对话框的父窗口传递。这是为了在 MDI 应用程序中,在对话框发生更改后正确重绘编辑器。
  • 将 C 风格的类型转换更改为 static_cast
  • 移除了 ShowPropertiesconst 属性。
CDiagramEntityContainer
  • 使 RemoveAt 成为虚拟函数。
  • 使几个撤销和复制/粘贴函数成为虚拟函数。为派生类添加了数组访问器。将成员函数 Find 移动到 protected 部分。所有这些都是为了简化从 CDiagramEntityContainer 派生。
  • 复制/粘贴处理已移至一个单独的类(CDiagramClipboardHandler),以便多个容器可以共享同一个剪贴板。
  • 将 C 风格的类型转换更改为 static_cast
CDiagramEntity
  • m_type 的访问器 - SetTypeGetType - 更改为 public
  • FromStringGetString 中添加了冒号作为替换字符,用于保存。之前,包含冒号的名称或标题将无效。
  • ShowProperties 中为属性对话框添加了一个重绘父窗口。
CDiagramPropertyDlg
  • 添加了一个重绘父窗口 m_redrawWnd,用于在 MDI 应用程序中重绘正确的窗口。派生类现在可以使用 Redraw 来更新编辑器。

如果您正在现有项目中 O使用 CDiagramEditor,受影响的将是上面提到的“*设置交互模式...*”、“*设置滚动条位置...*”和“*添加冒号作为替换字符...*”。要使用新包,请重新下载源代码并用新文件覆盖现有文件,然后将 CDiagramClipboardHandler.cppCDiagramClipboardHandler.h 添加到您的项目中。

15/5 2004

对话框编辑器演示中的错误修复

演示中的 CDiagramEntity 派生对象的 Export 函数没有更新为 const,因此未被导出机制调用。

13/6 2004

这肯定不是一个关键的更新。主要是我正在更新框架,因为即将有一个 UML 编辑器,其中一些东西已经被设为 virtualconst,以便于派生编辑器。总之,继续列表

滚动时的虚拟大小检查

Graham 建议了这一点(见下文)。虽然滚动位置功能应该报告正确的值,但为了安全起见,我在 VScroolHScroll 例程中加入了页面检查 - 不会不必要地尝试滚动一个没有滚动条的编辑器窗口。

为派生编辑器进行的修改
  • CDiagramEditor
    • SetInteractMode 中添加了对 m_subMode 的访问。
    • 使 OnObjectCommand 成为虚拟函数,以便在派生类中进行拦截。
    • 使 GetBackgroundColor 成为 const。
    • 使 SelectAll 成为虚拟函数。
  • CDiagramEntityContainer
    • 使 GetAt 成为虚拟函数。
    • RemoveAll,添加检查以查看对象数组中是否有任何项。
    • 使 RemoveAll 直接访问数据容器对象,以避免在派生类中发生连锁删除。适用于 delete 是否应自动删除其他项。

谁应该更新编辑器代码?任何人尝试滚动到纸张区域之外时遇到问题?

8/7 2004

又到了更新的时候了!我添加了一些错误修正,一些增强功能以进一步帮助派生编辑器,并添加了一些新功能。修正包括

  • 修正了 SetZoom 中的一个错误,该错误没有遵循最小尺寸(pgrohs)。
  • SetDiagramEntityContainerdelete 后将 m_internalData 置零,以避免崩溃(pgrohs)。
  • 修正了 LeftAlignSelected 等中的对齐处理错误 - 原来是所有项目,而不仅仅是选定的项目被对齐了。
  • 修正了 VirtualToScreen 中的错误,不再扣除滚动条位置(Wolfgang Busch)。
  • 在保存/加载时,为了允许在标题和名称中使用换行符,添加了 `\\newline` 作为替换字符(Unruled Boy)。

增强功能

  • 使 AddObject 成为虚拟函数,并添加了虚拟 MoveObject 函数。这是为了允许在派生类中捕获编辑器中添加或移动的对象(sunmoon9898)。
  • 使剪贴板函数和 DeleteAllSelected 成为虚拟函数。
  • 添加了一个虚拟的 GetCursor 函数,以简化自定义光标的添加。
  • 坐标转换函数已设为 public(Wolfgang Busch)。
  • 使容器中的 AddRemove 成为虚拟函数。
  • 为容器添加了 GetSelectCount

新功能

Unruled Boy 建议

现在可以通过右键单击编辑器并选择“组合”来组合选定的对象。组合的对象可以作为一个单元进行移动、复制和粘贴。通过右键单击并选择“取消组合”,可以移除选定项目的所有组合。

编程上,这可以通过调用 public 函数 CDiagramEditor::GroupCDiagramEditor::Ungroup 来实现。CDiagramEditor::UpdateGroup( CCmdUI* pCmdUI )CDiagramEditor::UpdateUngroup( CCmdUI* pCmdUI ) 可以作为命令启用器从例如视图中调用。

组合被实现为 CDiagramEntity 中的一个新属性。

注意事项

基于 CDiagramEditor 的演示应用程序的旧数据文件由于新的基本属性而不再与框架兼容。我认为这是一个可接受的权衡,希望不会造成不便。派生工作将需要更新以加载/保存此属性,并且我在 CDiagramEntity 中添加了简化此操作的功能:LoadFromStringGetDefaultGetStringGetHeaderFromStringGetDefaultFromString

一个典型的 FromString 在一个假设的 CDiagramEntity 派生类 CSomeEntity 中,带有一个额外的 CString 属性。m_someattribute 现在可以这样写

BOOL CSomeEntity::FromString( const CString& str )
{

  BOOL result = FALSE;
  CString data( str );

  if( LoadFromString( data ) )
  {
    CTokenizer tok( data );

    CString someattribute;
    tok.GetAt( 0, someattribute);

    SetSomeAttribute( someattribute );
    result = TRUE;
  }

  return result;

}

其中 CDiagramEntity::LoadFromString 用于设置基类属性,其余属性从修改后的输入字符串中解析。

GetString 可以这样写

CString CSomeEntity::GetString() const
{

  str.Format( _T( ",%s;" ), // Note the starting comma
    GetSomeAttribute()
  );

  str = GetDefaultGetString() + str;
  return str;

}
鼠标滚轮支持

John A. Johnson 建议。

通过在 CDiagramEditor 中处理 WM_MOUSEWHEEL,现在可以使用鼠标滚轮滚动纸张。我曾考虑过使其更灵活。可配置的滚动步长和缩放功能浮现在脑海中,但这将不得不等到以后。OnMouseWheel 已设为虚拟函数,以便在此方向上进行个人尝试 :-)

平移

John A. Johnson 建议。

实现了平移。通过按下鼠标中键,会在光标处显示一个平移标记。如果移动鼠标,编辑器将朝着光标方向滚动。滚动量与标记和当前光标之间的距离成比例。光标会改变以指示当前的光标方向。我使用了内置光标来实现这一点,以避免对资源的依赖。

我检查了 P J Arends 的优秀类 CWindowScroller,并确实有可能将其直接插入编辑器,但由于我希望 CDiagramEditor 拥有完全免费的许可证,我而是基于 MSDN(Visual Programmer 文章关于 George Shepherd 和 Scot Wingo 的平移)进行了工作。

这方面的主要函数是 OnMButtonDownSetPanningDrawPanning

缩放到适合

John A. Johnson 建议。

此函数将设置缩放级别,以便当前图中的所有对象都可见。要调用的函数是 public 函数 ZoomToFitScreen

最后,非常感谢所有提供反馈的人(特别是上面归功于的 - 但欢迎所有反馈)关于这个框架。

4/8 2004

这次进行了一些小修复和添加。

  • 添加了滚轮模式。通过调用 SetScrollWheelMode(WHEEL_SCROLL/WHEEL_ZOOM),滚轮将滚动或缩放。
  • 为更多消息处理程序添加了虚拟函数,包括滚动条处理程序。
  • SetZoom 添加了虚拟函数。
  • 添加了虚拟函数来设置滚动条位置,以便有一个统一的地方进行此操作。
  • 错误:OnLButtonDown 中放置项目时,检查光标是否在约束之外。
  • 添加了 ScrollIntoView 命令。该函数将当前选定的对象滚动到视图中。
  • 错误:修复了选定标记的错误 - 鼠标坐标的虚拟化与选定标记矩形不一致(Marc G)。
  • 向容器添加了 SelectAllUnselectAll

28/8 2004

虽然为时过早,但又一次的错误修正。再一次,非常感谢大家提供的反馈!

  • CDiagramEditor:在 OnLButtonDown 的命中测试中添加了对非规范化矩形的检查,因为线条有它们。
  • CDiagramEntity:在 ctor 中将 m_parent 设置为 NULLMarc G)。
  • CTokenizer:将一个 char 更改为 TCHAR 以允许 UNICODE 构建(Enrico Detoma)。

25/3 2005

一次维护更新,并添加了一些内容

  • 使 UnselectAll 成为虚拟函数(Grisha Vinevich)。
  • OnKeyDown 中添加了最小步长一像素,以避免箭头键的 0 像素移动(Graham G Pearson)。
  • 使 UnselectAll 成为虚拟函数(Grisha Vinevich)。
  • 添加了 PopUndo 函数,从堆栈中弹出最新的撤销项。
  • 使 IsUndoPossible 成为 const。
  • 使 SetParent/GetParent 成为 public。

以及对线条选择机制的修正(Graham G Pearson)。

15/5 2005

又一次维护更新

  • 通过一个虚拟成员函数路由所有对象选择,以允许自定义选择(Janiv Ratson)。
  • 添加了一个重做命令。该命令将撤销最后一次撤销。处理程序和启用器与撤销一样,但消息处理的是 ID_EDIT_REDO。键盘映射到 Ctrl+Y(Janiv Ratson)。
  • ID_EDIT_UNDO 添加了消息映射(Graham G Pearson)。
  • ID_EDIT_REDO 添加了消息映射。

注意:我只更新了 下载源代码文件 链接的源代码 - 没有更新演示项目。因此,请务必在使用代码到您自己的项目时下载此文件!

23/6 2006

迟来很久的又一次更新

  • 添加了 PasteToPosition 函数(Janiv Ratson
  • 错误:修正了 ZoomToFit 中的错误(DanMoshe
  • 错误:修正了用鼠标调整多个组合对象大小时的错误(obeea
  • 错误:SendMessageToObjects 添加了 dirty 参数用于 dirty 处理 - 并非所有命令都应将文档标记为已更改(Janiv Ratson
  • 错误:修正了 CDiagramEntityContainer 撤销堆栈处理,使其能考虑堆栈大小为 0 的情况(David Hoos
  • 错误:CDiagramEntity::Copy 中添加了组到复制的数据(JeffBean
  • 小的布局更改,并移除了冗余的滚动条处理(Alexey Shalnov

注意:再次提醒,我只更新了 下载源代码文件 链接的源代码 - 没有更新演示项目。因此,请务必在使用代码到您自己的项目时下载此文件!

© . All rights reserved.