Тема: Рекомендую для хранения и получения информации использовать SetXData и GetXData

Настоятельно рекомендую для хранения и получения информации использовать SetXData GetXData. Очень удобная штука. От атрибутов для этих целей скорее всего вообще откажусь!!!!

Re: Рекомендую для хранения и получения информации использовать SetXData и GetXData

Кстати, для хранения еще хорошо использовать Dictionary  и XRecord

Re: Рекомендую для хранения и получения информации использовать SetXData и GetXData

Рад этому делу вместе с Вами! А из своего немалого опыта прибавлю совет по организации расширенных данных. Чтобы не было мучительно больно за проделанную работу, которая не будет вписываться в постоянно расширяемую эволюционирующую систему. Итак, предлагаю первые три позиции массива расширенных данных заполнять следующим образом:
1)

pDataType(0) = 1001: pDataValue(0) = ИмяПриложения

Это обязательный элемент - пояснений не требуется.
2)

pDataType(1) = 1000: pDataValue(1) = ИмяТипаНадстроечногоОбъекта

Здесь в позиции pDataValue(1) нужно (в том смысле, что очень удобно) хранить имя типа того объекта, который выстроен на базе данного объекта Автокада. Таким образом, во время инициализации приложения  можно прочитав этот элемент данных сразу определить какой объект требуется выстраивать. Как правило это производится с помощью конструкции Select Case ИмяТипаНадстроечногоОбъекта ? Для удобства программирования, а при отслеживании событий это единственный вариант, свойства объекта располагаются в модуле класса. Я ИмяТипаНадстроечногоОбъекта делаю равным имени модуля, тогда эта строчка кода (при размещении её внутри модуля класса) будет выглядеть так:

pDataType(1) = 1000: pDataValue(1) = TypeName(Me) 

3)

pDataType(2) = 1000: pDataValue(2) = ПолноеИмяОбъекта

Проект, как правило, имеет довольно сложную иерархическую структуру. Задавая ПолноеИмяОбъекта в виде аналогичном полному имени файла т.е. ИмяПроекта\ИмяОбъекта\ИмяПодобъекта\ИмяДетали можно легко найти место детали в проекте и выстроить программную надстройку.
4)
Остальные данные зависят от функциональной наполненности объекта. Иногда эти данные и вовсе не требуются.
Для примера приведу пару процедур.

Public Function SetShortXData(vEnt As AcadEntity, vAppName As String, _
            vTypeName As String, vFullName As String, _
            Optional ClearData As Boolean = False) As Boolean
Dim pDataType() As Integer
Dim pDataValue() As Variant
Dim i As Integer
    If ClearData Then
        ReDim pDataType(0)
        ReDim pDataValue(0)
        pDataType(0) = 1001: pDataValue(0) = vAppName
    Else
        ReDim pDataType(0 To 2)
        ReDim pDataValue(0 To 2)
        pDataType(0) = 1001: pDataValue(0) = "LpCAD"
        pDataType(1) = 1000: pDataValue(1) = vTypeName
        pDataType(2) = 1000: pDataValue(2) = vFullName
    End If
    On Error Resume Next
        vEnt.SetXData pDataType, pDataValue
    If Err Then
        Err.Clear
        ShortXData = False
    Else
        ShortXData = True
    End If
End Function

Этой процедурой(функцией) пользуюсь тогда, когда для объекта нет отдельного модуля класса, а привязать графический элемент к проекту необходимо. Опциональный параметр ClearData позволяет очистить расширенные данные, т.е. удалить их, в случае если он равен True.

 Public Function XTypeName(vAppName As String, vEnt As AcadEntity, _
            Optional vXT, Optional vXD) As String
Dim pXD As Variant
Dim pXT As Variant
    vEnt.GetXData vAppName, pXT, pXD
    If VarType(pXD) <> vbEmpty Then
        If pXT(1) = 1000 Then
            XTypeName = pXD(1)
        Else
           XTypeName = ""
        End If
    Else
        XTypeName = ""
    End If
    If Not IsMissing(vXT) Then vXT = pXT
    If Not IsMissing(vXD) Then vXD = pXD
End Function 

Эта функция позволяет без хлопот выстроить программный код по образу:

    Select Case XTypeName(ИмяПриложения, pE, pXT, pXD)
    Case "ИмяКлассаОбъекта1"
        ? Инициализация Объекта класса 1
    Case "ИмяКлассаОбъекта2"
        ? Инициализация Объекта класса 2
    ? ?
    Case Else
        ? Свободный объект
    End Select

