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

CFlowchartEditor - 在 CDiagramEditor 中链接对象

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (122投票s)

2004 年 5 月 4 日

公共领域

8分钟阅读

viewsIcon

392281

downloadIcon

33934

一个带有链接对象的流程图编辑器,基于 CDiagramEditor。

Sample Image - flowcharteditor.gif

引言

CFlowchartEditorCDiagramEditor 的一个扩展。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_TOPLINK_BOTTOM,例如。
  • GetLinkPosition,它返回所需链接点的位置。

链接点可以位于对象的顶部、底部、左侧或右侧。对于线条,它们也可以位于对象的起始或结束处。

您还需要添加派生自 CDiagramEntity 的必要函数,如 DrawCloneCreateFromStringGetHitCodeGetCursorDrawSelectionMarkersBodyInRect,并根据需要进行实现。

为了允许从文件读取对象,还必须在 CFlowchartControlFactory::CreateFromString 中进行条目。

当然,您可以完全自由地使用源代码,我甚至授予您从所有源代码文件中删除我名字的权利,这样您就可以声称是自己写的 :-) 这包括使用 Flowcharter 代码 - 如果您愿意,可以基于参考应用程序创建一个新项目。

关注点

主要工作已经通过 CDiagramEditor 完成,CFlowchartEditor 的很多部分只是粗暴的代码编写。我对 CDiagramEditor 目前为止很满意,派生新绘图对象的所需工作量已降至最低,并且仍然可以轻松地将派生包集成到各种应用程序中。

然而,我意识到所有这些可能需要一些时间来消化,所以我添加了一个简化的链接(和 MDI 适配)参考实现,CNetworkEditor

Sample Image - flowcharteditor5.gif

它本身没有文档,除了注释过的源代码,但它使用了与 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 设置为 NULLMarc G)。
  • CTokenizer - 将一个 char 改为 TCHAR 以支持 UNICODE 构建(Enrico Detoma)。

25/3 2005

由于底层 CDiagramEditor 的更新,进行了一次维护升级。请参阅本文档(此处)了解详情。

25/6 2006

此项目最终基于的 CDiagramEditor 已更新。我未更新本文档的源代码,因为没有其他更改。请使用 CDiagramEditor 源代码 - 完整文章可在 此处找到。

© . All rights reserved.