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

AGE,.NET 中的另一个图形引擎

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.96/5 (32投票s)

2006年11月9日

CPOL

8分钟阅读

viewsIcon

140655

downloadIcon

6004

一个允许在运行时以简单方式进行 GDI+ 操作的库。

引言

这个库允许您在运行时在应用程序中操作图形对象。

Sample Image - another_graphic_engine_2.png

背景

前段时间,我看到有人问是否有一种方法可以将项拖放到面板上。我也有同样的问题(所有这些都始于 MyNeoReport 项目的设计器),所以我试图以一种通用的方式解决这个问题,以便该系统可以毫不费力地用于不同的应用程序。

AGE 支持项拖动、吸附到网格、多选、图层等。
AGE 发展迅速;如果我是你,我会密切关注这个项目。

主要主题

  • GraphicItem:要绘制的项
  • Painter:负责“脏活”绘制的类
  • GraphicDocument:收集所有要在单个表面上绘制的项的类
  • Canvas:显示绘图的控件

为什么要设置这个类?

:为什么我需要一个 RendererBase,为什么我不能将 Render() 方法直接放在 GraphicItem 类中?
:因为我需要更多的抽象。
这样我就可以将逻辑项与渲染的执行者分开。
我可以为不同的项使用相同的渲染器,或者为单个项选择不同的渲染器。

:为什么 GraphicDocument 是一个与 Canvas 分开的类?我可以将所有要绘制的项直接添加到 Canvas 中。
:原因与第一个问题相同:GraphicDocument 是逻辑项的集合,它是文档,而 Canvas 是绘制文档的表面。
同样,这样我就可以将同一个文档打开到多个画布上。

好的,开始吧……它是如何工作的?

:我如何创建自己的图形文档?
:好的,让我们定义一个全新的自定义文档

class CustomDocument : GraphicDocument
{
// define here your document properties
}

// define a custom item: an ellipse.
class Ellipse : GraphicItem
{
    Color _foreColor = Color.Blue;

    public Ellipse()
    {
        Painter = new EllipsePainter();
    }

    public Color ForeColor
    {
        get{ return _foreColor; }
        set{ _foreColor = value; }
    }
}

// define the ellipse painter
class EllipsePainter : Painter
{
    // how the ellipse is painted...
    protected override void Paint(Graphics g)
    {
        Ellipse ellipse = (Ellipse)Item;
        Pen pen = new Pen(ellipse.ForeColor);
        g.DrawEllipse(pen, Item.Bounds);
        pen.Dispose();
    }

}

...
canvas1.Document = new CustomDocument();
// add a new ellipse to the default layer
canvas1.Document.AddItem(new Ellipse());

从 1.4.0.0 版本开始,您还可以使用 ScriptAge 在运行时定义自定义图形项

ScriptedItem triangle = new ScriptedItem();
// Of course: it doesn't make much sense to hard code the script :)
// Rather you would load it from a separate text file or a string resource.
triangle.Script = "stroke: Polygon\n",
                  "pen: #7070ff, 2!\n",
                  "brush: gradient, #0000ff, #000000ff, (0; 0.5) (1; 1)\n",
                  "points: (0; 0.5) (1; 0.0) (1; 1)\n"

有关更多详细信息,请参阅下面的“参考文献”部分。

绘制项的限制

  • 您的 GDI+ 想象力

您如何贡献?

如果您认为本文和发布的项目有趣和/或有用,您可以通过多种方式帮助我维护它

  • 提供新想法
  • 通知/修复错误
  • 实现新功能
  • 传播项目
  • 在您应用程序的“关于”框中注明本项目
  • 为本文投票
  • 访问我的网站及其论坛
  • 访问我的网站并做出一些小贡献(这将是巨大的鼓励)

致谢

感谢Itai Bar-Haim提供的帮助和改进。