Опциональные параметры pXT, pXD позволяют избежать повторного чтения расширенных данных в случае необходимости инициализации объекта класса ? Однако это дело техники а не общей программной политики.
Может быть изложенное где-то уже описано. Я кроме Автокадовской документации ничего не читал. Судя по отзывам, то тут то там, придётся книжки всё-таки покупать. Но поскольку тему обозначили, решил высказать свои соображения. Буду рад критике и дополнениям.

Re: Рекомендую для хранения и получения информации использовать SetXData и GetXData

> Сергей
Каких еще атрибутов?
Вы бы еще USER# не к ночи упомянули...

Re: Рекомендую для хранения и получения информации использовать SetXData и GetXData

Большое спасибо JS. Ситуация такая детали а проекте изображаются схематически - кубиками. И в одном кубике может быть несколько деталей. Вложенности нет. Т.е. один кубик и скажем 3 детали. Как тогда в таком случае сохранить информацию по каждой детале? По типу name, code, code of material, color.

Re: Рекомендую для хранения и получения информации использовать SetXData и GetXData

"в проекте"

Re: Рекомендую для хранения и получения информации использовать SetXData и GetXData

Для организации хранения остальных данных есть как минимум два подхода.
  Первый ? жёсткий. Все необходимые свойства должны быть разложены по соответствующим позициям массива при записи расширенных данных. Чтение организуется в том же порядке. Метод совершенно прозрачный, обеспечивается наибольшее быстродействие процесса загрузки информации. Для демонстрации метода нужно создать модуль класса MyCADMouth и поместить туда следующий код:

'MyCADMouth ClassModule
Option Explicit
Public ACO As AcadLWPolyline
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Private mName As String
Private mLength As Double
Private mCentreX As Double
Private mCentreY As Double
Private mCentreZ As Double
Private mWidth As Double
Private mAngle As Double
Private mScale As Double
Private mThicknessTop As Double
Private mThicknessBottom As Double
Private mAperture As Double
Private mDecentre As Double
Private mColor As acColor
Private mExpression As String 'Angry Even Glad Unknown
Private mTag As String
Private mValid As Boolean
Private Const mSaddle As Double = 0.5
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Property Get Name() As String
    Name = mName
End Property
Public Property Let Name(vNewName As String)
    mName = vNewName
    'Это слишком просто!!!
    'На самом деле здесь должен быть код обеспечивающий
    'доступ к этому объекту по имени.
End Property
Public Function Init(vLWP As AcadLWPolyline) As Boolean
Dim pErrDescr As String
    Set ACO = vLWP
    GetMyXData pErrDescr
    Init = pErrDescr = ""
End Function
'*****************************************************************************
'****   Запись данных в Автокад
'*****************************************************************************
Public Function SetMyXData(Optional ClearData As Boolean = False) As Boolean
Dim pT() As Integer
Dim pD() As Variant
Dim i As Integer
    If ClearData Then
        ReDim pT(0)
        ReDim pD(0)
        pT(0) = 1001: pD(0) = "LpCAD"
    Else
        ReDim pT(0 To 15)
        ReDim pD(0 To 15)
        pT(0) = 1001: pD(0) = "MyCAD"
        pT(1) = 1000: pD(1) = TypeName(Me)
        pT(2) = 1000: pD(2) = Name
        pT(3) = 1040: pD(3) = mCentreX
        pT(4) = 1040: pD(4) = mCentreY
        pT(5) = 1040: pD(5) = mCentreZ
        pT(6) = 1040: pD(6) = mWidth
        pT(7) = 1040: pD(7) = mAngle
        pT(8) = 1040: pD(8) = mScale
        pT(9) = 1040: pD(9) = mThicknessTop
        pT(10) = 1040: pD(10) = mThicknessBottom
        pT(11) = 1040: pD(11) = mAperture
        pT(12) = 1040: pD(12) = mDecentre
        pT(13) = 1070: pD(13) = mColor
        pT(14) = 1000: pD(14) = mExpression
        pT(15) = 1000: pD(15) = mTag
    End If
    On Error Resume Next
        ACO.SetXData pT, pD
    If Err Then Err.Clear Else SetMyXData = True
End Function
'*****************************************************************************
'****   Чтение данных из Автокада
'*****************************************************************************
Public Function GetMyXData(Optional vErrDescr As String) As Boolean
Dim s As String, vLen As Double
Dim xT As Variant, xD As Variant
Dim vTi As Integer
    vErrDescr = ""
    mValid = False
    ACO.GetXData "MyCAD", xT, xD
    If IsArray(xD) Then
        If UBound(xD) = 15 And TypeName(Me) = xD(1) Then
            mName = xD(2)
            mCentreX = xD(3)
            mCentreY = xD(4)
            mCentreZ = xD(5)
            mWidth = xD(6)
            mAngle = xD(7)
            mScale = xD(8)
            mThicknessTop = xD(9)
            mThicknessBottom = xD(10)
            mAperture = xD(11)
            mDecentre = xD(12)
            mColor = xD(13)
            mExpression = xD(14)
            mTag = xD(15)
            mValid = True
        Else
            vErrDescr = "Error XData or it is not " & TypeName(Me)
        End If
    Else
        vErrDescr = "Error XData"
    End If
    GetMyXData = vErrDescr = ""
