在AutoCAD中多视口展示多张图纸(AutoCAD2018及以上版本可用)

文章讲述了如何使用C#在Autocad环境中实现用户自定义布局,通过合并多张图纸并创建多个视口,以便用户在单个窗口中校对和修改不同类型的图纸。作者详细描述了合并图纸、设置视口、定位功能以及保存用户修改的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        书接上回,绘制了页面布局wpf窗体。没错,用户自定义的布局,就是用来在Autocad中展示图纸的。

        真实场景中,用户存在多张图纸,假设是平面图、剖面图、三维图。用户需要在AutoCAD中修改某些图纸的细节,使其更符合真实情况。但是修改图纸需要其它图纸做参考,以防出错。因此,同一个窗口,显示多张图纸,方便用户校对数据、修改的需求就出现了。

先上效果吧:

三视图

一开始想用最简单的方案,直接调用Autocad的命令,把多张图纸的tab窗体显示出来,让用户自己修改。发现这个不友好,一是用户得处理开始窗体。开始界面一直显示,没法拿到它的句柄,把它最小化,又不能侵入性地把开始界面藏起来。二是这个命令只能平分,遇到个性化需求就无法满足了。给大家看看这个方案出来的效果:

        查找资料,最后决定使用视口来解决这个问题。但是视口也有局限性,视口只能对一张图纸进行多视口操作,咱们多张图纸就显得力不从心了。于是就想了个能实现需求的办法,把多张图纸合并成一张,然后开3个视口,每个视口居中显示不同的范围,看起来就像在操作多张图纸了。

        但是也有一个局限,那就是其实三个视口显示的都是一张被合并的图纸,用户缩小或者拖拽,就找不到原先的视口了。于是我们加了个定位的按钮,记录下每个视口在整张图纸中的居中范围,用户找不到修改的图纸了,就可以点击这个按钮进行复原操作。

        还有最后一个问题,用户修改的图纸,怎么分成3张存回到对应的图纸上去?因为我们修改的是同一张图纸,虽然我们记录了原图在被合并后图纸中的范围,但是用户可能在任意范围添加图形元素。我们可以加个按钮,让用户在保存的时候框选区域,选择对应的图纸类型(屏幕、剖面、三维)就可以了。

        解决方案定了,剩下就是写代码的事情了。我们要做的事情如下:

  1. 把3张dwg图纸合并成1张dwg,并记录下原始图纸在合并后图纸的范围,用于后期做视口居中操作。注意,由于是多图操作,记得CommandFlags.Session设置上。本文简化使用了块参照。需要注意的是,块插入的时候插入点需要计算一下,防止图纸重叠。
  2. 按照比例创建想要的视口,并在不同视口对图纸进行不同范围的居中操作。

事情定了,就看看是不是已经有代码实现了这些操作。拼一拼,改一改,看能不能快速实现这个需求。查找了编程宝典《AutoCAD VBA&VB.NET开发基础与实例教程》有关于视口的代码,但是不能完全满足需求,代码最终是靠命令实现的视口创建。也许在当时那个版本,还没有C#的接口开放吧。最后参考了AutoCAD的官方blog,这篇Create four split modelspace viewports and set different orthographic views

拿着代码改了改,很快就实现了需求。