愿望清单

  • [1.5.0.0] 缩放(感谢 Itai Bar-Haim)
  • [1.4.0.0 D] 移植到 Linux+Mono
  • [1.4.0.0 B] ItemEnter/ItemLeave 事件
  • [1.3.0.0] 加载 items 库的能力
  • [1.3.0.0] GraphicItem [Browsable(false)]Min/MaxSize
  • [1.3.0.0] SelectionPainter 自定义所选项的选中“边框”的渲染
  • [1.3.0.0] LoadFrom() SaveTo() Stream 上工作,因此您可以将文档作为程序集资源包含在内
  • [1.2.0.0] 图层
  • [1.1.0.0] 保存图形文档的良好序列化方式
  • [1.1.0.0] 将绘图保存为图像文件的能力
  • [1.1.0.0] 工具栏图标
  • 打印绘图的能力
  • 撤销/重做
  • 选中项的剪贴板复制/粘贴
  • 管理屏幕和其他设备(如打印机)上的物理单位度量(厘米、毫米、米、英寸……)
  • ……还有其他吗?

我需要一些帮助……有什么建议吗?

参考文献

历史

如有任何疑问或建议,请前往我的网站论坛。

1.5.0.0

抱歉,此版本与 1.4.0.0 C 不完全兼容,但您应该不会遇到问题。我更改了一些属性名称,因此我选择更改版本号,并且需要重新编译使用 AGE 引擎的应用程序。

如果您有需要加载的文档,则必须使用记事本编辑它们,并替换重命名的属性(如下所列)。

Sample Image - age_scripteditem_3.png
  • Added
    • GraphicItem.RotationCenterGraphicItem.RotationCenterRelativeTo,它们允许您定义旋转角度的中心
    • Stroke.RotationCenter,使用 ScriptAGE,您可以为每个单独的笔触定义旋转点
    • Canvas QueryCursor 事件允许您了解光标状态并设置自定义光标
    • 图像笔触
    • 文本脚本笔触 beta(尚未完全正常工作)
    • ThemeDefaultTheme 类允许您定义画布外观(感谢 Itai Bar-Haim)
    • Canvas.Zoom 属性(感谢 Itai Bar-Haim)
    • 选中项各侧的拖动句柄(感谢 Itai Bar-Haim)
  • 固定
    • Canvas.Cursor 现在是只读的,并反映运行时中的光标状态
    • 仅允许使用鼠标左键拖动(右键和中键可用于其他目的)
    • GraphicItem.Angle 已重命名为 GraphicItem.RotationAngle
    • Stroke.Angle 已重命名为 Stroke.RotationAngle
    • Canvas.GridSize 已重命名为 Canvas.SnapGridSize,以避免与 Visual Studio 设计器 GridSize 属性冲突
    • ScriptAGE 已修复笔触和项属性
    • 还有许多其他 bug
  • 在 Linux Ubuntu 7.4 + Mono 1.2.3.1 上测试:引擎运行良好,设计器需要一些变通方法,因为 Mono 不完全支持 TreeView (未实现 TreeView.Sort()TreeView.NodeMouseDoubleClick 事件、TreeView.NodeMouseClick() 事件)

1.4.0.0 版本 C

在此版本中,我再次增强了 ScriptAge。
ScriptAge 与 1.4.0.0 版本 100% 兼容。

Sample Image - age_scripteditem_2.png
  • 现在 ScriptAge 可以定义由多个图形组成的笔触
  • 这允许您创建由曲线和线条组成的笔触
  • 添加了 pathradialelliptic 笔刷类型
  • 笔刷现在可以有 2 种以上颜色
  • Scriptage 现在可扩展:您可以定义自己的 PaintStroke
  • 您的自定义笔触可以通过覆盖 PaintStroke.ParseToken() 方法来定义其接受的标记
  • 添加了 PathStroke

1.4.0.0 版本 B

从现在开始,我将使用 AssemblyDescriptionAttribute 标记 AGE 引擎,以指示子版本。这是因为从 1.4.0.0 开始,我使用强名称签名了程序集,出于兼容性原因,我无法再修改版本号。

在此版本中,我增强了 ScriptAge(请参阅“参考文献”部分)。
ScriptAge 与 1.4.0.0 版本 100% 兼容。

  • 现在 ScriptAge 支持注释、图像标记、多行标记等
  • 一些次要的 ScriptAge 和 Canvas 修复
  • 添加了 Canvas ItemMouseEnterItemMouseExit 事件
  • 修复了设计时在 Canvas 上拖放控件的错误

1.4.0.0

此版本使我能够对库的灵活性进行重大改进,允许您在运行时直接定义项。