End Function
Public Function IsGood(Optional vErrDescr As String) As Boolean
    Me.GetMyXData vErrDescr
    IsGood = vErrDescr = ""
End Function
Private Sub Class_Initialize()
    mCentreX = 0
    mCentreY = 0
    mCentreZ = 0
    mWidth = 1
    mAngle = 0
    mScale = 1
    mThicknessTop = 0.15
    mThicknessBottom = 0.3
    mAperture = 0
    mDecentre = 0
    mColor = acColor.acRed
    mExpression = "Even"
End Sub
Public Sub CreateMe(vBlock As AcadBlock, vName As String, Optional ByVal vCenter, Optional ByVal vScale As Double, Optional ByVal vExpression As String)
Dim pAV(0 To 5, 0 To 4) As Double
    mName = vName
    If Not IsMissing(vCenter) Then mCentreX = vCenter(0): mCentreY = vCenter(1): mCentreZ = vCenter(2)
    If Not IsMissing(vScale) Then mScale = vScale
    If Not IsMissing(vExpression) Then
        Select Case vExpression
        Case "Angry", "Glad": mExpression = vExpression
        Case Else: mExpression = "Even"
        End Select
    End If
    Select Case mExpression
    Case "Angry"
        mAperture = 0: mDecentre = -0.1
    Case "Even"
        mAperture = 0: mDecentre = 0
    Case "Glad"
        mAperture = 0.2: mDecentre = 0.2
    End Select
    '''
    pAV(0, 0) = mCentreX - mWidth * mScale / 2
    pAV(0, 1) = mCentreY + mDecentre * mScale
    pAV(0, 4) = -0.1
    pAV(1, 0) = mCentreX - mThicknessTop * mScale / 2
    pAV(1, 1) = mCentreY + mAperture * mScale / 2 + mThicknessTop * mScale
    pAV(1, 4) = mSaddle
    pAV(2, 0) = mCentreX + mThicknessTop * mScale / 2
    pAV(2, 1) = pAV(1, 1)
    pAV(2, 4) = -0.1
    pAV(3, 0) = mCentreX + mWidth * mScale / 2
    pAV(3, 1) = mCentreY + mDecentre * mScale
    pAV(3, 4) = (mAperture - 2 * mDecentre) / mWidth
    pAV(4, 0) = pAV(0, 0)
    pAV(4, 1) = pAV(0, 1)
    pAV(4, 4) = (mAperture + 2 * mDecentre) / mWidth
    pAV(5, 0) = pAV(3, 0)
    pAV(5, 1) = pAV(3, 1)
    pAV(5, 4) = -(mAperture + 2 * mDecentre + mThicknessBottom) / mWidth
    If Not ACO Is Nothing Then ACO.Delete
    Set ACO = CreateLWPolyFromAV(vBlock, pAV)
    ACO.Closed = True
    ACO.Color = mColor
    SetMyXData
End Sub 

Здесь кое что есть лишнее, кое чего не хватает. Прошу строго не судить.
Для работы этого класса необходима функция создания полилинии CreateLWPolyFromAV. Хотел я разместить эти полезные штучки в Готовых программах ? не дали. Привожу отдельно:

Public Function CreateLWPolyFromAV(vBlc As AcadBlock, vAV() As Double) As AcadLWPolyline
Dim pLWP As AcadLWPolyline
Dim pCrds() As Double
Dim nvx As Long, i As Long
    nvx = UBound(vAV, 1)
    ReDim pCrds(nvx * 2 + 1)
    For i = 0 To nvx
        pCrds(i * 2) = vAV(i, 0)
        pCrds(i * 2 + 1) = vAV(i, 1)
    Next i
    Set pLWP = vBlc.AddLightWeightPolyline(pCrds)
    For i = 0 To nvx
        pLWP.SetBulge i, vAV(i, 4)
        pLWP.SetWidth i, vAV(i, 2), vAV(i, 3)
    Next i
    Set LWPolyFromAV = pLWP
End Function

Теперь можно проверить, как это работает. В каком-либо глобальном модуле нужно разместить переменную

 Public ColAA As New Collection

И две процедуры. Первая ? AA создаст три экземпляра класса MyCadMouth . Вторая - InitAA обеспечит доступ т.е. восстановление доступа при открытии документа и т.д.

 Sub aa()
Dim pNM As New MyCadMouth
Dim pOM As MyCadMouth
Dim pC(2) As Double
Dim pACD As AcadDocument
    On Error Resume Next
    Set pACD = ThisDrawing
    For Each pOM In ColAA
        pOM.ACO.Delete
    Next pOM
    Set ColAA = Nothing
    If Err Then Err.Clear
    '''
    pC(1) = 100
    pNM.CreateMe pACD.ModelSpace, "First", pC, 60
    ColAA.Add pNM, pNM.Name
    Set pNM = Nothing
    '''
    pC(1) = 50
    pNM.CreateMe pACD.ModelSpace, "Second", pC, 60, "Glad"
    ColAA.Add pNM, pNM.Name
    Set pNM = Nothing
    '''
    pC(1) = 0
    pNM.CreateMe pACD.ModelSpace, "Third", pC, 60, "Angry"
    ColAA.Add pNM, pNM.Name
    Set pNM = Nothing
    '''
End Sub
Sub InitAA()
Dim pE As AcadEntity
Dim pNM As New MyCadMouth
Dim pACD As AcadDocument
Dim pOM As MyCadMouth
    Set ColAA = Nothing
    Set pACD = ThisDrawing
    For Each pE In pACD.ModelSpace
        Select Case XTypeName("MyCAD", pE)
        Case "MyCadMouth"
            If pNM.Init(pE) Then
                ColAA.Add pNM, pNM.Name
                Set pNM = Nothing
            End If
        Case Else
        End Select
    Next pE
    PrintMessage vbCrLf & vbCrLf & "В чертеже есть:"
    For Each pOM In ColAA
        PrintMessage vbCrLf & "    >" & pOM.Name
    Next pOM
    PrintMessage vbCrLf & "Конец инициализации." & vbCrLf
End Sub

Процедурку PrintMessage я уже раз пять приводил, больше не хочу.
Запускаем AA. Получаем ротики. Теперь можно сохранить и закрыть документ. После открытия запускаем InitAA. Программка обнаружит ротики и сообщит:
В чертеже есть:
    >First
    >Second
    >Third
Конец инициализации.
Пришлось выдумывать этот бесполезный класс, поэтому так задержался с ответом. Но как известно из классики, лучше тренироваться на кошках, не свои же рабочие коды слать. Другой способ организации хранения данных опишу в следующий раз. Спать охота. Всего хорошего.

Re: Рекомендую для хранения и получения информации использовать SetXData и GetXData

Спасибо огромное. Я правда нашел решение попроще. Но оно не такое красивое. Хранить всю инфу по каждой записи в отдельной строке разделяя их спец символами. Чтение данных и парсинг строки.

Re: Рекомендую для хранения и получения информации использовать SetXData и GetXData

>
JS

Жму пять! Спасибо и "увага" тебе!

Re: Рекомендую для хранения и получения информации использовать SetXData и GetXData

> pavel
Скор?ше "велике шанування".
:))

Re: Рекомендую для хранения и получения информации использовать SetXData и GetXData

Все здорово! Но такая проблема. Программа создает блок. И этот блок ввиде AcadBlockReference вставляется в ModelSpace... неважно куда вставляется ....
Фишка в том, что когда мы привязываем данные к блоку, то при вставке эти данные не появляются в AcadBlockReference. Они хранятся в конретном объекте AcadBlock.  А если данные давать в AcadBlockReference, то если в процессе работы юзер вставит этот блок, то блок в пространстве модели получится без данных. Через специальный диалог блоки вставлять. Что-бы X данные переходили от блока к  AcadBlockReference?

Re: Рекомендую для хранения и получения информации использовать SetXData и GetXData

И опять приходим к ненависным атрибутам. Можно было-бы привязать базу данных. Но к чему ее привяжешь, если значение ID объекта меняются при каждом открытии документаа т.е. зависят от сессий ...

Re: Рекомендую для хранения и получения информации использовать SetXData и GetXData

Как удалить расширенные данные объекта ???

Re: Рекомендую для хранения и получения информации использовать SetXData и GetXData

> Сергей
Но к чему ее привяжешь, если значение ID объекта меняются при каждом открытии документа...
А если привязать к Handle?

Re: Рекомендую для хранения и получения информации использовать SetXData и GetXData

Я не знаю что такое Handle. Но я приму это к сведению. Спасибо.

Re: Рекомендую для хранения и получения информации использовать SetXData и GetXData

Допустим, наш класс прекрасно отработал не один год. Но постепенно накопилось некоторое количество претензий которые должны повлечь введение новых переменных или аннулирование каких то из имеющихся. Можно выйти из положения, написав новый класс с новым именем, ну, например - MyCADMouth01 и перекопировать в него все, что было и осталось полезным. После этого дописать всё необходимое в том же духе, как и прежде. Так можно будет продержаться какое то время и немало. Однако наступит день, когда очень захочется выбросить всю кучу старого хлама и облегчить свой проект. Но что делать со старыми файлами, которых может быть уже много тысяч? После такого шага нормальный доступ к данным окажется прерванным. Может спасти применение одной или нескольких буферных переменных, в которых можно хранить неопределённое заранее число значений. Так в классе MyCADMouth есть строковая  переменная mTag в которую можно записать несколько значений новых переменных преобразованных в строку, используя символ (строку) разделитель обычно ;-точка с запятой. Необходимо помнить про одно ограничение при использовании этого подхода, это ограничение на длину строки в расширенных данных объекта ? 255 символов.
Такой подход обеспечивает большое долгожительство классов и хорошую совместимость старых и новых файлов.  Однако, есть ещё один способ обеспечивающий необыкновенную гибкость при хранении и работе с расширенными данными. Идея его в том, чтобы хранить значение свойства вместе с его именем в одном элементе массива расширенных данных. Естественно тип данных при этом будет только строковый. При таком подходе можно хранить только те данные, которые отличаются от значений по умолчанию. Один из способов реализации такого подхода приведён ниже в модуле классаMyCADFaceContour.

 'MyCADFaceContour ClassModule
Option Explicit
Public ACO As AcadLWPolyline
Public Enum EFaceContourType
    fcntUnknown = -1
    fcntOrdinary = 0
    fcntCook = 1
    fcntKoschei = 2
    fcntHunter = 3
End Enum
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Private mName As String
Private mCentre(0 To 2) As Double
Private mWidth As Double
Private mScaleWidth As Double
Private mHeight As Double
Private mScaleHeight As Double
Private mAngle As Double
Private mColor As acColor
Private mFaceContourType As EFaceContourType
Private mCheekHeight As Double
Private mChinWidth As Double
Private mCrownBulge As Double
Private mCheekBoneBulge As Double
Private mCheekBulge As Double
Private mChinBulge As Double
'''
Private Const mcWidth As Double = 1
Private Const mcScaleWidth As Double = 1
Private Const mcHeight As Double = 1
Private Const mcScaleHeight As Double = 1
Private Const mcAngle As Double = 0
Private Const mcColor As Integer = 2
Private Const mcFaceContourType As Integer = 0
Private mTag As String
Private mValid As Boolean
Private mLength As Double
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Property Get Name() As String
    Name = mName
