雪山飞狐_lzh 发表于 2009-8-18 11:37:00

二、更改动态块属性
March 06, 2009
Painting properties between dynamic AutoCAD blocks using .NET
In the last post we looked at some code that essentially dumped out the dynamic properties of dynamic blocks to the command-line. In this post we take it a step further and capture the properties from one dynamic block reference and attempt to apply them to another.
We're going to apply slightly different logic, depending on the situation...
If the block references refer to the same dynamic block definition (i.e. their DynamicBlockTableRecord property contains the same ObjectId) then we'll assume they have the same dynamic properties in the same order. So we'll go through and attempt to copy the property value from the first to the second. This may be a flawed approach, I don't know enough to say whether there are situations where dynamic blocks might have different sets of properties or properties in a different order... if it proves to be a problem, it'd be a simple matter to remove this clause and rely on the second approach for all dynamic block references.
If the block references refer to different dynamic block definitions (i.e. their DynamicBlockTableRecord property contains different ObjectIds) then we use a slightly more elaborate approach: we go through the dynamic properties of the "source" dynamic block reference and store them in a dictionary. We then go through the dynamic properties of the "target" dynamic block reference, and if we find a value in the list we then attempt to apply it to this reference. In this way we should be able to paint properties across blocks - as long as they have the same name - even if they don't have the same overall make-up.
Here's the C# code:
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using System.Collections.Generic;

namespace DynamicBlocks2
{
public class Commands
{
   
    static public void ApplyDynamicBlockProps()
    {
      Document doc =
      Application.DocumentManager.MdiActiveDocument;
      Database db = doc.Database;
      Editor ed = doc.Editor;

      // Select the source and target block references

      PromptEntityOptions peo =
      new PromptEntityOptions(
          "\nSelect source dynamic block reference: "
      );

      peo.SetRejectMessage("\nEntity is not a block.");
      peo.AddAllowedClass(typeof(BlockReference), false);

      PromptEntityResult per =
      ed.GetEntity(peo);

      if (per.Status != PromptStatus.OK)
      return;

      ObjectId sourceId = per.ObjectId;

      peo.Message =
      "\nSelect target dynamic block reference: ";
      per = ed.GetEntity(peo);

      if (per.Status != PromptStatus.OK)
      return;

      ObjectId targetId = per.ObjectId;

      Transaction tr =
      db.TransactionManager.StartTransaction();
      using (tr)
      {
      // Open our block references: the source (br1)
      // for read and the target (br2) for write

      BlockReference br1 =
          (BlockReference)tr.GetObject(
            sourceId,
            OpenMode.ForRead
          );

      BlockReference br2 =
          (BlockReference)tr.GetObject(
            targetId,
            OpenMode.ForWrite
          );

      // They must both be dynamic block references

      if (br1 != null && br1.IsDynamicBlock &&
            br2 != null && br2.IsDynamicBlock)
      {
          if (br1.DynamicBlockTableRecord ==
            br2.DynamicBlockTableRecord)
          {
            // They use the same block definition - let's assume
            // the properties are in the same order

            DynamicBlockReferencePropertyCollection pc1 =
            br1.DynamicBlockReferencePropertyCollection;

            DynamicBlockReferencePropertyCollection pc2 =
            br2.DynamicBlockReferencePropertyCollection;

            if (pc1.Count == pc2.Count)
            {
            for (int i = 0; i < pc1.Count; i++)
            {
                // Get each property. If they have the same
                // name and are not read-only, attempt to
                // copy the value from the source (prop1)
                // to the target (prop2)

                DynamicBlockReferenceProperty prop1 = pc1,
                                              prop2 = pc2;
                if (prop1.PropertyName == prop2.PropertyName &&
                  !prop1.ReadOnly && !prop2.ReadOnly)
                {
                  prop2.Value = prop1.Value;
                }
            }
            }
          }
          else
          {
            // If the block references refer to different
            // dynamic block definitions, let's collect the
            // properties for the first in a dictionary and
            // attempt to apply them to the second

            DynamicBlockReferencePropertyCollection pc1 =
            br1.DynamicBlockReferencePropertyCollection;

            // Create and populate our dictionary

            Dictionary<string, DynamicBlockReferenceProperty> dict =
            new
            Dictionary<string, DynamicBlockReferenceProperty>();

            foreach (DynamicBlockReferenceProperty prop in pc1)
            {
            if (!prop.ReadOnly &&
                  !dict.ContainsKey(prop.PropertyName))
                dict.Add(prop.PropertyName, prop);
            }

            // Try to copy them to the target block reference's
            // dynamic properties

            DynamicBlockReferencePropertyCollection pc2 =
            br2.DynamicBlockReferencePropertyCollection;

            foreach (DynamicBlockReferenceProperty prop2 in pc2)
            {
            if (!prop2.ReadOnly &&
                  dict.ContainsKey(prop2.PropertyName))
            {
                try
                {
                  DynamicBlockReferenceProperty prop1;
                  if (dict.TryGetValue(
                        prop2.PropertyName, out prop1
                  )
                  )
                  {
                  if (prop2.PropertyTypeCode ==
                        prop1.PropertyTypeCode)
                  {
                      prop2.Value = prop1.Value;
                  }
                  }
                }
                // Expand if you want to diagnose specific issues
                catch { }
            }
            }
          }
      }
      else
      {
          ed.WriteMessage(
            "\nYou need to select two dynamic bock references."
          );
      }
      tr.Commit();
      }
    }
}
}
Let's see what happens when we put our ADBP command through its paces. Here's our initial state - we have two differently-sized instances of the same dynamic block, plus one instance of a different dynamic block (which shares parameters - the left-hand block is a "Hex Socket Bolt (Side)" while the right-hand block is a "Hex Nut" - both these blocks are available in the "Mechanical - Metric.dwg" sample file in the Samples\Dynamic Blocks folder of your AutoCAD installation).

Dynamic block references
Here's where we are when we run our ADBP command and select the block reference at the top followed by the block reference at the bottom-left:

