Einzelnen Beitrag anzeigen

Benutzerbild von jfheins
jfheins

Registriert seit: 10. Jun 2004
Ort: Garching (TUM)
4.579 Beiträge
 
#10

Re: Klassen dynamisch mit xsd verändern?

  Alt 15. Dez 2009, 12:43
So, falls das nochmal wen interessiert, hier meine Lösung:

Der Graph (Containerklasse für Kanten und Knoten) implementiert das Interface ICustomTypeDescriptor - damit kann man dynamisch Zeilen im PropertyGrid hinzufügen.

Ein Feld in der Klasse dient als Sammelbecken für alle nicht-erkannten Attribute:
Code:
        private List<XAttribute> Attrib;
Dann muss man mindestens eine Methode wie folgt überschreiben:
Code:
        public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
        {
            var normal = TypeDescriptor.GetProperties(this, attributes, true);

            var all = new List<PropertyDescriptor>(normal.Count + Attrib.Count);

            foreach (PropertyDescriptor item in normal)
            {
                all.Add(item);
            }

            foreach (var item in Attrib)
            {
                if (VariantDescriptor.Supports(item))
                {
                    all.Add(new VariantDescriptor(item));
                }
            }
           
            return new PropertyDescriptorCollection(all.ToArray());
        }
Den Rest kann man einfach weiterleiten:
Code:
        public string GetClassName()
        {
            return TypeDescriptor.GetClassName(this, true);
        }
        public string GetComponentName()
        {
            return TypeDescriptor.GetComponentName(this, true);
        }
        public TypeConverter GetConverter()
        {
            return TypeDescriptor.GetConverter(this, true);
        }
/// etc...
Und hier kommt der Interessante Teil: Der PropertyDescriptor der das typsichere Bearbeiten ermöglicht:
Code:
    /*  Diese Klasse leistet die Darstellung der verschidenen XAttribute
     *  im PropertyGrid. Es werden einfache Typen unterschieden, und zum
     *  Editieren in ihre C#-Equivalente überführt.
     *  Getestet mit int, float, double, decimal, string und DateTime, ggf. switches erweitern.
     *  Unbekannte Typen sollen nicht editierbar gemacht werden,
     *  wenn TypeCodeToType() den Wert null zurückgibt. (kann mit Supports() geprüft werden)
     * 
     *  Klasse benötigt Schema-Infos.
     * 
     */
    class VariantDescriptor : PropertyDescriptor
    {
        private XAttribute attr;
        private XmlTypeCode type;

        public VariantDescriptor(XAttribute attribute)
            : base(attribute.Name.ToString(), null)
        {
            attr = attribute;
            var info = attr.GetSchemaInfo();

            if (info == null)
                throw new ArgumentException("Schema-Info is required!\r\nUse XDocument.Validate(..., ..., true) or equivalent.");
           
            type = attr.GetSchemaInfo().SchemaType.TypeCode;
           
            if (TypeCodeToType(type) == null)
                throw new NotImplementedException(string.Format("This type (\"{0}\") is not supported.", type.ToString()));
        }

        public override bool CanResetValue(object component)
        {
            return false;
        }

        public override Type ComponentType
        {
            get { return typeof(Graph); }
        }

        public override object GetValue(object component)
        {
            switch (type)
            {
                case XmlTypeCode.Time:
                case XmlTypeCode.Date:
                case XmlTypeCode.DateTime:
                    return XmlConvert.ToDateTime(attr.Value, XmlDateTimeSerializationMode.RoundtripKind);

                // Konvertierung nötig, um das Dezimaltrennzeichen korrekt zu wandeln
                case XmlTypeCode.Decimal:
                    return XmlConvert.ToDecimal(attr.Value);
                case XmlTypeCode.Double:
                    return XmlConvert.ToDouble(attr.Value);
                case XmlTypeCode.Float:
                    return XmlConvert.ToSingle(attr.Value);

                default:
                    return attr.Value;
            }
        }

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

        public override Type PropertyType
        {
            get
            {
                return TypeCodeToType(type);
            }
        }

        private static Type TypeCodeToType(XmlTypeCode TypeCode)
        {
            switch (TypeCode)
            {
                case XmlTypeCode.Boolean:
                    return typeof(Boolean);
                case XmlTypeCode.UnsignedByte:
                case XmlTypeCode.Byte:
                    return typeof(byte);
                case XmlTypeCode.Date:
                case XmlTypeCode.Time:
                case XmlTypeCode.DateTime:
                    return typeof(DateTime);
                case XmlTypeCode.Decimal:
                    return typeof(decimal);
                case XmlTypeCode.Double:
                    return typeof(double);
                case XmlTypeCode.Float:
                    return typeof(float);
                case XmlTypeCode.Int:
                    return typeof(int);
                case XmlTypeCode.NonPositiveInteger:
                case XmlTypeCode.NegativeInteger:
                case XmlTypeCode.Integer:
                case XmlTypeCode.Long:
                    return typeof(long);
                case XmlTypeCode.Short:
                    return typeof(short);
                case XmlTypeCode.Name:
                case XmlTypeCode.NCName:
                case XmlTypeCode.NmToken:
                case XmlTypeCode.Text:
                case XmlTypeCode.String:
                    return typeof(string);
                case XmlTypeCode.UnsignedInt:
                    return typeof(uint);
                case XmlTypeCode.NonNegativeInteger:
                case XmlTypeCode.PositiveInteger:
                case XmlTypeCode.UnsignedLong:
                    return typeof(ulong);
                case XmlTypeCode.UnsignedShort:
                    return typeof(ushort);
                default:
                    return null;
            }
        }

        public override void ResetValue(object component)
        {
        }

        public override void SetValue(object component, object value)
        {
            switch (type)
            {
                case XmlTypeCode.Time:
                case XmlTypeCode.Date:
                case XmlTypeCode.DateTime:
                    attr.Value = XmlConvert.ToString((DateTime)value, XmlDateTimeSerializationMode.RoundtripKind);
                    break;

                // Konvertierung nötig, um das Dezimaltrennzeichen korrekt zu wandeln
                case XmlTypeCode.Decimal:
                    attr.Value = XmlConvert.ToString((decimal)value);
                    break;
                case XmlTypeCode.Double:
                    attr.Value = XmlConvert.ToString((double)value);
                    break;
                case XmlTypeCode.Float:
                    attr.Value = XmlConvert.ToString((float)value);
                    break;

                default:
                    attr.Value = value.ToString();
                    break;
            }
        }

        public override bool ShouldSerializeValue(object component)
        {
            return false;
        }

        internal static bool Supports(XAttribute item)
        {
            return TypeCodeToType(item.GetSchemaInfo().SchemaType.TypeCode) != null;
        }
    }
Wenn jetzt ein objekt der Klasse graph einem properyGrid zugewiesen wird, erscheinen die zusätzlichen Attribute wie normale Eigenschaften und sind ebenso editierbar. Typsicherheit ist gegeben (z.B. "100000" ist kein gültiger wert für System.Short)
  Mit Zitat antworten Zitat