End Property
Public Property Let Name(vNewName As String)
    mName = vNewName
    'Это слишком просто!!!
    'На самом деле здесь должен быть код обеспечивающий
    'доступ к этому объекту по имени.
End Property
Public Function Init(vLWP As AcadLWPolyline) As Boolean
Dim pErrDescr As String
    Set ACO = vLWP
    GetMyXData pErrDescr
    Init = pErrDescr = ""
End Function
'*****************************************************************************
'****   Запись данных в Автокад
'*****************************************************************************
Public Function SetMyXData(Optional ClearData As Boolean = False) As Boolean
Dim pT() As Integer
Dim pD() As Variant
Dim i As Long, ii As Long
Dim pColD As New Collection
Dim pZeroPoint(2) As Double
    If ClearData Then
        ReDim pT(0)
        ReDim pD(0)
        pT(0) = 1001: pD(0) = "MyCAD"
    Else
        pColD.Add "MyCAD"
        pColD.Add TypeName(Me)
        pColD.Add Name
        If Distance3D(mCentre, pZeroPoint) <> 0 Then pColD.Add _
            "Centre=" & CStr(mCentre(0)) & " " & CStr(mCentre(1)) & " " & CStr(mCentre(2))
        If mWidth <> mcWidth Then pColD.Add _
            "Width=" & CStr(mWidth)
        If mScaleWidth <> mcScaleWidth Then pColD.Add _
            "ScaleWidth=" & CStr(mScaleWidth)
        If mHeight <> mcHeight Then pColD.Add _
            "Height=" & CStr(mHeight)
        If mScaleHeight <> mcScaleHeight Then pColD.Add _
            "ScaleHeight=" & CStr(mScaleHeight)
        If mAngle <> mcAngle Then pColD.Add _
            "Angle=" & CStr(mAngle)
        If mColor <> mcColor Then pColD.Add _
            "Color=" & CStr(mColor)
        If mFaceContourType <> mcFaceContourType Then pColD.Add _
            "FaceContourType=" & CStr(mFaceContourType)
        '''
        ii = pColD.Count - 1
        ReDim pT(0 To ii)
        ReDim pD(0 To ii)
        For i = 0 To ii
            pT(i) = 1000
            pD(i) = pColD.Item(i + 1)
        Next i
        pT(0) = 1001
    End If
    On Error Resume Next
        ACO.SetXData pT, pD
    If Err Then Err.Clear Else SetMyXData = True
End Function
'*****************************************************************************
'****   Чтение данных из Автокада '*****************************************************************************
Public Function GetMyXData(Optional vErrDescr As String) As Boolean
Dim s As String, vLen As Double
Dim pT As Variant, pD As Variant, pV As Variant
Dim pMaxInd As Long, i As Long
Dim psH As String, psT As String
    vErrDescr = ""
    mValid = False
    ACO.GetXData "MyCAD", pT, pD
    If IsArray(pD) Then
        pMaxInd = UBound(pD)
        If pMaxInd >= 2 And TypeName(Me) = pD(1) Then
            mName = pD(2)
            For i = 3 To pMaxInd
                psH = HeadTailStr(pD(i), "=", psT)
                Select Case psH
                Case "Centre"
                    pV = Strings.Split(psT, " ")
                    mCentre(0) = CDbl(pV(0))
                    mCentre(1) = CDbl(pV(1))
                    mCentre(2) = CDbl(pV(2))
                Case "Width":           mWidth = CDbl(psT)
                Case "ScaleWidth":      mScaleWidth = CDbl(psT)
                Case "Height":          mHeight = CDbl(psT)
                Case "ScaleHeight":     mScaleHeight = CDbl(psT)
                Case "Angle":           mAngle = CDbl(psT)
                Case "Color":           mColor = CInt(psT)
                Case "FaceContourType": mFaceContourType = CInt(psT)
                End Select
            Next i
            mValid = True
        Else
            vErrDescr = "Error XData or it is not " & TypeName(Me)
        End If
    Else
        vErrDescr = "Error XData"
    End If
    GetMyXData = vErrDescr = ""
End Function
Public Function IsGood(Optional vErrDescr As String) As Boolean
    Me.GetMyXData vErrDescr
    IsGood = vErrDescr = ""
End Function
Private Sub Class_Initialize()
    mWidth = mcWidth
    mScaleWidth = mcScaleWidth
    mHeight = mcHeight
    mScaleHeight = mcScaleHeight
    mAngle = mcAngle
    mColor = mcColor
    mFaceContourType = mcFaceContourType
    mCheekHeight = 0.5
    mChinWidth = 0.2
    mCrownBulge = 1
    mCheekBoneBulge = 0.1
    mCheekBulge = 0.3
    mChinBulge = 0.2
End Sub
Public Sub CreateMe(vBlock As AcadBlock, vName As String, Optional ByVal vCenter, _
        Optional ByVal vScaleWidth As Double, Optional ByVal vScaleHeight As Double, _
        Optional ByVal vFaceContourType As EFaceContourType)
Dim pAV(0 To 5, 0 To 4) As Double
    mName = vName
    If Not IsMissing(vCenter) Then mCentre(0) = vCenter(0): mCentre(1) = vCenter(1): mCentre(2) = vCenter(2)
    If Not IsMissing(vScaleWidth) Then mScaleWidth = vScaleWidth
    If Not IsMissing(vScaleHeight) Then mScaleHeight = vScaleHeight
    If Not IsMissing(vFaceContourType) Then mFaceContourType = vFaceContourType
    Select Case mFaceContourType
    Case fcntCook: mCheekHeight = 0.7: mCrownBulge = 0.4: mCheekBulge = 1
    Case fcntHunter: mCheekHeight = 0.4: mCrownBulge = 0.6: mCheekBulge = 0.15: mChinWidth = 0.4
    Case fcntKoschei: mCheekHeight = 0.6: mCrownBulge = 1: mCheekBulge = -0.05
    End Select
    '''
    pAV(0, 0) = mCentre(0) - mWidth * mScaleWidth / 2
    pAV(0, 1) = mCentre(1) + mHeight * mScaleHeight
    pAV(0, 4) = mCheekBoneBulge
    pAV(1, 0) = pAV(0, 0)
    pAV(1, 1) = mCentre(1) + mCheekHeight * mScaleHeight
    pAV(1, 4) = mCheekBulge
    pAV(2, 0) = mCentre(0) - mChinWidth * mScaleWidth / 2
    pAV(2, 1) = mCentre(1)
    pAV(2, 4) = mChinBulge
    pAV(3, 0) = mCentre(0) + mChinWidth * mScaleWidth / 2
    pAV(3, 1) = pAV(2, 1)
    pAV(3, 4) = mCheekBulge
    pAV(4, 0) = mCentre(0) + mWidth * mScaleWidth / 2
    pAV(4, 1) = pAV(1, 1)
    pAV(4, 4) = mCheekBoneBulge
    pAV(5, 0) = pAV(4, 0)
    pAV(5, 1) = pAV(0, 1)
    pAV(5, 4) = mCrownBulge
    If Not ACO Is Nothing Then ACO.Delete
    Set ACO = Create.LWPolyFromAV(vBlock, pAV)
    ACO.Closed = True
    ACO.Color = mColor
    SetMyXData