Dynamic block references - after paint from top to bottom elevation view
So we see it works on references of the same dynamic block definition. Now let's try it, selecting one of the left-hand elevation views on the bolt, followed by the plan view of the nut on the right:

Dynamic block references - after paint from elevation to plan view
Because they share a dynamic property ("Size", which went from "M10" to "M14"), we were able to "paint" from one to the other.
With AutoCAD 2010 you'll be able to create constraints between geometry - which is a much simpler and more powerful approach - but what we've seen in this post should be of use to people working with dynamic blocks today.
By the way - for those of you with ADN website access - I used this DevNote as inspiration for how to modify properties in a dynamic block reference: How to programmatically insert a dynamic block with attributes containing dynamic parameters? (requires login.) Thanks to Fernando for pointing me to it (at that point in my research I had not quite gotten as far as checking the ADN website... ahem), as well as for suggesting this topic in the first place.

雪山飞狐_lzh 发表于 2009-9-6 19:31:00

uly 15, 2009
Adding a dynamic distance property to an AutoCAD object using .NET
Back in these previous posts, we introduced an ObjectARX module that exposed AutoCAD’s Property Palette to .NET (thanks again to Cyrille Fauvel for his work on this :-). In this post we’re going to build on this work to demonstrate how to integrate properties with more advanced editing controls into the Property Palette.

This code is being built on top of the sample project provided with the OPM .NET implementation, which I’ve posted previously for AutoCAD 2007-2009 and for AutoCAD 2010. It defines a custom dynamic property – much like the original CustomProp, an integer property – but this one implements a new interface, IAcPiPropertyDisplay. This special interface allows you to – among other things – specify a custom editing control, which gets instantiated by the Property Palette and is then used to allow editing of the property.
The ObjectARX documentation contains this information on the main method we’re interested in implementing, GetCustomPropertyCtrl():
    Supplies a custom ActiveX control for property display/edit on a per-property basis.
    Before attempting to pick one of the ACPEX stock custom ActiveX controls for property display/edit, the Property Inspector calls this method to see if the object/command wishes to supply its own custom ActiveX control. This method is called on a per-property basis (both static and dynamic) for an object/command whose properties are active for display in the Property Inspector.
    The supplied custom control must implement IPropertyEditCtrl to be displayed for property value display/edit. The Property Inspector releases the output BSTR pointer after its use.
We want to enable this interface for use from .NET. In the previous cases we’ve had to do some extra work to expose or enable our Property Palette interfaces for use from .NET, but luckily this work has already been done for AutoCAD’s Tool Palette implementation. We can now simply add an assembly reference to AcTcMgd.dll and include the Autodesk.AutoCAD.Windows.ToolPalette namespace in our code.

When we implement our GetCustomPropertyCtrl() method, we then need to set the string output argument to the ProgId of the editing control we wish to use. To decide an appropriate ProgId, I looked into the Registry, to identify the candidates:

Various AcPEXCtl ProgIds in the Registry
It’s also possible to implement your own truly custom editing control exposing the IPropertyEditCtrl interface, but that’s way beyond the scope of this post. :-)
For our custom distance property, we’re going to use a (standard) custom control that allows the user to select two points in the drawing; the distance between them then gets set as our dynamic property’s value. The ProgId for this control is “AcPEXCtl.AcPePick2PointsCtrl.16”.
Otherwise the methods of IAcPiPropertyDisplay should be fairly straightforward to understand, although perhaps IsFullView() requires a little explanation: the “visible” parameter actually indicates whether the control should take up the whole of the row – obscuring the property name – which is why we set it to false.
One final note: I’ve remapped the GUID for the property, to prevent it from conflicting with the previous CustomProp implementation.
Here’s the C# code for our custom distance property:
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Windows.OPM;
using Autodesk.AutoCAD.Windows.ToolPalette;
using System;
using System.Reflection;
using System.Runtime.InteropServices;

namespace OPMNetSample
{
#region Our Custom Distance Property
[
    Guid("78286EBD-7380-4bb5-9EA6-1742677FA2E3"),
    ProgId("OPMNetSample.DistanceProperty.1"),
    ClassInterface(ClassInterfaceType.None),
    ComDefaultInterface(typeof(IDynamicProperty2)),
    ComVisible(true)
]
public class DistanceProp : IDynamicProperty2, IAcPiPropertyDisplay
{
    private IDynamicPropertyNotify2 _pSink = null;
    private double _value = 0.0;

    #region IDynamicProperty2 methods

    // Unique property ID

    public void GetGUID(out Guid propGUID)
    {
      propGUID =
      new Guid("78286EBD-7380-4bb5-9EA6-1742677FA2E3");
    }

    // Property display name

    public void GetDisplayName(out string szName)
    {
      szName = "Distance";
    }

    // Show/Hide property in the OPM, for this object instance

    public void IsPropertyEnabled(object pUnk, out int bEnabled)
    {
      bEnabled = 1;
    }

    // Is property showing but disabled

    public void IsPropertyReadOnly(out int bReadonly)
    {
      bReadonly = 0;
    }

    // Get the property description string

    public void GetDescription(out string szName)
    {
      szName =
      "This distance property is from picking two points";
    }

    // OPM will typically display these in an edit field
    // optional: meta data representing property type name,
    // ex. ACAD_ANGLE

    public void GetCurrentValueName(out string szName)
    {
      throw new System.NotImplementedException();
    }

    // What is the property type, ex. VT_R8

    public void GetCurrentValueType(out ushort varType)
    {
      // The Property Inspector supports the following data
      // types for dynamic properties:
      // VT_I2 (2), VT_I4 (3), VT_R4 (4), VT_R8 (5),
      // VT_BSTR (8), VT_BOOL (11), and VT_USERDEFINED (29).

      varType = 4; // VT_R4
    }

    // Get the property value, passes the specific object
    // we need the property value for.

    public void GetCurrentValueData(object pUnk, ref object pVarData)
    {
      pVarData = _value;
    }

    // Set the property value, passes the specific object we
    // want to set the property value for

    public void SetCurrentValueData(object pUnk, object varData)
    {
      _value = (double)varData;
    }

    // OPM passes its implementation of IDynamicPropertyNotify, you
    // cache it and call it to inform OPM your property has changed