Sample Image - age_scripteditem.png

  • 添加了 ScriptedItem 类,允许您使用少量脚本定义图形
  • 进行了大量代码清理

1.3.0.0

这次,主要的改变是能够将自定义项写入单独的库中,这些库可以在运行时被文档加载。

Sample Image - another_graphic_engine_libraries.png
  • Added
    • virtual GraphicItem.PointIsOverMe(),它允许您定义一个点是否在项之上
    • Size GraphicItem.MinSize,它允许您定义项的最小尺寸
    • class SelectionPainterGraphicItem.SelectionPainter,它们允许您自定义项的选中边框
    • virtual Document.LoadLibrary(),它允许您从外部 DLL 导入 GraphicItems
    • GraphicLayer Canvas.ActiveLayer
    • bool Canvas.AllowUserEditItemsbool Canvas.AllowUserScrollCanvas,允许您决定用户是否可以操作画布 content 以及如何操作
    • Painter.GetPoint()Painter.GetSize()Painter.FlipPoint()Painter.FlipRectangle()
    • Painter.OnItemBoundsChanged()
    • 我记不清的其他一些内容
  • 测试项目开始成为一个真正的设计器

1.2.0.0

此版本侧重于图层功能。这是一项真正的挑战,目前仍处于 beta 阶段(它奏效了!我正在考虑一个更面向对象的解决方案)。

Sample Image - another_graphic_engine_layers.png
  • 添加了 Document.Groups 来定义项组和包装器 GraphicDocument.Layers
  • canvas 中移除了 ContextMenuStrip ,因为它对实现 canvas 的应用程序来说不是灵活的解决方案,并且 Mono 不支持它
  • Canvas.OffsetXCanvas.OffsetY 现在是 Point Canvas.Offset
  • RendererBase 已重命名为 Painter,使其更类似于框架命名
  • 常规修复和次要更改

1.1.0.0

此版本侧重于将文档保存到磁盘的功能,但在此过程中进行了数十项附带的改进

  • 我对类基础设施进行了重大审查。现在我引入了这些通用基类
    • 文档
    • DocumentItem

这允许您派生您想要的文档类型,并允许您将其保存到磁盘而无需额外的代码。
GraphicDocument 现在继承自 DocumentGraphicItem 继承自 DocumentItem

  • 序列化由一些类完成,我不会在本文中进行描述,因为它们不完整并且可能很快会更改
  • 增强并修复了 canvas 上的光标和选择方式
  • 提高了刷新速度
  • 添加了虚拟方法,让您可以决定接受或取消对项或文档的操作
   Document.OnAddingToDocument(DocumentActionEventArgs e)
   Document.OnRemovingFromDocument(DocumentActionEventArgs e)
   Document.OnAddedToDocument(DocumentActionEventArgs e)
   Document.OnRemovedFromDocument(DocumentActionEventArgs e)

   DocumentItem.OnAddingToDocument(DocumentActionEventArgs e)
   DocumentItem.OnAddedToDocument(DocumentActionEventArgs e)
   DocumentItem.OnRemovingFromDocument(DocumentActionEventArgs e)
   DocumentItem.OnRemovedFromDocument(DocumentActionEventArgs e)
  • Added
   GraphicDocument.SaveImage(string fileName, ImageFormat format)
   GraphicDocument.GetBounds()
   GraphicDocument.GraphicItems // this is a subset of Document.Items
   Document.SaveTo(string fileName)
   Document.LoadFrom(string fileName)

   Document.OnLoadingDocument()
   Document.OnSavingDocument()
   Document.OnDocumentLoaded()
   Document.OnDocumentSaved()
  • 移除了 Renderer.RenderType enum:现在由 Canvas 决定如何以及渲染什么
  • 添加了属性
    • Canvas.DrawInvisibleItems
    • Canvas.DrawDocumentBounds
  • Document.Items 现在是 GraphicItemCollection
  • Canvas.SelectedItems 现在是 GraphicItemCollection
  • Canvas.ItemMouseDoubleClick 事件现在是 Canvas.ItemDoubleClick
  • 添加了 Canvas.ItemClick 事件
  • 各种次要修改和修复,我记不清了

1.0.0.0

  • 这是第一个公开版本
© . All rights reserved.