End Sub 

Если обратить внимание на функции записи и чтения расширенных данных, то видно, что при записи будут записываться только переменные отличающиеся по значению от принятых по умолчанию. При считывании значения по умолчанию перекрываются считанными из расширенных данных значениями.
Для работы класса потребуются две функции Distance3D и HeadTailStr. Первая не требует пояснений. Вторая разделяет строку на две части голову и хвост, при помощи символа ? разделителя. Разделитель может содержать больше чем один символ. Эта функция удобна именно в этом случае, т.к. ищется первое вхождение символа разделителя. Так, если бы мы описывали строковую переменную и её значение содержало бы символ разделителя, это нам не помешает правильно извлечь значение. Т.е. строка ИмяПеременной=Y=a*X^2+b*X+c разложится на две: голова  ИмяПеременной, хвост Y=a*X^2+b*X+c. Вот эти функции.

Function Distance3D(t0 As Variant, t1 As Variant) As Double
    Distance3D = Sqr((t1(0) - t0(0)) ^ 2 + (t1(1) - t0(1)) ^ 2 + (t1(2) - t0(2)) ^ 2)
End Function
Public Function HeadTailStr( _
        ByVal InpStr As String, CtrlStr As String, _
        Optional TailStr As String) As String
Dim p1 As Long, p2 As Long, pL As Long
    If InpStr = "" Or CtrlStr = "" Then
        HeadTailStr = ""
        Exit Function
    End If
    pL = Strings.Len(CtrlStr)
    p1 = Strings.InStr(1, InpStr, CtrlStr, vbBinaryCompare)
    If p1 = 0 Then
        TailStr = ""
        HeadTailStr = InpStr
    Else
        TailStr = Strings.Right(InpStr, Strings.Len(InpStr) - p1 - pL + 1)
        HeadTailStr = Strings.Left(InpStr, p1 - 1)
    End If