上源码

 [CommandMethod("STVP", CommandFlags.Session)]
 public void SpliterThreeViewPort()
 {
     string templateFile = @"acad.dwt";
     //把三张图纸变成块参照放到一张图纸中
     //todo:替换成你自己的图纸路径
     string sectionalView = "../剖面图.dwg";
     string threeDView = "../三维图.dwg";
     string planView = "../平面图.dwg";

     List<string> drawingNames = new List<string> { planView, sectionalView, threeDView };
     Dictionary<string, ObjectId> dicBlocks = new Dictionary<string, ObjectId>();

     //新建一张图纸
    //todo:替换成你自己的图纸路径
     string combinedPath = @"../combined.dwg";
     Document tempDoc = Application.DocumentManager.Add(templateFile);
     Application.DocumentManager.MdiActiveDocument = tempDoc;
     tempDoc.Database.SaveAs(combinedPath, DwgVersion.Current);

     Document combinDoc = Application.DocumentManager.Open(combinedPath);
     Application.DocumentManager.MdiActiveDocument = combinDoc;

     string blockName = string.Empty;
     List<string> blockNames = new List<string>();
     for (int i = 0; i < drawingNames.Count; i++)
     {
         blockName = Path.GetFileNameWithoutExtension(drawingNames[i]);
         ImportDrawingAsBlkstoCurDoc(combinDoc, drawingNames[i], combinedPath);
         blockNames.Add(blockName);
     }

     Dictionary<string, Extents3d> dicBlockExtents = InsertBlocks(combinDoc, blockNames);
     Application.DocumentManager.MdiActiveDocument = combinDoc;
     //打开图纸,分成3个视口,并且按照块参照的名称分别居中每个视口的图纸    
     SplitAndSetViewModelViewports(combinDoc, dicBlockExtents.Values.ToList());
 }
 public Dictionary<string, Extents3d> InsertBlocks(Document doc, List<string> blockNames)
 {
     // 记录已插入块参照的外包围框
     Dictionary<string, Extents3d> dicBlockExtents = new Dictionary<string, Extents3d>();

     Database db = doc.Database;
     // 获取要插入的块名称列表

     using (DocumentLock docLock = doc.LockDocument())
     {
         db.UpdateExt(true);
         using (Transaction tr = db.TransactionManager.StartTransaction())
         {
             BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
             BlockTableRecord modelSpace = tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;


             Point3d insertPosition = Point3d.Origin;

             foreach (string blockName in blockNames)
             {
                 // 获取块定义
                 ObjectId blockId = bt[blockName];
                 if (blockId.IsNull)
                 {
                     continue;
                 }

                 // 创建块参照
                 using (BlockReference blockRef = new BlockReference(insertPosition, blockId))
                 {
                     // 计算块的外包围框
                     blockRef.TransformBy(Matrix3d.Scaling(1, insertPosition));
                     Extents3d blockExtents = blockRef.GeometricExtents;

                     // 将块参照插入模型空间
                     modelSpace.AppendEntity(blockRef);
                     tr.AddNewlyCreatedDBObject(blockRef, true);

                     // 更新已插入块参照的外包围框列表
                     dicBlockExtents.Add(blockName, blockExtents);
                     insertPosition = blockExtents.MaxPoint;
                 }
             }
             tr.Commit();
         }
     }
     return dicBlockExtents;
 }
public static void SplitAndSetViewModelViewports(Document doc, Dictionary<string, Extents3d> dicExtents)
{
    using (DocumentLock docLock = doc.LockDocument())
    {
        Database db = doc.Database;

        db.UpdateExt(true);
        Extents3d dbExtent = new Extents3d(db.Extmin, db.Extmax);

        using (Transaction tr = db.TransactionManager.StartTransaction())
        {
            ViewportTable vt = tr.GetObject(
                db.ViewportTableId, OpenMode.ForWrite)
                as ViewportTable;

            ViewportTableRecord vtr1 = tr.GetObject(
                doc.Editor.ActiveViewportId,
                OpenMode.ForWrite) as ViewportTableRecord;

            Point2d ll = vtr1.LowerLeftCorner;
            Point2d ur = vtr1.UpperRightCorner;

            List<string> extentsKeys = dicExtents.Keys.ToList();

            vtr1.LowerLeftCorner = ll;
            vtr1.UpperRightCorner = new Point2d(
                ll.X + (ur.X - ll.X) * 0.5,
                ll.Y + (ur.Y - ll.Y));
            vtr1.SetViewDirection(OrthographicView.TopView);
            ZoomExtents(vtr1, dicExtents[extentsKeys[0]]);

            ViewportTableRecord vtr2 =
            CreateVTR(vt, vtr1,
            new Point2d(ll.X + (ur.X - ll.X) * 0.5, ll.Y + (ur.Y - ll.Y) * 0.5),
            new Point2d(ll.X + (ur.X - ll.X), ll.Y + (ur.Y - ll.Y)),
            dicExtents[extentsKeys[1]], OrthographicView.TopView);
            vt.Add(vtr2);
            tr.AddNewlyCreatedDBObject(vtr2, true);

            ViewportTableRecord vtr3 =

                CreateVTR(vt, vtr1,
                    new Point2d(ll.X + (ur.X - ll.X) * 0.5, ll.Y),
                    new Point2d(ll.X + (ur.X - ll.X), ll.Y + (ur.Y - ll.Y) * 0.5),
                dicExtents[extentsKeys[2]], OrthographicView.TopView);
            vt.Add(vtr3);
            tr.AddNewlyCreatedDBObject(vtr3, true);

            // Update the display with new tiled viewports 
            doc.Editor.UpdateTiledViewportsFromDatabase();

            // Commit the changes  
            tr.Commit();
        }
    }

}