    public void Connect(object pSink)
    {
      _pSink = (IDynamicPropertyNotify2)pSink;
    }

    public void Disconnect() {
      _pSink = null;
    }

    #endregion

    #region IAcPiPropertyDisplay methods

    public void GetCustomPropertyCtrl(
      object id, uint lcid, out string progId
    )
    {
      progId = "AcPEXCtl.AcPePick2PointsCtrl.16";
    }

    public void GetPropertyWeight(object id, out int propertyWeight)
    {
      propertyWeight = 0;
    }

    public void GetPropertyIcon(object id, out object icon)
    {
      icon = null;
    }

    public void GetPropTextColor(object id, out uint textColor)
    {
      textColor = 0;
    }

    public void IsFullView(
      object id, out bool visible, out uint integralHeight
    )
    {
      visible = false;
      integralHeight = 1;
    }
    #endregion
}
#endregion

#region Application Entry Point
public class MyEntryPoint : IExtensionApplication
{
    protected internal DistanceProp distProp = null;

    public void Initialize()
    {
      Assembly.LoadFrom("asdkOPMNetExt.dll");

      // Add the Dynamic Property

      Dictionary classDict = SystemObjects.ClassDictionary;
      RXClass lineDesc = (RXClass)classDict.At("AcDbLine");
      IPropertyManager2 pPropMan =
      (IPropertyManager2)xOPM.xGET_OPMPROPERTY_MANAGER(lineDesc);

      distProp = new DistanceProp();
      pPropMan.AddProperty((object)distProp);
    }

    public void Terminate()
    {
      // Remove the Dynamic Property

      Dictionary classDict = SystemObjects.ClassDictionary;
      RXClass lineDesc = (RXClass)classDict.At("AcDbLine");
      IPropertyManager2 pPropMan =
      (IPropertyManager2)xOPM.xGET_OPMPROPERTY_MANAGER(lineDesc);

      pPropMan.RemoveProperty((object)distProp);
      distProp = null;
    }
}
#endregion
}Here’s what we see when we NETLOAD the asdkOPMNetExt.dll and our OPMNetSample.dll, and then select a standard line:

Our new dynamic distance property on the line object
When we select the property we see we get a custom editing icon:

Our new dynamic distance property with its pick two points function
When we click that button, we get prompted to select two points in the drawing:
Pick a start point in the drawing:
Pick an end point in the drawing:
Once we’ve done so, the distance between the two points gets assigned to our property:

Bear in mind that we’re currently storing this value with the property, not with the line itself. So you’ll see the same value irrespective of the line you’ve selected. See the note at the beginning of this post if you wish to go further and attach this data per object.
I’m going to spend some time looking further into the capabilities of the custom control mechanism: in the next post in this series we’ll look at hosting a masked string (as in a password) dynamic property inside the Property Palette. If you have suggestions for additional controls to implement, please let me know (and I’ll see what I can figure out :-).

雪山飞狐_lzh 发表于 2009-9-6 19:36:00

July 20, 2009
Adding a dynamic password property to an AutoCAD object using .NET
In this recent post we looked at adding custom editing capabilities for dynamic properties we’ve added via .NET. In the first example we looked at a “distance” property which provided a button allowing the user to select two points. In this post we’ll look at implementing a masked string property, such as one you would use for a password.
I won’t repeat too much of the background information from the last post in this series: you should refer to that to understand the fundamentals (the fact that we’re basing this implementation on Cyrille Fauvel’s OPM .NET sample, that we’re not dealing with per-object persistence, and that we’re using “stock” ActiveX editing controls we’ve discovered via the Registry). That said, the code we’re providing here stands alone from the code in the last post, even if it’s altogether possible to combine them.
Here is the C# code to implement a simple, masked string dynamic property:
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Windows.OPM;
using Autodesk.AutoCAD.Windows.ToolPalette;
using System;
using System.Reflection;
using System.Runtime.InteropServices;

namespace OPMNetSample
{
#region Our Custom Password Property
[
    Guid("B5D9010E-48A2-4bfb-8601-76A7B1007C68"),
    ProgId("OPMNetSample.PasswordProperty.1"),
    ClassInterface(ClassInterfaceType.None),
    ComDefaultInterface(typeof(IDynamicProperty2)),
    ComVisible(true)
]
public class PasswordProp : IDynamicProperty2, IAcPiPropertyDisplay
{
    private IDynamicPropertyNotify2 _pSink = null;
    private string _password = "";

    #region IDynamicProperty2 methods

    // Unique property ID

    public void GetGUID(out Guid propGUID)
    {
      propGUID =
      new Guid("B5D9010E-48A2-4bfb-8601-76A7B1007C68");
    }

    // Property display name

    public void GetDisplayName(out string szName)
    {
      szName = "Password";
    }

    // Show/Hide property in the OPM, for this object instance

    public void IsPropertyEnabled(object pUnk, out int bEnabled)
    {
      bEnabled = 1;
    }

    // Is property showing but disabled

    public void IsPropertyReadOnly(out int bReadonly)
    {
      bReadonly = 0;
    }

    // Get the property description string

    public void GetDescription(out string szName)
    {
      szName =
      "This masked property is a string";
    }

    // OPM will typically display these in an edit field
    // optional: meta data representing property type name,
    // ex. ACAD_ANGLE

    public void GetCurrentValueName(out string szName)
    {
      throw new System.NotImplementedException();
    }

    // What is the property type, ex. VT_R8

    public void GetCurrentValueType(out ushort varType)
    {
      // The Property Inspector supports the following data
      // types for dynamic properties:
      // VT_I2 (2), VT_I4 (3), VT_R4 (4), VT_R8 (5),
      // VT_BSTR (8), VT_BOOL (11), and VT_USERDEFINED (29).

      varType = 8; // VT_BSTR
    }

    // Get the property value, passes the specific object
    // we need the property value for.

    public void GetCurrentValueData(object pUnk, ref object pVarData)
    {
      pVarData = _password;
    }

    // Set the property value, passes the specific object we
    // want to set the property value for