End Function

Для проверки работоспособности класса используем две процедурки:

 Sub aa()
Dim pNM As New MyCadMouth, pOM As MyCadMouth
Dim pNFC As New MyCADFaceContour, pOFC As MyCADFaceContour
Dim pC(2) As Double
Dim pACD As AcadDocument
Dim pObj As Object
    On Error Resume Next
    Set pACD = ThisDrawing
    For Each pObj In ColAA
        pObj.ACO.Delete
    Next pObj
    Set ColAA = Nothing
    If Err Then Err.Clear
    '''
    pC(0) = 0: pC(1) = 0
    pNFC.CreateMe pACD.ModelSpace, "CookFaceContour", pC, 120, 150, fcntCook
    ColAA.Add pNFC, pNFC.Name
    Set pNFC = Nothing
    pC(0) = 0: pC(1) = 50
    pNM.CreateMe pACD.ModelSpace, "CookMouth", pC, 60, "Glad"
    ColAA.Add pNM, pNM.Name
    Set pNM = Nothing
    '''
    pC(0) = 200: pC(1) = 0
    pNFC.CreateMe pACD.ModelSpace, "HunterFaceContour", pC, 120, 150, fcntHunter
    ColAA.Add pNFC, pNFC.Name
    Set pNFC = Nothing
    pC(0) = 200: pC(1) = 50
    pNM.CreateMe pACD.ModelSpace, "HunterMouth", pC, 60
    ColAA.Add pNM, pNM.Name
    Set pNM = Nothing
    '''
    pC(0) = 400: pC(1) = 0
    pNFC.CreateMe pACD.ModelSpace, "KoscheiFaceContour", pC, 120, 150, fcntKoschei
    ColAA.Add pNFC, pNFC.Name
    Set pNFC = Nothing
    pC(0) = 400: pC(1) = 50
    pNM.CreateMe pACD.ModelSpace, "KoscheiMouth", pC, 50, "Angry"
    ColAA.Add pNM, pNM.Name
    Set pNM = Nothing
    '''