/// <summary>
/// 将图纸作为一个块整个插入导数据库
/// </summary>
/// <param name="destDb"></param>
/// <param name="FileName">图纸的完整路径,dxf或者dwg都可以</param>
/// <returns></returns>
/// 
public static void ImportDrawingAsBlkstoCurDoc(Document destDoc, string strFileName, string savePath)
{
    using (DocumentLock docLock = destDoc.LockDocument())
    {
        using (Database db = new Database(false, true))
        {
            // Read the DWG into our side database
            db.ReadDwgFile(strFileName, FileShare.Read, true, "");
            // Create a list of block identifiers (will only contain one entry, the modelspace ObjectId)
            ObjectIdCollection ids = new ObjectIdCollection();
            // Start a transaction on the source database

            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                // Open the block table
                BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);

                // Add modelspace to list of blocks to import
                ids.Add(bt[BlockTableRecord.ModelSpace]);

                // Committing is cheaper than aborting
                tr.Commit();
            }
            // Copy our modelspace block from the source to

            // destination database

            // (will also copy required, referenced objects)

            IdMapping im = new IdMapping();

            db.WblockCloneObjects(
              ids,
              destDoc.Database.BlockTableId,
              im,
              DuplicateRecordCloning.MangleName,
              false
            );

            using (Transaction tr2 = destDoc.Database.TransactionManager.StartTransaction())
            {
                // Work through the results of the WblockClone
                foreach (IdPair ip in im)
                {
                    // Open each new destination object, checking for

                    // BlockTableRecords

                    BlockTableRecord btr = tr2.GetObject(ip.Value, OpenMode.ForRead) as BlockTableRecord;
                    if (btr != null)
                    {
                        // If the name starts with the modelspace string

                        if (btr.Name.StartsWith(BlockTableRecord.ModelSpace, StringComparison.InvariantCultureIgnoreCase))
                        {
                            // Get write access to it and change the name

                            // to that of the source drawing

                            btr.UpgradeOpen();
                            btr.Name = Path.GetFileNameWithoutExtension(strFileName);
                        }
                    }
                }
                db.UpdateExt(true);
                // We need to commit, as we've made changes
                tr2.Commit();
            }
            db.SaveAs(savePath, DwgVersion.Current);
        }
    }
}

public static ViewportTableRecord CreateVTR(
   ViewportTable vt, ViewportTableRecord refVTR,
   Point2d ll, Point2d ur, Extents3d dbExtent,
   OrthographicView ov)
{
    ViewportTableRecord newVTR = new ViewportTableRecord();

    newVTR.LowerLeftCorner = ll;
    newVTR.UpperRightCorner = ur;
    newVTR.Name = "*Active";

    newVTR.ViewDirection = refVTR.ViewDirection;
    newVTR.ViewTwist = refVTR.ViewTwist;
    newVTR.Target = refVTR.Target;
    newVTR.BackClipEnabled = refVTR.BackClipEnabled;
    newVTR.BackClipDistance = refVTR.BackClipDistance;
    newVTR.FrontClipEnabled = refVTR.FrontClipEnabled;
    newVTR.FrontClipDistance = refVTR.FrontClipDistance;
    newVTR.Elevation = refVTR.Elevation;
    newVTR.SetViewDirection(ov);

    ZoomExtents(newVTR, dbExtent);

    return newVTR;
}