    public void SetCurrentValueData(object pUnk, object varData)
    {
      _password = varData.ToString();
      System.Windows.Forms.MessageBox.Show(
      "String entered was \"" + _password + "\"."
      );
    }

    // OPM passes its implementation of IDynamicPropertyNotify, you
    // cache it and call it to inform OPM your property has changed

    public void Connect(object pSink)
    {
      _pSink = (IDynamicPropertyNotify2)pSink;
    }

    public void Disconnect() {
      _pSink = null;
    }

    #endregion

    #region IAcPiPropertyDisplay methods

    public void GetCustomPropertyCtrl(
      object id, uint lcid, out string progId
    )
    {
      progId = "AcPEXCtl.AcPePropertyEditorPwdText.16";
    }

    public void GetPropertyWeight(object id, out int propertyWeight)
    {
      propertyWeight = 0;
    }

    public void GetPropertyIcon(object id, out object icon)
    {
      icon = null;
    }

    public void GetPropTextColor(object id, out uint textColor)
    {
      textColor = 0;
    }

    public void IsFullView(
      object id, out bool visible, out uint integralHeight
    )
    {
      visible = false;
      integralHeight = 1;
    }
    #endregion
}
#endregion

#region Application Entry Point
public class MyEntryPoint : IExtensionApplication
{
    protected internal PasswordProp passProp = null;

    public void Initialize()
    {
      Assembly.LoadFrom("asdkOPMNetExt.dll");

      // Add the Dynamic Property

      Dictionary classDict = SystemObjects.ClassDictionary;
      RXClass lineDesc = (RXClass)classDict.At("AcDbLine");
      IPropertyManager2 pPropMan =
      (IPropertyManager2)xOPM.xGET_OPMPROPERTY_MANAGER(lineDesc);

      passProp = new PasswordProp();
      pPropMan.AddProperty((object)passProp);
    }

    public void Terminate()
    {
      // Remove the Dynamic Property

      Dictionary classDict = SystemObjects.ClassDictionary;
      RXClass lineDesc = (RXClass)classDict.At("AcDbLine");
      IPropertyManager2 pPropMan =
      (IPropertyManager2)xOPM.xGET_OPMPROPERTY_MANAGER(lineDesc);

      pPropMan.RemoveProperty((object)passProp);
      passProp = null;
    }
}
#endregion
}
Let’s see what happens when we NETLOAD the asdkOPMNetExt.dll and our OPMNetSample.dll, and then select a standard line:

Once we start entering a string inside our dynamic password property, we see it displayed as a masked string:

And once we finish entering our string, we can see that our code has the unmasked data available to it – here’s a MessageBox we throw up to display the contents:

If we really wanted to store this securely – to avoid any determined application from snooping the underlying data – then we would really need to encrypt it before assigning it to our object (whether in Xdata or an Xrecord stored in the extension dictionary of an object). Beyond that we might even want to take measures to make sure the variable – as stored in memory, not just persisted with the object – isn’t readable by external applications. But again, this is beyond the scope of this post, which is really just to introduce another property editing control available inside AutoCAD.

雪山飞狐_lzh 发表于 2009-9-9 08:01:00

创建尺寸式样替代
September 07, 2009
Creating and overriding AutoCAD dimension styles using .NET
A request came in by email during last week’s vacation:
    I have been looking around to find a way about creating Dimension Style Overrides, but have not really had any success at anything yet. I have created program in which I do create several dimension styles, but I just keep getting lost with the overrides.
This seemed like a really good topic to cover, so this post contains some simple code that to create a dimension style and two nearly-identical linear dimensions: both use our newly-created dimension style but the second of the two also contains some Dimension Style Overrides, which we attach to the dimension via External Entity Data (XData).
Here’s the C# code:
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;

namespace DimStyleOverrideTest
{
public class Commands
{
   