End Sub
Sub aaa()
Dim pE As AcadEntity, pObj As Object
Dim pNM As New MyCadMouth, pOM As MyCadMouth
Dim pNFC As New MyCADFaceContour, pOFC As MyCADFaceContour
Dim pACD As AcadDocument
    Set ColAA = Nothing
    Set pACD = ThisDrawing
    For Each pE In pACD.ModelSpace
        Select Case XTypeName("MyCAD", pE)
        Case "MyCadMouth"
            If pNM.Init(pE) Then
                ColAA.Add pNM, pNM.Name
                Set pNM = Nothing
            End If
        Case "MyCADFaceContour"
            If pNFC.Init(pE) Then
                ColAA.Add pNFC, pNFC.Name
                Set pNFC = Nothing
            End If
        Case Else
        End Select
    Next pE
    PrintMessage vbCrLf & vbCrLf & "В чертеже есть:"
    For Each pObj In ColAA
        PrintMessage vbCrLf & "    >" & pObj.Name
    Next pObj
    PrintMessage vbCrLf & "Конец инициализации." & vbCrLf
End Sub 

При выполнении aa в чертеже появляются почти симпатичные, почти рожицы. А при запуске aaa появляется сообщение обо всех обнаруженных объектах:

Command: -vbarun
Macro name: aaa
В чертеже есть:
    >CookFaceContour
    >CookMouth
    >HunterFaceContour
    >HunterMouth
    >KoscheiFaceContour
    >KoscheiMouth