public static void ZoomExtents
    (ViewportTableRecord vtr, Extents3d dbExtent)
{
    //get the screen aspect ratio to  
    // calculate the height and width 
    double scrRatio = (vtr.Width / vtr.Height);

    //prepare Matrix for DCS to WCS transformation 
    Matrix3d matWCS2DCS
        = Matrix3d.PlaneToWorld(vtr.ViewDirection);

    //for DCS target point is the origin 
    matWCS2DCS = Matrix3d.Displacement
        (vtr.Target - Point3d.Origin) * matWCS2DCS;

    //WCS Xaxis is twisted by twist angle 
    matWCS2DCS = Matrix3d.Rotation(-vtr.ViewTwist,
                                    vtr.ViewDirection,
                                    vtr.Target
                                ) * matWCS2DCS;

    matWCS2DCS = matWCS2DCS.Inverse();

    //tranform the extents to the DCS  
    // defined by the viewdir 
    dbExtent.TransformBy(matWCS2DCS);

    //width of the extents in current view 
    double width
         = (dbExtent.MaxPoint.X - dbExtent.MinPoint.X);

    //height of the extents in current view 
    double height
         = (dbExtent.MaxPoint.Y - dbExtent.MinPoint.Y);

    //get the view center point 
    Point2d center = new Point2d(
         (dbExtent.MaxPoint.X + dbExtent.MinPoint.X) * 0.5,
         (dbExtent.MaxPoint.Y + dbExtent.MinPoint.Y) * 0.5);

    //check if the width' in current window 
    //if not then get the new height as per the  
    // viewports aspect ratio 
    if (width > (height * scrRatio))
        height = width / scrRatio;

    vtr.Height = height;
    vtr.Width = height * scrRatio;
    vtr.CenterPoint = center;
}

【转载声明】转载本博客的文章请注明原始出处和作者

