CFlowchartEditor - 在 CDiagramEditor 中链接对象






4.94/5 (122投票s)
一个带有链接对象的流程图编辑器,基于 CDiagramEditor。
- 下载源代码文件 - 111 Kb
- 下载演示项目 - 44.2 Kb
- 下载演示项目源代码 - 135 Kb
- 下载 HTML 文档 - 57.4 Kb
- 下载 nw 地图编辑器演示二进制文件 - 35.4 Kb
- 下载 nw 地图编辑器项目源代码 - 106 Kb
引言
CFlowchartEditor
是 CDiagramEditor
的一个扩展。CDiagramEditor
是一个矢量编辑器,它包含一个 CWnd
派生窗口(CDiagramEditor
),一个数据容器(CDiagramEntityContainer
),用于保存绘图对象、撤销堆栈以及管理复制和粘贴,还有派生自 CDiagramEntity
的对象,代表屏幕上绘制的对象。CDiagramEditor
缺少一项功能(这项功能很难以简单和通用的方式实现)——链接。手动维护例如流程图或网络拓扑图中的链接是繁琐的。因此,我创建了一个用于添加此功能的参考实现。
流程图编辑器
您可以在演示应用程序 Flowcharter 中测试该编辑器。在此应用程序中,您可以绘制基本的流程图对象,使用鼠标或键盘移动它们,剪切/复制/删除/粘贴,无限次撤销,打印预览,打印并将流程图导出为 EMF(增强型图元文件)。如果您选择两个未链接的对象,您也可以将它们链接起来。一条带有箭头指示链接方向的线会自动绘制。您可以选择两个链接的对象并取消链接它们,反转链接方向,或设置链接标签(一个随链接线移动的文本)。对象具有固定大小,如果链接的对象被移动,其他链接的对象也会随之移动。
链接会自动维护,如果您复制和粘贴链接的对象,粘贴的对象将具有相同的相对链接。甚至还有一个特殊的链接线对象,可以用来创建更复杂的流程。请注意,自动移动不会改变对象的大小,即使是线条也不会。
编辑器还包含两个不可链接的对象:一个虚线,可用于分隔流程图;一个标签,可用于…为图表添加标签。
CDiagramEditor
的其他功能包括使用 +/- 键进行缩放,以及在对象移出可见区域时自动滚动,还有键盘编辑和右键弹出菜单。您可能想下载并阅读 CDiagramEditor
文档(上方链接)以获取其功能的完整说明。
CFlowchartEditor
我首先尝试将链接作为 CDiagramEntity
对象的一个属性,但出于多种原因,这过于复杂。主要原因是对象开始需要相互了解,并且复制/粘贴等操作变得非常困难。相反,我选择了一种解决方案,在编辑器容器中使用一个单独的链接数组。这需要对 CDiagramEditor
进行一些修改,以简化从 CDiagramEntityContainer
派生(主要是将一些内容设为虚函数,或将它们从 private 改为 protected)。
我还想做的另一件事是提供一个如何在 MDI 应用程序中使用 CDiagramEditor
的参考实现。很快我就意识到我的架构存在一个巨大的缺陷——无法在多个窗口之间复制和粘贴。当然,因为剪贴板处理程序是容器内部的……将剪贴板处理程序剥离到一个单独的类就可以实现这一点。
由于我实际上**自己**正在使用流程图编辑器,所以我也需要增强型图元文件的支持,这一点也已添加。以及为编辑器定制的右键弹出菜单(从资源加载,实现链接命令)。
使用该包
如果您想将 CFlowchartEditor
添加到其他应用程序,您需要执行以下操作:
- 在您的项目中启用 RTTI。您可以在 **Project|Settings|C/C++|C++ language** 中完成此操作。
CFlowchartEditor
使用运行时类型信息来混合链接对象和非链接对象。我原本可以使用对象类型(CDiagramEntity
的一个属性)来实现这一点,但是添加新的对象类型将需要在代码的许多地方进行更改。 - 将 FlowchartEditor 目录下的所有文件(*FlowchartEditor.rc* 和 *resource.h* 除外),以及 FlowchartEditor/DiagramEditor 目录下的所有文件添加到您的项目中。我通过在项目中创建子文件夹来实现这一点 - 请参阅 Flowcharter。
- 同时打开您的项目资源和 FlowchartEditor.rc。按住 CTRL 键,将资源从 FlowchartEditor.rc 拖到您的项目。
如果您使用的是对话框应用程序
- 在主对话框类中添加一个
CFlowchartEditor
成员。 - 在对话框类的
OnInitDialog
中调用CFlowchartEditor::Create
。
如果您使用的是 SDI 应用程序
- 在派生自
CDocument
的类中添加一个CFlowchartEntityContainer
成员。 - 添加虚函数
SaveModified
。添加类似如下的代码:SetModifiedFlag( m_objs.IsModified() ); return CDocument::SaveModified();
其中
m_objs
是您的CFlowchartEntityContainer
的名称。 - 修改
OnNewDocument
如下:if (!CDocument::OnNewDocument()) return FALSE; m_objs.Clear(); return TRUE;
- 在
Serialize
中添加保存和加载。这是一个例子:if (ar.IsStoring()) { ar.WriteString( m_objs.GetString() + _T( "\r\n" ) ); int count = 0; CDiagramEntity* obj; while( ( obj = m_objs.GetAt( count++ ) ) ) ar.WriteString( obj->GetString() + _T( "\r\n" ) ); int max = m_objs.GetLinks(); for( int t = 0 ; t < max ; t++ ) { CFlowchartLink* link = m_objs.GetLinkAt( t ); if( link ) ar.WriteString( link->GetString() + _T( "\r\n" ) ); } m_objs.SetModified( FALSE ); } else { m_objs.Clear(); CString str; while(ar.ReadString( str ) ) { if( !m_objs.FromString( str ) ) { CDiagramEntity* obj = CFlowchartControlFactory::CreateFromString(str); if( obj ) m_objs.Add( obj ); else { CFlowchartLink* link = new CFlowchartLink; if( link->FromString( str ) ) m_objs.AddLink( link ); else delete link; } } } m_objs.SetModified( FALSE ); }
请注意,您可能需要添加
#pragma warning( disable : 4706 )
在文档类文件顶部,以及一个
#pragma warning( default : 4706 )
以避免烦人的警告。
- 在派生自
CView
的类中添加一个CFlowchartEditor
成员。下面我将假定名称为m_editor
。 - 添加虚函数
OnInitialUpdate
,包含(至少)以下代码:if( !m_editor.m_hWnd ) { // Creating the editor window CFlowcharterDoc* pDoc = GetDocument(); CRect rect; GetClientRect( rect ); m_editor.Create(WS_CHILD | WS_VISIBLE, rect, this, pDoc->GetData()); } else m_editor.Clear();
- 处理
OnSize
CView::OnSize(nType, cx, cy); if( m_editor.m_hWnd ) m_editor.MoveWindow(0,0,cx,cy);
- 最后,
OnEraseBkgnd
return TRUE;
以避免闪烁。
如果您使用的是 MDI 应用程序
除了上面 SDI 应用程序的说明外,您还应该执行以下操作:
- 在某个中心位置添加一个
CDiagramClipboardHandler
成员。在 Flowcharter 应用程序中,我选择了应用程序类本身。我将其设为 public,并在 Flowcharter.h 中添加了应用程序对象的外部声明,如下所示:extern CFlowcharterApp theApp;
- 修改
CView OnInitialUpdate
如下:CView::OnInitialUpdate(); if( !m_editor.m_hWnd ) { CFlowcharterDoc* pDoc = GetDocument(); CRect rect; GetClientRect( rect ); pDoc->GetData()->SetClipboardHandler( &theApp.m_clip ); m_editor.Create( WS_CHILD | WS_VISIBLE, rect, this, pDoc->GetData() ); } else m_editor.Clear();
以在所有 MDI 子窗口之间添加一个共享的剪贴板。
图元文件导出
添加图元文件导出很容易。由于编辑器已经有一个函数可以将图表内容绘制到 CDC
中,因此可以使用以下代码将 8x11 英寸的图表绘制到图元文件中:
CClientDC dc( this ); CMetaFileDC metaDC; CRect rect( 0,0, m_editor.GetVirtualSize().cx, m_editor.GetVirtualSize().cy ); // Himetric rect CRect r( 0, 0, 8 * 2540, 11 * 2540 ); metaDC.CreateEnhanced( &dc, dlg.GetPathName(), &r, _T( "FlowchartEditor Drawing" ) ); m_editor.SetRedraw( FALSE ); m_editor.Print( &metaDC, rect, 1 ); m_editor.SetRedraw( TRUE ); m_editor.RedrawWindow(); ::DeleteEnhMetaFile ( metaDC.CloseEnhanced() );
添加对象
可以通过派生自 CFlowchartEntity
的类来添加可链接对象。您需要为您的对象添加一些特定函数:
AllowLinks
,它应该返回允许的链接点。最初,CFlowchartEntityTerminator
只允许在顶部或底部链接,并暴露LINK_TOP
或LINK_BOTTOM
,例如。GetLinkPosition
,它返回所需链接点的位置。
链接点可以位于对象的顶部、底部、左侧或右侧。对于线条,它们也可以位于对象的起始或结束处。
您还需要添加派生自 CDiagramEntity
的必要函数,如 Draw
、Clone
、CreateFromString
、GetHitCode
、GetCursor
、DrawSelectionMarkers
和 BodyInRect
,并根据需要进行实现。
为了允许从文件读取对象,还必须在 CFlowchartControlFactory::CreateFromString
中进行条目。
当然,您可以完全自由地使用源代码,我甚至授予您从所有源代码文件中删除我名字的权利,这样您就可以声称是自己写的 :-) 这包括使用 Flowcharter 代码 - 如果您愿意,可以基于参考应用程序创建一个新项目。
关注点
主要工作已经通过 CDiagramEditor
完成,CFlowchartEditor
的很多部分只是粗暴的代码编写。我对 CDiagramEditor
目前为止很满意,派生新绘图对象的所需工作量已降至最低,并且仍然可以轻松地将派生包集成到各种应用程序中。
然而,我意识到所有这些可能需要一些时间来消化,所以我添加了一个简化的链接(和 MDI 适配)参考实现,CNetworkEditor
。
它本身没有文档,除了注释过的源代码,但它使用了与 CFlowchartEditor
相同的机制,功能更少(例如,链接对象不会被移动,只有一个链接点,每个对象有无限个链接),我的意图是可以用它来与 CFlowchartEditor
进行实现比较。它还演示了如何使用图标作为绘图对象。
历史
13/6 2004
随着底层 DiagramEditor 的更新,我也会更新本文档,以便两个文章中的框架保持同步。
我还更新了网络地图编辑器演示,这得益于 Dimitris Vassiliades 的反馈,将图标更改为 DIB 以 - 希望 - 获得正确的打印输出。
8/7 2004
对 DiagramEditor 的又一次更新。我纠正了错误,进行了增强,并添加了功能。
- 组
添加了一个组命令。对象可以被分组并作为一个单一对象处理。
- 平移
添加了平移功能。通过单击鼠标中键并移动光标,编辑器将平移图表。
- 鼠标滚轮支持
可以使用鼠标滚轮滚动编辑器。
- 缩放到适应屏幕
已添加功能,可以将图表缩放到显示所有对象。
更改已在上面链接的 Diagram Editor 文章中记录并归因。
5/8 2004
源代码已更新,因为 DiagramEditor 已更新。
28/8 2004
虽然还太早,但还是进行了一轮 bug 修复。再次,非常感谢大家提供的反馈!
CDiagramEditor
- 在OnLButtonDown
中添加了对非归一化矩形的检查,因为线条有它们(Marc G)。CDiagramEntity
- 在ctor
中将m_parent
设置为NULL
(Marc G)。CTokenizer
- 将一个char
改为TCHAR
以支持 UNICODE 构建(Enrico Detoma)。
25/3 2005
由于底层 CDiagramEditor
的更新,进行了一次维护升级。请参阅本文档(此处)了解详情。
25/6 2006
此项目最终基于的 CDiagramEditor
已更新。我未更新本文档的源代码,因为没有其他更改。请使用 CDiagramEditor
源代码 - 完整文章可在 此处找到。