Конец инициализации.

Описанные два способа можно комбинировать друг с другом, несколько усложняя алгоритм. Но, думаю, подход понятен: зафиксировать первые позиции для хранения типа объекта и его имени (полного имени), в остальных позициях разместить данные и в путь.

Re: Рекомендую для хранения и получения информации использовать SetXData и GetXData

Ужасный интернет по мобилке. Еле-еле отправил. Теперь текущие замечания:

> Romik
Обрати внимание на то как работает SetMyXData True
это как раз и снимет расширенные данные.

> Сергей
На мой взгляд, именно через специально самонаписанные диалоговые окна и надо управлять своими объектами. А для чего тогда это всё нужно. Стандартными свойствами можно неплохо орудовать стандартными Автокадовскими способами (почти всегда). А твои свойства, о которых знаешь только ты, управляются твоими программами. Это придаёт Автокаду уникальные свойства для решения уникальных задач.

Re: Рекомендую для хранения и получения информации использовать SetXData и GetXData

Проблема в юзерах. Как их заставить вставлять блоки не обычным способом скажем "_insert" а через написанные диалоги. Фиг заставишь!!!!

Re: Рекомендую для хранения и получения информации использовать SetXData и GetXData

Значит смысловая нагрузка нового объекта  слишком мала и не перевешивает неудобства или непривычность работы с ним. Два выхода: нагрузить так чтобы все охнули или ойкнули от удовольствия, и диалог сделать очень лёгким в обращении.

Re: Рекомендую для хранения и получения информации использовать SetXData и GetXData

Может быть ... Может быть ...

Re: Рекомендую для хранения и получения информации использовать SetXData и GetXData

Тут без lisp не обойтись для того что-бы сделать диалог адекватным. Т.к. юзер должен видеть куда вставляется блок а в VB это помоему невозможно.

Re: Рекомендую для хранения и получения информации использовать SetXData и GetXData

Без ЛИСПа в Автокаде действительно не обойтись. Но программировать в ЛИСПе это сомнительное удовольствие. Приходится крутиться.

Re: Рекомендую для хранения и получения информации использовать SetXData и GetXData

Согласен. Удовольсвие еще то. Только куча народа (наверное даже больше чем на VBA) делает это. И ... довольны ..

Re: Рекомендую для хранения и получения информации использовать SetXData и GetXData

Дело не в удовольствии, а в особой заточенности ЛИСПа под Автокад и исключительно под него. Поэтому народ там, на ЛИСП ветке, как правило более осведомлённый и цепкий. VBA он ведь теперь, стараниями Майкрософт, вездесущ. Это облегчает начальные усилия по созданию программ. А если попривыкнуть к нему, то потом очень сложно отказаться от многих замечательных особенностей и вольностей, которые облегчают жизнь и ускоряют программирование. Отладить программу на ЛИСПе - вешалка. Написать и отладить программу на C - жуть. Если нужно получить достаточно сложный продукт в ограниченное время, да ещё не очень понятно будет ли это работать, то VBA самое то, что надо.

Re: Рекомендую для хранения и получения информации использовать SetXData и GetXData

Я целиком и полностью согласен. Но если чесно иногода просто не хватает возможностей VB. Скажем я до сих пор не понял как работать с материалами для рендеринга в VB и как получить инфу о том какой материал назначен. Сейчас это неактуально но года три назад я намучался и забил на это ...