    public void OverrideDimensionStyle()
    {
      Database db =
      HostApplicationServices.WorkingDatabase;

      Transaction tr =
      db.TransactionManager.StartTransaction();
      using (tr)
      {
      // Open our dimension style table to add our
      // new dimension style

      DimStyleTable dst =
          (DimStyleTable)tr.GetObject(
            db.DimStyleTableId, OpenMode.ForWrite
          );

      // Create our new dimension style

      DimStyleTableRecord dstr =
          new DimStyleTableRecord();
      dstr.Dimtad = 2;
      dstr.Dimgap = 0.3;
      dstr.Name = "MyStyle";

      // Add it to the dimension style table

      ObjectId dsId = dst.Add(dstr);
      tr.AddNewlyCreatedDBObject(dstr, true);

      // Now create two identical dimensions, one
      // next to the other, using our dimension
      // style

      AlignedDimension ad1 =
          new AlignedDimension(
            Point3d.Origin,
            new Point3d(5.0, 0.0, 0.0),
            new Point3d(2.5, 2.0, 0.0),
            "Standard dimension",
            dsId
          );

      // The only thing we change is the text string

      AlignedDimension ad2 =
          new AlignedDimension(
            new Point3d(5.0, 0.0, 0.0),
            new Point3d(10.0, 0.0, 0.0),
            new Point3d(7.5, 2.0, 0.0),
            "Overridden dimension",
            dsId
          );

      /*

      Now we'll add dimension overrides for DIMTAD
      and DIMGAP via XData

      Dimension variable group codes are:

          DIMPOST   3
          DIMAPOST    4
          DIMSCALE   40
          DIMASZ   41
          DIMEXO   42
          DIMDLI   43
          DIMEXE   44
          DIMRND   45
          DIMDLE   46
          DIMTP      47
          DIMTM      48
          DIMTOL   71
          DIMLIM   72
          DIMTIH   73
          DIMTOH   74
          DIMSE1   75
          DIMSE2   76
          DIMTAD   77
          DIMZIN   78
          DIMAZIN    79
          DIMTXT    140
          DIMCEN    141
          DIMTSZ    142
          DIMALTF   143
          DIMLFAC   144
          DIMTVP    145
          DIMTFAC   146
          DIMGAP    147
          DIMALTRND 148
          DIMALT    170
          DIMALTD   171
          DIMTOFL   172
          DIMSAH    173
          DIMTIX    174
          DIMSOXD   175
          DIMCLRD   176
          DIMCLRE   177
          DIMCLRT   178
          DIMADEC   179
          DIMDEC    271
          DIMTDEC   272
          DIMALTU   273
          DIMALTTD274
          DIMAUNIT275
          DIMFRAC   276
          DIMLUNIT277
          DIMDSEP   278
          DIMATMOVE 279
          DIMJUST   280
          DIMSD1    281
          DIMSD2    282
          DIMTOLJ   283
          DIMTZIN   284
          DIMALTZ   285
          DIMALTTZ286
          DIMUPT    288
          DIMATFIT289
          DIMTXSTY340
          DIMLDRBLK 341
          DIMBLK    342
          DIMBLK1   343
          DIMBLK2   344
          DIMLWD    371
          DIMLWE    372

      Variables have different types: these can be found in
      the ObjectARX Reference - search for "Dimension Style
      Overrides"

      */

      ResultBuffer rb =
          new ResultBuffer(
            new TypedValue{
            new TypedValue(
                (int)DxfCode.ExtendedDataRegAppName, "ACAD"
            ),
            new TypedValue(
                (int)DxfCode.ExtendedDataAsciiString, "DSTYLE"
            ),
            new TypedValue(
                (int)DxfCode.ExtendedDataControlString, "{"
            ),
            new TypedValue(
                (int)DxfCode.ExtendedDataInteger16, 77// DIMTAD
            ),
            new TypedValue(
                (int)DxfCode.ExtendedDataInteger16, 4   // Below
            ),
            new TypedValue(
                (int)DxfCode.ExtendedDataInteger16, 147 // DIMGAP
            ),
            new TypedValue(
                (int)DxfCode.ExtendedDataReal, 0.5      // Larger
            ),
            new TypedValue(
                (int)DxfCode.ExtendedDataControlString, "}"
            )
            }
          );

      // Set the XData on our object

      ad2.XData = rb;
      rb.Dispose();

      // Now let's open the current space and add our two
      // dimensions

      BlockTableRecord btr =
          (BlockTableRecord)tr.GetObject(
            db.CurrentSpaceId,
            OpenMode.ForWrite
          );

      btr.AppendEntity(ad1);
      btr.AppendEntity(ad2);

      tr.AddNewlyCreatedDBObject(ad1, true);
      tr.AddNewlyCreatedDBObject(ad2, true);

      // And commit the transaction, of course

      tr.Commit();
      }
    }
}
}When we NETLOAD our application and run the ODS command, we see that two dimensions are created in the current space:

I won’t spend time on the specifics of the various dimension variables you can override using this mechanism – I’m sure many (if not most) of you know much more about AutoCAD’s dimension mechanism than I – but I have included the list of the various dimension variables’ group codes in a comment in the above code. For more information regarding the specific types of these variables – and their ranges, if applicable – I suggest searching for “Dimension Style Override” in the ObjectARX Reference.
Update
OK, I missed the obvious on this one (as does happen from time-to-time, as regular readers will by now be aware). Rather than setting the overrides directly via XData, there are handy properties belonging to the dimension’s managed interface that do this for you. Very cool. So we can reduce the code to the following:
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;

namespace DimStyleOverrideTest
{
public class Commands
{
   
    public void OverrideDimensionStyle()
    {
      Database db =
      HostApplicationServices.WorkingDatabase;

      Transaction tr =
      db.TransactionManager.StartTransaction();
      using (tr)
      {
      // Open our dimension style table to add our
      // new dimension style

      DimStyleTable dst =
          (DimStyleTable)tr.GetObject(
            db.DimStyleTableId, OpenMode.ForWrite
          );

      // Create our new dimension style

      DimStyleTableRecord dstr =
          new DimStyleTableRecord();
      dstr.Dimtad = 2;
      dstr.Dimgap = 0.3;
      dstr.Name = "MyStyle";

      // Add it to the dimension style table

      ObjectId dsId = dst.Add(dstr);
      tr.AddNewlyCreatedDBObject(dstr, true);

      // Now create two identical dimensions, one
      // next to the other, using our dimension
      // style

      AlignedDimension ad1 =
          new AlignedDimension(
            Point3d.Origin,
            new Point3d(5.0, 0.0, 0.0),
            new Point3d(2.5, 2.0, 0.0),
            "Standard dimension",
            dsId
          );

      // The only thing we change is the text string

      AlignedDimension ad2 =
          new AlignedDimension(
            new Point3d(5.0, 0.0, 0.0),
            new Point3d(10.0, 0.0, 0.0),
            new Point3d(7.5, 2.0, 0.0),
            "Overridden dimension",
            dsId
          );

      // Isn't this easier?

      ad2.Dimtad = 4;
      ad2.Dimgap = 0.5;

      // Now let's open the current space and add our two
      // dimensions

      BlockTableRecord btr =
          (BlockTableRecord)tr.GetObject(
            db.CurrentSpaceId,
            OpenMode.ForWrite
          );

      btr.AppendEntity(ad1);
      btr.AppendEntity(ad2);

      tr.AddNewlyCreatedDBObject(ad1, true);
      tr.AddNewlyCreatedDBObject(ad2, true);

      // And commit the transaction, of course

      tr.Commit();
      }
    }
}
}I managed to forget to name the style in the previous example, so I’ve gone ahead and fixed that in both sets of code.
And it turns out that the original question may actually have been about something different, so I’m now going to go away and look at that (and will create a new post, as needed).

雪山飞狐_lzh 发表于 2009-9-14 16:42:00

十六、操纵新创建填充的绘图顺序
March 30, 2007
Manipulating the draw order of a newly created AutoCAD hatch using .NET
This question came in from Limin as a comment in this previous post:
    Is there .Net interface for autocad command "DrawOrder"? For example: after solid-hatch a polyline, need to reorder polyline and hatch, how to make it happen using .NET?
I've put together some C# code to demonstrate how to do this:
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
namespace HatchDrawOrder
{
public class Commands
{
   
