/// <summary> /// Creates a property source for the specified CLR object using reflection. /// </summary> /// <param name="instance">The instance.</param> /// <param name="name">The name for the property source.</param> /// <param name="typeName">The type name of the property source.</param> /// <returns>The property source.</returns> /// <remarks> /// <para> /// This method can be used for static types and dynamic types that implement /// <see cref="ICustomTypeDescriptor"/>. The property source lists instance properties. /// Properties where the <see cref="BrowsableAttribute"/> is set to <see langword="false"/> /// are ignored. /// </para> /// <para> /// To find the name of the property source the method uses (in this order): /// </para> /// <list type="number"> /// <item>The <paramref name="name"/> parameter.</item> /// <item>The <see cref="INamedObject"/> interface.</item> /// <item>A string property called "Name".</item> /// </list> /// <para> /// To find the type name of the property source the method uses (in this order): /// </para> /// <list type="number"> /// <item>The <paramref name="typeName"/> parameter.</item> /// <item>The CLR type name.</item> /// </list> /// <para> /// The <paramref name="instance"/> is stored in the <see cref="PropertySource.UserData"/> /// of the <see cref="PropertySource"/>. /// </para> /// </remarks> /// <exception cref="ArgumentNullException"> /// <paramref name="instance"/> is <see langword="null"/>. /// </exception> public static PropertySource CreatePropertySource(object instance, string name = null, string typeName = null) { if (instance == null) { throw new ArgumentNullException(nameof(instance)); } var propertySource = new PropertySource { UserData = instance }; // Get property descriptors. var typeConverter = TypeDescriptor.GetConverter(instance); PropertyDescriptorCollection propertyDescriptors = null; if (typeConverter != null && typeConverter.GetPropertiesSupported()) { // Try to get properties from type converter. propertyDescriptors = typeConverter.GetProperties(instance); } if (propertyDescriptors == null && instance is ICustomTypeDescriptor) { // Instance is a dynamic object. propertyDescriptors = ((ICustomTypeDescriptor)instance).GetProperties(); } bool isBasicObject = false; if (propertyDescriptors == null) { // Instance is a regular CLR object. isBasicObject = true; propertyDescriptors = TypeDescriptor.GetProperties(instance.GetType()); } // Create a list of all properties. propertySource.Properties.AddRange( propertyDescriptors.OfType <PropertyDescriptor>() .Where(descriptor => descriptor.IsBrowsable) .Select(descriptor => new ReflectedProperty(instance, descriptor)) .OrderBy(p => p, PropertyComparer.Instance)); // Add public fields of regular CLR objects (very important for structs like Vector3F). if (isBasicObject) { foreach (var field in instance.GetType().GetFields()) { if (!field.IsLiteral && !field.IsStatic) { propertySource.Properties.Add(new ReflectedField(instance, field)); } } } // Add items of an IEnumerable. var enumerable = instance as IEnumerable; if (enumerable != null) { int index = 0; foreach (var item in enumerable) { string itemName = "Item[" + index + "]"; var namedItem = item as INamedObject; if (namedItem != null) { itemName = Invariant($"{itemName}: {namedItem.Name}"); } propertySource.Properties.Add(new CustomProperty(itemName, item) { Category = "Items", IsReadOnly = true }); index++; } } // ----- Set name: // 1. Try name parameter. propertySource.Name = name; // 2. Try INamedObject interface. if (propertySource.Name == null) { var namedObject = instance as INamedObject; if (namedObject != null) { propertySource.Name = namedObject.Name; } } // 3. Try "Name" property. if (propertySource.Name == null) { var namePropertyDescriptor = propertyDescriptors.OfType <PropertyDescriptor>() .FirstOrDefault(d => d.Name == "Name" && d.PropertyType == typeof(string)); if (namePropertyDescriptor != null) { propertySource.Name = (string)namePropertyDescriptor.GetValue(instance); } } // Fallback: Empty string. if (propertySource.Name == null) { propertySource.Name = string.Empty; } // Set type name. propertySource.TypeName = typeName; if (propertySource.TypeName == null) { propertySource.TypeName = instance.GetType().Name; } return(propertySource); }
//-------------------------------------------------------------- #region Properties & Events //-------------------------------------------------------------- #endregion //-------------------------------------------------------------- #region Creation & Cleanup //-------------------------------------------------------------- #endregion //-------------------------------------------------------------- #region Methods //-------------------------------------------------------------- private void UpdateProperties() { if (_modelPropertySource == null) _modelPropertySource = new PropertySource(); _currentPropertySource = _modelPropertySource; _modelPropertySource.Name = this.GetName(); _modelPropertySource.TypeName = "3D Model"; _modelPropertySource.Properties.Clear(); if (State != ModelDocumentState.Loaded) return; // Try to load drmdl file. string drmdlFile = Path.ChangeExtension(Uri.LocalPath, "drmdl"); if (UseDigitalRuneGraphics && File.Exists(drmdlFile)) { _modelPropertySource.Properties.Add(new CustomProperty { Category = "General", Name = "Model description", Value = drmdlFile, //Description = , PropertyType = typeof(string), DataTemplateKey = OpenLinkKey, CanReset = false, IsReadOnly = true, }); } int numberOfVertices = 0; int numberOfPrimitives = 0; if (UseDigitalRuneGraphics) { foreach (var mesh in ModelNode.GetDescendants().OfType<MeshNode>().Select(mn => mn.Mesh)) { foreach (var submesh in mesh.Submeshes) { numberOfVertices += submesh.VertexCount; numberOfPrimitives += submesh.PrimitiveCount; } } } else { foreach (var meshPart in Model.Meshes.SelectMany(m => m.MeshParts)) { numberOfVertices += meshPart.NumVertices; numberOfPrimitives += meshPart.PrimitiveCount; } } _modelPropertySource.Properties.Add(new CustomProperty { Category = "General", Name = "Triangles", Value = numberOfPrimitives, //Description = , PropertyType = typeof(int), DataTemplateKey = TextBlockKey, CanReset = false, IsReadOnly = true, }); _modelPropertySource.Properties.Add(new CustomProperty { Category = "General", Name = "Vertices", Value = numberOfVertices, //Description = , PropertyType = typeof(int), DataTemplateKey = TextBlockKey, CanReset = false, IsReadOnly = true, }); // Try to load drmat files. if (UseDigitalRuneGraphics) { string drmdlFolder = Path.GetDirectoryName(drmdlFile); var drmatFiles = new HashSet<string>(); var textures = new HashSet<string>(); var xDocument = XDocument.Load(drmdlFile); foreach (var submeshElement in xDocument.Descendants("Submesh")) { var materialAttribute = submeshElement.Attributes("Material").FirstOrDefault(); if (materialAttribute == null) continue; string drmatFile = GetAbsolutePath(drmdlFolder, materialAttribute.Value); if (!File.Exists(drmatFile)) continue; drmatFiles.Add(drmatFile); string drmatFolder = Path.GetDirectoryName(drmatFile); // Collect all referenced texture filenames. var drmatXDocument = XDocument.Load(drmatFile); foreach (var textureElement in drmatXDocument.Descendants("Texture")) { var fileAttribute = textureElement.Attributes("File").FirstOrDefault(); if (fileAttribute == null) continue; string textureFile = GetAbsolutePath(drmatFolder, fileAttribute.Value); textures.Add(textureFile); } } int i = 0; foreach (var drmatFile in drmatFiles) { _modelPropertySource.Properties.Add(new CustomProperty { Category = "Materials", Name = Invariant($"Material {i}"), Value = drmatFile, //Description = , PropertyType = typeof(string), DataTemplateKey = OpenLinkKey, CanReset = false, IsReadOnly = true, }); i++; } i = 0; foreach (var texture in textures) { _modelPropertySource.Properties.Add(new CustomProperty { Category = "Textures", Name = Invariant($"Texture {i}"), Value = texture, //Description = , PropertyType = typeof(string), DataTemplateKey = OpenLinkKey, CanReset = false, IsReadOnly = true, }); i++; } _animationPropertyViewModels.Clear(); if (HasAnimations) { var mesh = ModelNode.GetDescendants().OfType<MeshNode>().First().Mesh; foreach (var entry in mesh.Animations) { var animationPropertyViewModel = new AnimationPropertyViewModel(this) { Name = entry.Key, Animation = entry.Value, }; _animationPropertyViewModels.Add(animationPropertyViewModel); _modelPropertySource.Properties.Add(new CustomProperty { Category = "Animations", Name = "\"" + entry.Key + "\"", Value = animationPropertyViewModel, //Description = , PropertyType = typeof(AnimationPropertyViewModel), CanReset = false, IsReadOnly = true, }); } } } if (_documentService.ActiveDocument == this) { // This document is active and can control tool windows. if (_propertiesService != null) _propertiesService.PropertySource = _currentPropertySource; } }
private void UpdateProperties() { if (_propertySource == null) _propertySource = new PropertySource(); _propertySource.Name = (IsUntitled) ? UntitledName : Path.GetFileName(Uri.LocalPath); _propertySource.TypeName = "Texture"; var textBlockKey = new ComponentResourceKey(typeof(PropertyGrid), "TextBlock"); _propertySource.Properties.Clear(); _propertySource.Properties.Add(new CustomProperty { Category = "Misc", Name = "File name", Value = this.GetName(), Description = "The filename of the image.", PropertyType = typeof(string), //DataTemplateKey = textBlockKey, CanReset = false, IsReadOnly = true, }); _propertySource.Properties.Add(new CustomProperty { Category = "Misc", Name = "Size", Value = Texture2D?.Width, Description = "Image width in pixels.", PropertyType = typeof(int), DataTemplateKey = textBlockKey, CanReset = false, IsReadOnly = true, }); _propertySource.Properties.Add(new CustomProperty { Category = "Misc", Name = "Height", Value = Texture2D?.Height, Description = "Image height in pixels.", PropertyType = typeof(int), DataTemplateKey = textBlockKey, CanReset = false, IsReadOnly = true, }); _propertySource.Properties.Add(new CustomProperty { Category = "Misc", Name = "Format", Value = Texture2D?.Format, Description = null, PropertyType = typeof(SurfaceFormat), DataTemplateKey = textBlockKey, CanReset = false, IsReadOnly = true, }); _propertySource.Properties.Add(new CustomProperty { Category = "Misc", Name = "Mipmap levels", Value = Texture2D?.LevelCount, Description = null, PropertyType = typeof(int), DataTemplateKey = textBlockKey, CanReset = false, IsReadOnly = true, }); }