从acdbmgd.dll导的内容,方便参考,格式如下: 类:Autodesk.AutoCAD.DatabaseServices.TextStyleTableRecord:Autodesk.AutoCAD.DatabaseServices.SymbolTableRecord New() 方法: {dtor}() {dtor}() {dtor}() {dtor}() ApplyPaperOrientationTransform(Viewport) ApplyPartialUndo(DwgFiler,RXClass) Audit(AuditInfo) Cancel() Close() CloseAndPage(Boolean) CopyFrom(RXObject) CreateExtensionDictionary() DisableUndoRecording(Boolean) Dispose() DowngradeOpen() DowngradeToNotify(Boolean) DwgIn(DwgFiler) DwgOut(DwgFiler) DxfIn(DxfFiler) DxfOut(DxfFiler) Erase() Erase(Boolean) HandOverTo(DBObject,Boolean,Boolean) ReleaseExtensionDictionary() RemoveField(ObjectId) ResetScaleDependentProperties() SetObjectIdsInFlux() SetPaperOrientation(Boolean) SwapIdWith(ObjectId,Boolean,Boolean) SwapReferences(IdMapping) UpgradeOpen() ViewportDraw(ViewportDraw) XDataTransformBy(Matrix3d) 函数: Clone() AS System.Object CompareTo(Object) AS System.Int32 CreateObjRef(Type) AS System.Runtime.Remoting.ObjRef DecomposeForSave(DwgVersion) AS Autodesk.AutoCAD.DatabaseServices.DecomposeForSaveReplacementRecord DeepClone(DBObject,IdMapping,Boolean) AS Autodesk.AutoCAD.DatabaseServices.DBObject Equals(Object) AS System.Boolean GetHashCode() AS System.Int32 GetLifetimeService() AS System.Object GetObjectSaveVersion(DwgFiler) AS Autodesk.AutoCAD.DatabaseServices.FullDwgVersion GetObjectSaveVersion(DxfFiler) AS Autodesk.AutoCAD.DatabaseServices.FullDwgVersion GetPersistentReactorIds() AS Autodesk.AutoCAD.DatabaseServices.ObjectIdCollection GetReactors() AS System.Collections.Generic.List`1[[Autodesk.AutoCAD.Runtime.RXObject, acdbmgd, Version=17.1.0.0, Culture=neutral, PublicKeyToken=null]] GetRXClass() AS Autodesk.AutoCAD.Runtime.RXClass GetType() AS System.Type GetXDataForApplication(String) AS Autodesk.AutoCAD.DatabaseServices.ResultBuffer HasPersistentReactor(ObjectId) AS System.Boolean InitializeLifetimeService() AS System.Object QueryX(RXClass) AS System.IntPtr RemoveField() AS Autodesk.AutoCAD.DatabaseServices.ObjectId RemoveField(String) AS Autodesk.AutoCAD.DatabaseServices.ObjectId SetAttributes(DrawableTraits) AS System.Int32 SetField(Field) AS Autodesk.AutoCAD.DatabaseServices.ObjectId SetField(String,Field) AS Autodesk.AutoCAD.DatabaseServices.ObjectId SetFromStyle() AS System.Boolean ToString() AS System.String UpgradeFromNotify() AS System.Boolean ViewportDrawLogicalFlags(ViewportDraw) AS System.Int32 WblockClone(RXObject,IdMapping,Boolean) AS Autodesk.AutoCAD.DatabaseServices.DBObject WorldDraw(WorldDraw) AS System.Boolean X(RXClass) AS System.IntPtr 属性: AcadObject AS System.Object 可读不可写 Annotative AS Autodesk.AutoCAD.DatabaseServices.AnnotativeStates 可读可写 AutoDelete AS System.Boolean 可读可写 BigFontFileName AS System.String 可读可写 ClassID AS System.Guid 可读不可写 Database AS Autodesk.AutoCAD.DatabaseServices.Database 可读不可写 Drawable AS Autodesk.AutoCAD.GraphicsInterface.Drawable 可读不可写 DrawableType AS Autodesk.AutoCAD.GraphicsInterface.DrawableType 可读不可写 ExtensionDictionary AS Autodesk.AutoCAD.DatabaseServices.ObjectId 可读不可写 FileName AS System.String 可读可写 FlagBits AS System.Byte 可读可写 Font AS Autodesk.AutoCAD.GraphicsInterface.FontDescriptor 可读可写 Handle AS Autodesk.AutoCAD.DatabaseServices.Handle 可读不可写 HasFields AS System.Boolean 可读不可写 HasSaveVersionOverride AS System.Boolean 可读可写 Id AS Autodesk.AutoCAD.DatabaseServices.ObjectId 可读不可写 IsAProxy AS System.Boolean 可读不可写 IsCancelling AS System.Boolean 可读不可写 IsDependent AS System.Boolean 可读不可写 IsDisposed AS System.Boolean 可读不可写 IsErased AS System.Boolean 可读不可写 IsEraseStatusToggled AS System.Boolean 可读不可写 IsModified AS System.Boolean 可读不可写 IsModifiedGraphics AS System.Boolean 可读不可写 IsModifiedXData AS System.Boolean 可读不可写 IsNewObject AS System.Boolean 可读不可写 IsNotifyEnabled AS System.Boolean 可读不可写 IsNotifying AS System.Boolean 可读不可写 IsObjectIdsInFlux AS System.Boolean 可读不可写 IsPersistent AS System.Boolean 可读不可写 IsReadEnabled AS System.Boolean 可读不可写 IsReallyClosing AS System.Boolean 可读不可写 IsResolved AS System.Boolean 可读不可写 IsShapeFile AS System.Boolean 可读可写 IsTransactionResident AS System.Boolean 可读不可写 IsUndoing AS System.Boolean 可读不可写 IsVertical AS System.Boolean 可读可写 IsWriteEnabled AS System.Boolean 可读不可写 MergeStyle AS Autodesk.AutoCAD.DatabaseServices.DuplicateRecordCloning 可读可写 Name AS System.String 可读可写 ObjectBirthVersion AS Autodesk.AutoCAD.DatabaseServices.FullDwgVersion 可读不可写 ObjectId AS Autodesk.AutoCAD.DatabaseServices.ObjectId 可读不可写 ObliquingAngle AS System.Double 可读可写 OwnerId AS Autodesk.AutoCAD.DatabaseServices.ObjectId 可读可写 PaperOrientation AS Autodesk.AutoCAD.DatabaseServices.PaperOrientationStates 可读不可写 PriorSize AS System.Double 可读可写 TextSize AS System.Double 可读可写 UndoFiler AS Autodesk.AutoCAD.DatabaseServices.DwgFiler 可读不可写 UnmanagedObject AS System.IntPtr 可读不可写 XData AS Autodesk.AutoCAD.DatabaseServices.ResultBuffer 可读可写 XScale AS System.Double 可读可写 -------------------------------- 类:Autodesk.AutoCAD.DatabaseServices.TextVerticalMode:System.Enum 函数: CompareTo(Object) AS System.Int32 Equals(Object) AS System.Boolean GetHashCode() AS System.Int32 GetType() AS System.Type GetTypeCode() AS System.TypeCode ToString() AS System.String ToString(IFormatProvider) AS System.String ToString(String,IFormatProvider) AS System.String ToString(String) AS System.String 字段: TextBase AS TextVerticalMode TextBottom AS TextVerticalMode TextTop AS TextVerticalMode TextVerticalMid AS TextVerticalMode value__ AS Int32
### 如何在 AutoCAD 中设置或更改的缩放比例 #### 使用菜单栏调整图纸比例因子 为了修改图纸的比例,在AutoCAD中可以按照特定的操作流程完成。当需要改变图纸中的对象尺寸显示效果而不实际改变其大小时,可以通过以下方式操作:点击菜单栏上的“注释”,随后定位至“标注”。接着,留意标注右侧的小箭头并点击它进入更详细的选项界面。在此之后,“标注样式管理器”的弹会提供进一步的选择路径,其中选择“替代”能引导用户到达设定细节之处——即“替代当前样式”窗内的“测量单位比例->比例因子”处,这里能够输入新的数值比如0.75作为自定义的比例因子[^1]。 #### 设置布局空间下的比例 对于希望精确控制不同展示情况的设计者来说,了解如何配置布局里的各个窗显得尤为重要。一旦进入了具体的环境之中,便可以根据需求自由绘制形元素。值得注意的是,在一个中所做的改动可能会依据所选查看模式影响其他关联的内容呈现形式。例如,在东南等轴测(SE Isometric)中进行绘活动会使得该区域与其他正交之间形成联动效应,从而实现角度观察同一模型的效果优化[^2]。 #### 实时动态缩放功能的应用 除了上述较为固定的参数调节手段外,AutoCAD还提供了便捷直观的手动缩放工具供使用者即时调整屏幕野范围。只要保持命令激活状态,通过上下拖拽鼠标指针或是利用滚轮滚动动作即可轻松达成放大或缩小整个工作区画面的目的。这种交互式的操控方法不仅简化了操作流程而且提高了工作效率[^3]。 ```python # Python代码仅用于说明逻辑结构而非真实可用AutoCAD脚本 def zoom_in_out(action='in'): if action == 'in': move_cursor_upwards() # 假设函数表示向上移动光标以放大像 elif action == 'out': move_cursor_downwards() # 同理,假设这是用来描述向下滑动减少尺度的动作 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值