    static public void HatchDrawOrder()
    {
      Document doc =
      Application.DocumentManager.MdiActiveDocument;
      Editor ed = doc.Editor;
      // Ask the user to select a hatch boundary
      PromptEntityOptions opt =
      new PromptEntityOptions(
          "\nSelect boundary: "
      );
      opt.SetRejectMessage("\nObject must be a curve.");
      opt.AddAllowedClass(typeof(Curve), false);
      PromptEntityResult res =
      ed.GetEntity(opt);
      if (res.Status == PromptStatus.OK)
      {
      Transaction tr =
          doc.TransactionManager.StartTransaction();
      using (tr)
      {
          // Check the entity is a closed curve
          DBObject obj =
            tr.GetObject(
            res.ObjectId,
            OpenMode.ForRead
            );
          Curve cur = obj as Curve;
          if (cur != null && cur.Closed == false)
          {
            ed.WriteMessage("\nLoop must be a closed curve.");
          }
          else
          {
            // Add the hatch to the model space
            BlockTable bt =
            (BlockTable)tr.GetObject(
                doc.Database.BlockTableId,
                OpenMode.ForRead
            );
            BlockTableRecord btr =
            (BlockTableRecord)tr.GetObject(
                bt,
                OpenMode.ForWrite
            );
            Hatch hat = new Hatch();
            hat.SetDatabaseDefaults();
            hat.SetHatchPattern(
            HatchPatternType.PreDefined,
            "SOLID"
            );
            ObjectId hatId = btr.AppendEntity(hat);
            tr.AddNewlyCreatedDBObject(hat, true);
            // Add the hatch loop and complete the hatch
            ObjectIdCollection ids =
            new ObjectIdCollection();
            ids.Add(res.ObjectId);
            hat.Associative = true;
            hat.AppendLoop(
            HatchLoopTypes.Default,
            ids
            );
            hat.EvaluateHatch(true);
            // Change the draworder of both hatch and loop
            btr.DowngradeOpen();
            ids.Add(hatId);
            DrawOrderTable dot =
            (DrawOrderTable)tr.GetObject(
                btr.DrawOrderTableId,
                OpenMode.ForWrite
            );
            dot.MoveToBottom(ids);
            tr.Commit();
          }
      }
      }
    }
}
}
The code to manipulate the draw order of these two entities is very simple - we have to open the DrawOrderTable for the space containing the entities we care about, and use it to change the order in which the entities are drawn. In this case I chose to send them both to the back of the draw order stack, but you might also manipulate their draw order relative to other entities in the drawing.
On a personal note, my family and I have arrived safely in Beijing - we're all a bit jet-lagged, but very happy to be here. The journey was thankfully uneventful, and with two young children that's really as much as you can hope for. :-)

雪山飞狐_lzh 发表于 2010-2-3 23:05:00

十七、创建非矩形的视口
December 14, 2009
Creating non-rectangular paperspace viewports in AutoCAD using .NET
Thanks to Philippe Leefsma, from DevTech in Europe, for the ObjectARX code that inspired this post.
In AutoCAD it’s possible to create non-rectangular viewports in paperspace using a variety of closed curve objects: circles, polylines (2D, 3D and lightweight), ellipses, regions, splines and faces. In this post we’re going to see some code that creates four new viewports in the paperspace of the active drawing using a subset of these objects: an Ellipse, a Circle, a closed Spline and a closed Polyline.
Here’s the C# code:
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;

namespace ViewportCreation
{
public class Commands
{
   
    static public void CreateNonRectangularViewports()
    {
      Document doc =
      Application.DocumentManager.MdiActiveDocument;
      Database db = doc.Database;
      Editor ed = doc.Editor;

      // We're accessing drawing objects, so we need a transaction

      Transaction tr = db.TransactionManager.StartTransaction();
      using (tr)
      {
      // Get the primary paperspace from the block table

      BlockTable bt =
          (BlockTable)tr.GetObject(
            db.BlockTableId,
            OpenMode.ForRead
          );
      BlockTableRecord ps =
          (BlockTableRecord)tr.GetObject(
            bt,
            OpenMode.ForWrite
          );

      // Create a variety of objects for our clip boundaries

      DBObjectCollection objs = new DBObjectCollection();

      // An ellipse...

      Ellipse el =
          new Ellipse(
            new Point3d(3.5, 4.7, 0),
            Vector3d.ZAxis,
            new Vector3d(1.4, 0.03, 0),
            0.35, 0, 0
          );
      objs.Add(el);

      // A circle...

      Circle cir =
          new Circle(
            new Point3d(3.4, 1.9, 0),
            Vector3d.ZAxis,
            0.9
          );
      objs.Add(cir);

      // A closed polyline...

      Polyline pl =
          new Polyline(6);
      pl.AddVertexAt(0, new Point2d(4.92, 5.29), 0, 0, 0);
      pl.AddVertexAt(1, new Point2d(5.16, 6.02), 0, 0, 0);
      pl.AddVertexAt(2, new Point2d(6.12, 6.49), 0, 0, 0);
      pl.AddVertexAt(3, new Point2d(7.29, 6.26), -0.27, 0, 0);
      pl.AddVertexAt(4, new Point2d(8.11, 5.53), -0.47, 0, 0);
      pl.AddVertexAt(5, new Point2d(7.75, 5.41), 0, 0, 0);
      pl.Closed = true;
      objs.Add(pl);

      // A closed spline...

      Point3dCollection pts =
          new Point3dCollection(
            new Point3d[] {
            new Point3d (5.5, 2.06, 0),
            new Point3d (5.26, 2.62, 0),
            new Point3d (5.66, 4.16, 0),
            new Point3d (8.56, 4.21, 0),
            new Point3d (7.2, 0.86, 0),
            new Point3d (6.44, 2.85, 0),
            new Point3d (5.62, 1.8, 0),
            new Point3d (5.5, 2.06, 0)
            }
          );
      Spline sp = new Spline(pts, 2, 0.5);
      objs.Add(sp);

      // Add each to the paperspace blocktablerecord
      // and create/add an associated viewport object

      foreach (DBObject obj in objs)
      {
          Entity ent = obj as Entity;
          if (ent != null)
          {
            // Add our boundary to paperspace and the
            // transaction

            ObjectId id = ps.AppendEntity(ent);
            tr.AddNewlyCreatedDBObject(obj, true);

            // Create our viewport, adding that also

            Viewport vp = new Viewport();
            ps.AppendEntity(vp);
            tr.AddNewlyCreatedDBObject(vp, true);

            // Set the boundary entity and turn the
            // viewport/clipping on

            vp.NonRectClipEntityId = id;
            vp.NonRectClipOn = true;
            vp.On = true;
          }
      }
      tr.Commit();
      }

      // Let's take a look at the results in paperspace

      db.TileMode = false;
    }
}
}Here’s what happens when we draw some geometry in modelspace:

