.NET
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Some AutoCAD classes entities as Source for PropertyGrid control = Fatal Error

6 REPLIES 6
Reply
Message 1 of 7
Anonymous
1164 Views, 6 Replies

Some AutoCAD classes entities as Source for PropertyGrid control = Fatal Error

Windows XP SP3 x86 Rus, AutoCAD 2009 SP3 x86 Rus, .Net Framework 3.5 SP1, MS Visual Studio 2010.

I want use PropertyGrid control in window my AutoCAD plagin's for browse drawing database entities. But it is big problem in AutoCAD: I have always fatal error for some AutoCAD entities, when these used as ObjectSource property for PropertyGrid.


For example (fatal source😞 Database, BlockTable, BlockTableRecord, Circle and many other!
Video here and Code source here (MS VS 2010, AutoCAD 2009 SP3 x86).


Screen for quick understanding:


 

6 REPLIES 6
Message 2 of 7
Anonymous
in reply to: Anonymous

You can't simply assign DBObjects to the PropertyGrid's SelectedObject property,

because it will try to access the object's properties every time it refreshes its

view (e.g, when the control is painted).

 

To do this, you have to implement an object that acts as a wrapper for the

DBObject, and on that object, you have to implement ICustomTypeDescriptor,

and that must return a collection of custom PropertyDescriptor objects that

know how to open the DBObject in order to get each property's value. Then

you assign the wrapper object to the PropertyGrid's SelectedObject property.

 

That's how my own tool (shown below) does it.

 

Message 3 of 7
Anonymous
in reply to: Anonymous

 

Thanks for the answer!

 

>You can't simply assign DBObjects to the PropertyGrid's SelectedObject property,

because it will try to access the object's properties every time it refreshes its

view (e.g, when the control is painted).

 

Than it is dangerous?

 

But DBObject already is a controlled wrapper for class AcDbObject. Class Autodesk. AutoCAD.DatabaseServices. DBObject it is flagged by attribute [Wrapper ("AcDbObject")]. I am right?

 

And all other entities, wich I use in my code as source for PropertyGrid, too is managed wrappers for unmanaged classes already.

Must I write a wrapper over DBObject / Document / Database / e.t.c., wich is wrapper already?

 

______________________________________________

P.S. Excuse me for my bad English...

 

Message 4 of 7
Anonymous
in reply to: Anonymous


@Compositum wrote:

 

Than it is dangerous?

 

But DBObject already is a controlled wrapper for class AcDbObject. Class Autodesk. AutoCAD.DatabaseServices. DBObject it is flagged by attribute [Wrapper ("AcDbObject")]. I am right?

 

And all other entities, wich I use in my code as source for PropertyGrid, too is managed wrappers for unmanaged classes already.

Must I write a wrapper over DBObject / Document / Database / e.t.c., wich is wrapper already?

 

______________________________________________

P.S. Excuse me for my bad English...

 


Yes, it's dangerous.

 

Yep, DBObject is a 'wrapper' for the native AcDbObject, but that doesn't

change the fact that the DBObject wrapper cannot be used indiscriminately

at any time.

 

A DBObject is only usable while the transaction it was obtained from is active.

 

While there may be some cases where you can use a DBObject after the

transaction you get it from has ended (for read-only use only) that is entirely

undocumented/unsupported behavior, and doesn't always work, depending

on the type of object, and members being accessed.

 

So, you need a 'wrapper' for the DBObject wrapper, and that wrapper must

implement ICustomTypeDescrptor. The GetProperties() methods of that interface

must return a collection of custom PropertyDescriptor objects that know how to

access the properties of a DBObject given its ObjectId.

 

You only need wrappers for DBObjects because of the special requirements

I noted above. For other types of objects that do not have those constraints.

 

Message 5 of 7
Anonymous
in reply to: Anonymous

Many thanks! Now I understand.

 

Tell me please, how aright implementation  ICustomTypeDescriptor for DBObject?

 

I write it (russian / english comments):

 

 

    public class PropDispNameWrapper : ICustomTypeDescriptor
  {
    // Оборачиваемый объект (source object).
    private object _obj;
    // Коллекция, хранящая обертки над описаниями свойств (collection of description properties).
    PropertyDescriptorCollection _propsCollection;

    // Позволяет получить обернутый объект (get wrapped object).
    public object Unwrap{ get{ return _obj; } }

    public PropDispNameWrapper(object obj)
    {
      // Запоминаем оборачиваемый объект (remember source object link).
        _obj = obj;
        
      // Создаем новую (пустую) коллекцию описаний свойств, 
      // в которую поместим обертки над реальными описаниями (Create new (clear) collection of property descriptions and put wrappers for real descriptions).
      _propsCollection = new PropertyDescriptorCollection(null);
      PropertyDescriptorCollection pdc = 
          TypeDescriptor.GetProperties(obj, true);
      // Перебираем описания свойств, создаем для каждого 
      // из них обертку и помещаем ее в коллекцию (description properties iteration. Create wpapper for each, and add in collection).
      foreach(PropertyDescriptor pd in pdc)
        _propsCollection.Add(new MyPropDesc(pd));
    }

    /////////////////////////////////////////////////////////
    /// ICustomTypeDescriptor
    ///
    System.ComponentModel.AttributeCollection ICustomTypeDescriptor.GetAttributes() 
    {
      return new  System.ComponentModel.AttributeCollection(null);
    }

    string ICustomTypeDescriptor.GetClassName() 
    {
      return null;
    }

    string ICustomTypeDescriptor.GetComponentName() 
    {
      return null;
    }

    TypeConverter ICustomTypeDescriptor.GetConverter() 
    {
      return null;
    }

    EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() 
    {
      return null;
    }


    PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() 
    {
      return null;
    }

    object ICustomTypeDescriptor.GetEditor(Type editorBaseType) 
    {
      return null;
    }

    EventDescriptorCollection ICustomTypeDescriptor.GetEvents() 
    {
      return new EventDescriptorCollection(null);
    }

    EventDescriptorCollection ICustomTypeDescriptor.GetEvents
        (Attribute[] attributes) 
    {
      return new EventDescriptorCollection(null);
    }

    PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() 
    {
      return _propsCollection;
    }

    PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(
        Attribute[] attributes) 
    {
      return _propsCollection;
    }

    object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) 
    {
      return this;
    }
  }

  public class MyPropDesc : PropertyDescriptor
  {

    PropertyDescriptor _PropDesc;

    public MyPropDesc(PropertyDescriptor PropDesc) : base(PropDesc)
    {
      _PropDesc = PropDesc;
    }
    public override string Category{ get{ return _PropDesc.Category; } }
  
    // Это свойство возвращает название свойства, 
    // отображаемое в propertyGrid (It's property, which return displayed property name).
    public override string DisplayName 
    { 
      get 
      {
        // Пытаемся получить атрибут DisplayNameAttribute.
          // В случае неудачи будет возвращен null (try get attribute DisplayNameAttribute. If fail - return null).
        DisplayNameAttribute mna = 
            _PropDesc.Attributes[typeof(DisplayNameAttribute)] as 
            DisplayNameAttribute;
        if(mna != null)
          // Если имеется атрибут DisplayNameAttribute,
            // возвращаем текст, помещенный в него (If DisplayNameAttribute assign, then return text, which in this).
          return mna.ToString();
        // Если атрибут DisplayNameAttribute не задан,
        // возвращаем оригинальное имя свойства (If DisplayNameAttribute not assign, then return original name of property). 
        return _PropDesc.Name;
      }
    }

    public override Type ComponentType 
    {
      get 
      {
        return _PropDesc.ComponentType;
      }
    }

    public override bool IsReadOnly 
    {
      get 
      {
        return false;
      }
    }

    public override Type PropertyType 
    {
      get 
      {
        return _PropDesc.PropertyType;
      }
    }

    public override bool CanResetValue(object component) 
    {
      return _PropDesc.CanResetValue(((PropDispNameWrapper)component).Unwrap);
    }

    public override object GetValue(object component) 
    {
      return _PropDesc.GetValue(((PropDispNameWrapper)component).Unwrap);
    }

    public override void ResetValue(object component) 
    {
      _PropDesc.ResetValue(((PropDispNameWrapper)component).Unwrap);
    }

    public override void SetValue(object component, object value) 
    {
      _PropDesc.SetValue(((PropDispNameWrapper)component).Unwrap, value);
    }

    public override bool ShouldSerializeValue(object component) 
    {
      return _PropDesc.ShouldSerializeValue(
          ((PropDispNameWrapper)component).Unwrap);
    }
  }

  [AttributeUsage(AttributeTargets.Property |
     AttributeTargets.Field)]
  [Serializable]
  public class DisplayNameAttribute : Attribute
  {
    string _sText;
    public DisplayNameAttribute(string Text) : base()
    {
      _sText = Text;
    }
    public override string ToString()
    {
      return _sText;
    }
  }

 but it not correct implementation for DBObject.

 

Message 6 of 7
Anonymous
in reply to: Anonymous

Your ICustomTypeDescriptor should store only the ObjectId of the DBObject.

 

Whenever you need to get data from the DBObject, you have to open it in

a transaction, get the data, and commit the transaction and discard the

DBObject. Do not cache the DBObject for subsequent use, because it is

still unusable within the wrapper.

 

So, everywhere in your code where you reference your 'Unwrap'

property, you are using the original DBObject,which you can't do.

 

You have to cache it's ObjectId instead, and when you need the

DBObject, you have to use the ObjectId to open the DBObject in

a transaction, use the DBObject,and then discard it.

 

That's the first problem. The other problem is that you must ensure that

any property that returns a DBObject, is wrapped in your custom wrapper,

rather than allowing the DBObject itself to be exposed as the value of

the property.

 

What you're attempting to do is not simple or trivial. Trust me. It tiook me

about a year to get own implmentation fully working and debugged.

Message 7 of 7
Anonymous
in reply to: Anonymous

Many thanks!

I will try to do how you have advised to me.

 

--------------------------------------

Sorry for my bad English.

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

AutoCAD Inside the Factory


Autodesk Design & Make Report