And then call the NRVPS command to create our non-rectangular viewports

lzx838 发表于 2010-2-4 09:43:00

[转帖]直线延长-Extending a set of AutoCAD lines

A nice little one to start the week. We’re going to ask the user to select a bunch of lines and we’ll then go through and edit each one, extending it in both directions by a quarter of its original length.
The code shows a couple of interesting techniques: aside from extending the lines themselves we also use a SelectionFilter in combination with a PromptSelectionOptions object to restrict the selection process from selecting anything but lines (and to give a customized experience by changing the selection prompt).
我也不明白KEN要写这个实例,但对于初学者来说:这也算是个不错的例子:
1>过滤选择直线对象
2>如何延长直线的长度
C#代码如下:      
      public void ExtendLines()
      {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            //使用一个选择对象,指定我们想选择的直线
            PromptSelectionOptions pso = new PromptSelectionOptions();
            pso.MessageForAdding = "\nSelect lines: ";
            TypedValue[] tvs = new TypedValue { new TypedValue((int)DxfCode.Start, "LINE") };
            SelectionFilter sf = new SelectionFilter(tvs);
            //选择对象
            PromptSelectionResult psr = ed.GetSelection(pso, sf);
            if (psr.Status != PromptStatus.OK)
            {
                return;
            }
            //当选择集不为空时
            if (psr.Value.Count > 0)
            {
                //打开事务
                Transaction tr = db.TransactionManager.StartTransaction();
                using (tr)
                {
                  //编辑选定的每一条直线
                  foreach (SelectedObject so in psr.Value)
                  {
                        //我们假定只有线的选择集
                        //也可以使用更多的判断办法,
                        //使用动态转换(Line ln = xxx as Line;)
                        Line ln = (Line)tr.GetObject(so.ObjectId, OpenMode.ForWrite);
                        //我们将直线两个方向比现在延长多四分之一
                        Vector3d ext = ln.Delta / 4;
                        // 直线起点
                        ln.Extend(true, ln.StartPoint - ext);
                        // 直线终点
                        ln.Extend(false, ln.EndPoint + ext);
                  }
                  //别忘记提示事务
                  tr.Commit();
                }
            }
      }



雪山飞狐_lzh 发表于 2010-11-29 15:29:14

本帖最后由 lzh741206 于 2010-11-29 15:39 编辑

十八、块参照的剪裁Adding a 2D spatial filter to perform a simple xclip on an external reference in AutoCAD using .NET
I’ve now checked in for my flight to Las Vegas – and, thanks to Jeremy Tammik’s recent troubles, I luckily renewed myESTA – so I’m pretty much all set for my trip to AU 2010, at least from a travel perspective. I’m just keeping my fingers crossed that the gastric 'flu my kids seem to have come down with doesn’t hit me before I leave (or once I’m at AU… what a grim thought).

I was going to keep today’s post light, just like the last one, but then decided to dip into my email folder of externally contributed posts. This little nugget from Jamie Robertson shows how to add a simple 2D spatial filter to an external reference, effectively duplicating the XCLIP command (although this basic sample doesn’t handle non-World UCSs, block rotation/scaling, etc.).

At some point I may look at implementing a more complete version of this command, but even as it stands I have no doubt it’ll be of use to many of you. Many thanks for contributing this code, Jamie!

Here’s the C# code, with minor modifications to fit this blog: using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices.Filters;
using Autodesk.AutoCAD.Runtime;
using System;


namespace SpatialFiltering
{
public class ClipBlock
{
    const string filterDictName = "ACAD_FILTER";
    const string spatialName = "SPATIAL";

   
    public static void clipBlockTest()
    {
      Document doc =
      Application.DocumentManager.MdiActiveDocument;
      Database db = doc.Database;
      Editor ed = doc.Editor;

      // Select a block to clip
      PromptEntityOptions peo =
      new PromptEntityOptions(
          "\nSelect a block reference to clip:"
      );

      peo.AllowNone = false;
      peo.SetRejectMessage("\nNot a block reference.");
      peo.AddAllowedClass(typeof(BlockReference), false);
      PromptEntityResult per = ed.GetEntity(peo);
      if (per.Status != PromptStatus.OK)
      return;

      // Select rectangle to clip
      PromptPointOptions ppo =
      new PromptPointOptions(
          "\nSelect first clip corner: "
      );

      ppo.AllowNone = false;
      PromptPointResult ppr = ed.GetPoint(ppo);
      if (ppr.Status != PromptStatus.OK)
      return;
      Point3d pt1 = ppr.Value;

      PromptCornerOptions pco =
      new PromptCornerOptions(
          "\nSelect second clip corner: ", pt1
      );

      ppr = ed.GetCorner(pco);
      if (ppr.Status != PromptStatus.OK)
      return;
      Point3d pt2 = ppr.Value;

      Transaction tr = db.TransactionManager.StartTransaction();
      using (tr)
      {
      // Transform and re-order points
      BlockReference br =
          (BlockReference)tr.GetObject(
            per.ObjectId, OpenMode.ForRead
          );

      pt1 = pt1.TransformBy(br.BlockTransform.Inverse());
      pt2 = pt2.TransformBy(br.BlockTransform.Inverse());
      Point2d rectMin =
          new Point2d(
            Math.Min(pt1.X, pt2.X),
            Math.Min(pt1.Y, pt2.Y)
          );

      Point2d rectMax =
          new Point2d(
            Math.Max(pt1.X, pt2.X),
            Math.Max(pt1.Y, pt2.Y)
          );

      Point2dCollection pts =
          new Point2dCollection(2){ rectMin, rectMax };
      // Create spatial filter
      // Just zeroes for the elevation and clip depths
      SpatialFilterDefinition sfd =
          new SpatialFilterDefinition(
            pts, Vector3d.ZAxis, 0.0, 0.0, 0.0, true
          );
      SpatialFilter sf = new SpatialFilter();
      sf.Definition = sfd;

      // Create extension dictionary if doesn't exist
      if (br.ExtensionDictionary == ObjectId.Null)
      {
          br.UpgradeOpen();
          br.CreateExtensionDictionary();
          br.DowngradeOpen();
      }

      // Add spatial filter to extension dictionary
      DBDictionary xDict =
          (DBDictionary)tr.GetObject(
            br.ExtensionDictionary, OpenMode.ForWrite
          );
      if (xDict.Contains(filterDictName))
      {
          DBDictionary fDict =
            (DBDictionary)tr.GetObject(
            xDict.GetAt(filterDictName), OpenMode.ForWrite
            );
          if (fDict.Contains(spatialName))
            fDict.Remove(spatialName);
          fDict.SetAt(spatialName, sf);
      }
      else
      {
          DBDictionary fDict = new DBDictionary();
          xDict.SetAt(filterDictName, fDict);
          tr.AddNewlyCreatedDBObject(fDict, true);
          fDict.SetAt(spatialName, sf);
      }
      tr.AddNewlyCreatedDBObject(sf, true);
      tr.Commit();
      }
      ed.Regen();
    }
}
}Here’s an xrefed drawing – the venerable HVAC Architectural sample drawing, no less:

And here’s the effect of running the 2DXCLIP command, selecting the Xref (the grey geometry) and a window to which to clip:

That’s it until AU – I’m looking forward to catching up with many of you in Las Vegas! :-)

雪山飞狐_lzh 发表于 2012-7-26 18:50:18

本帖最后由 雪山飞狐_lzh 于 2012-7-26 18:51 编辑


July 06, 2007
Applying a gradient fill to an AutoCAD hatch using .NET
So, back in the saddle after an eventful week off, back in the UK. Aside from the extremely changeable weather (even by British standards) and the heightened security at UK airports, the week went swimmingly... :-)

It's now Friday night, and disappointingly I've spent nearly two days regaining control of my inbox. I really wanted to make a quick post before the weekend, so rather than diving into the issue I'd planned on tackling (Palettes), I decided to take a shot at a request I'd received by email a few weeks ago: to show how to create a gradient fill using .NET.

I started by reusing the code from this previous post: so much of the code I needed was there already, it had to be easy to adjust the hatch to use a gradient fill, right? Well, no - it took me much longer than I'd expected to work this one out. Maybe I should have stuck with Palettes, after all...

I decided to use a spherical gradient fill of two colours (the default colours used when you create a two-tone gradient fill using the UI), as this shows one of the trickier parts of the API, that of specifying the colours themselves. One other call it took me a while to work out was to set the HatchObjectType property - if you don't do that then you'll get an eAmbiguousOutput exception.

Here's the C# code:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.Colors;



namespace HatchGradient

{

public class Commands

{

   

    static public void GradientFillHatch()

    {

      Document doc =

      Application.DocumentManager.MdiActiveDocument;

      Editor ed = doc.Editor;



      // Ask the user to select a hatch boundary

      PromptEntityOptions opt =

      new PromptEntityOptions(

          "\nSelect boundary: "

      );

      opt.SetRejectMessage("\nObject must be a curve.");

      opt.AddAllowedClass(typeof(Curve), false);

      PromptEntityResult res =

      ed.GetEntity(opt);



      if (res.Status == PromptStatus.OK)

      {

      Transaction tr =

          doc.TransactionManager.StartTransaction();

      using (tr)

      {

          // Check the entity is a closed curve

          DBObject obj =

            tr.GetObject(

            res.ObjectId,

            OpenMode.ForRead

            );

          Curve cur = obj as Curve;

          if (cur != null && cur.Closed == false)

          {

            ed.WriteMessage("\nLoop must be a closed curve.");

          }

          else

          {

            // We'll add the hatch to the model space

            BlockTable bt =

            (BlockTable)tr.GetObject(

                doc.Database.BlockTableId,

                OpenMode.ForRead

            );

            BlockTableRecord btr =

            (BlockTableRecord)tr.GetObject(

                bt,

                OpenMode.ForWrite

            );



            Hatch hat = new Hatch();

            hat.SetDatabaseDefaults();



            // Firstly make it clear we want a gradient fill

            hat.HatchObjectType =

            HatchObjectType.GradientObject;



            // Let's use the pre-defined spherical gradient

            hat.SetGradient(

            GradientPatternType.PreDefinedGradient,

            "SPHERICAL");



            // We're defining two colours

            hat.GradientOneColorMode = false;

            GradientColor[] gcs = new GradientColor;

            gcs =

            new GradientColor(

                Color.FromRgb(0, 0, 255),

                0 // First colour must have value of 0

            );

            gcs =

            new GradientColor(

                Color.FromRgb(255, 255, 153),

                1 // Second colour must have value of 1

            );

            hat.SetGradientColors(gcs);



            // Add the hatch to the model space

            // and the transaction

            ObjectId hatId = btr.AppendEntity(hat);

            tr.AddNewlyCreatedDBObject(hat, true);



            // Add the hatch loop and complete the hatch

            ObjectIdCollection ids =

            new ObjectIdCollection();

            ids.Add(res.ObjectId);

            hat.Associative = true;

            hat.AppendLoop(

            HatchLoopTypes.Default,

            ids

            );



            hat.EvaluateHatch(true);



            tr.Commit();

          }

      }

      }

    }

}

}


Here's what you get when you run the GFH command and select a polyline boundary you've created previously:



That's it for this week - have a great weekend, all of you.
页: 1 2 [3]
查看完整版本: Kean专题(9)—Object_Properties