//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //------------------------------------------------------------------------------ namespace System.ComponentModel.Design { using System; using System.Collections; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Security.Permissions; /// /// Provides access to get and set option values for a designer. /// [HostProtection(SharedState = true)] public abstract class DesignerOptionService : IDesignerOptionService { private DesignerOptionCollection _options; /// /// Returns the options collection for this service. There is /// always a global options collection that contains child collections. /// public DesignerOptionCollection Options { get { if (_options == null) { _options = new DesignerOptionCollection(this, null, string.Empty, null); } return _options; } } /// /// Creates a new DesignerOptionCollection with the given name, and adds it to /// the given parent. The "value" parameter specifies an object whose public /// properties will be used in the Propeties collection of the option collection. /// The value parameter can be null if this options collection does not offer /// any properties. Properties will be wrapped in such a way that passing /// anything into the component parameter of the property descriptor will be /// ignored and the value object will be substituted. /// protected DesignerOptionCollection CreateOptionCollection (DesignerOptionCollection parent, string name, object value) { if (parent == null) { throw new ArgumentNullException("parent"); } if (name == null) { throw new ArgumentNullException("name"); } if (name.Length == 0) { throw new ArgumentException(SR.GetString(SR.InvalidArgument, name.Length.ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture)), "name.Length"); } return new DesignerOptionCollection(this, parent, name, value); } /// /// Retrieves the property descriptor for the given page / value name. Returns /// null if the property couldn't be found. /// private PropertyDescriptor GetOptionProperty(string pageName, string valueName) { if (pageName == null) { throw new ArgumentNullException("pageName"); } if (valueName == null) { throw new ArgumentNullException("valueName"); } string[] optionNames = pageName.Split(new char[] {'\\'}); DesignerOptionCollection options = Options; foreach(string optionName in optionNames) { options = options[optionName]; if (options == null) { return null; } } return options.Properties[valueName]; } /// /// This method is called on demand the first time a user asks for child /// options or properties of an options collection. /// protected virtual void PopulateOptionCollection(DesignerOptionCollection options) { } /// /// This method must be implemented to show the options dialog UI for the given object. /// protected virtual bool ShowDialog(DesignerOptionCollection options, object optionObject) { return false; } /// /// /// Gets the value of an option defined in this package. /// object IDesignerOptionService.GetOptionValue(string pageName, string valueName) { PropertyDescriptor optionProp = GetOptionProperty(pageName, valueName); if (optionProp != null) { return optionProp.GetValue(null); } return null; } /// /// /// Sets the value of an option defined in this package. /// void IDesignerOptionService.SetOptionValue(string pageName, string valueName, object value) { PropertyDescriptor optionProp = GetOptionProperty(pageName, valueName); if (optionProp != null) { optionProp.SetValue(null, value); } } /// /// The DesignerOptionCollection class is a collection that contains /// other DesignerOptionCollection objects. This forms a tree of options, /// with each branch of the tree having a name and a possible collection of /// properties. Each parent branch of the tree contains a union of the /// properties if all the branch's children. /// [TypeConverter(typeof(DesignerOptionConverter))] [Editor("", "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing)] public sealed class DesignerOptionCollection : IList { private DesignerOptionService _service; private DesignerOptionCollection _parent; private string _name; private object _value; private ArrayList _children; private PropertyDescriptorCollection _properties; /// /// Creates a new DesignerOptionCollection. /// internal DesignerOptionCollection(DesignerOptionService service, DesignerOptionCollection parent, string name, object value) { _service = service; _parent = parent; _name = name; _value = value; if (_parent != null) { if (_parent._children == null) { _parent._children = new ArrayList(1); } _parent._children.Add(this); } } /// /// The count of child options collections this collection contains. /// public int Count { get { EnsurePopulated(); return _children.Count; } } /// /// The name of this collection. Names are programmatic names and are not /// localized. A name search is case insensitive. /// public string Name { get { return _name; } } /// /// Returns the parent collection object, or null if there is no parent. /// public DesignerOptionCollection Parent { get { return _parent; } } /// /// The collection of properties that this OptionCollection, along with all of /// its children, offers. PropertyDescriptors are taken directly from the /// value passed to CreateObjectCollection and wrapped in an additional property /// descriptor that hides the value object from the user. This means that any /// value may be passed into the "component" parameter of the various /// PropertyDescriptor methods. The value is ignored and is replaced with /// the correct value internally. /// public PropertyDescriptorCollection Properties { get { if (_properties == null) { ArrayList propList; if (_value != null) { PropertyDescriptorCollection props = TypeDescriptor.GetProperties(_value); propList = new ArrayList(props.Count); foreach(PropertyDescriptor prop in props) { propList.Add(new WrappedPropertyDescriptor(prop, _value)); } } else { propList = new ArrayList(1); } EnsurePopulated(); foreach(DesignerOptionCollection child in _children) { propList.AddRange(child.Properties); } PropertyDescriptor[] propArray = (PropertyDescriptor[])propList.ToArray(typeof(PropertyDescriptor)); _properties = new PropertyDescriptorCollection(propArray, true); } return _properties; } } /// /// Retrieves the child collection at the given index. /// public DesignerOptionCollection this[int index] { [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters")] get { EnsurePopulated(); if (index < 0 || index >= _children.Count) { throw new IndexOutOfRangeException("index"); } return (DesignerOptionCollection)_children[index]; } } /// /// Retrieves the child collection at the given name. The name search is case /// insensitive. /// public DesignerOptionCollection this[string name] { get { EnsurePopulated(); foreach(DesignerOptionCollection child in _children) { if (string.Compare(child.Name, name, true, CultureInfo.InvariantCulture) == 0) { return child; } } return null; } } /// /// Copies this collection to an array. /// public void CopyTo(Array array, int index) { EnsurePopulated(); _children.CopyTo(array, index); } /// /// Called before any access to our collection to force it to become populated. /// private void EnsurePopulated() { if (_children == null) { _service.PopulateOptionCollection(this); if (_children == null) { _children = new ArrayList(1); } } } /// /// Returns an enumerator that can be used to iterate this collection. /// public IEnumerator GetEnumerator() { EnsurePopulated(); return _children.GetEnumerator(); } /// /// Returns the numerical index of the given value. /// public int IndexOf(DesignerOptionCollection value) { EnsurePopulated(); return _children.IndexOf(value); } /// /// Locates the value object to use for getting or setting a property. /// private static object RecurseFindValue(DesignerOptionCollection options) { if (options._value != null) { return options._value; } foreach(DesignerOptionCollection child in options) { object value = RecurseFindValue(child); if (value != null) { return value; } } return null; } /// /// Displays a dialog-based user interface that allows the user to /// configure the various options. /// public bool ShowDialog() { object value = RecurseFindValue(this); if (value == null) { return false; } return _service.ShowDialog(this, value); } /// /// /// Private ICollection implementation. /// bool ICollection.IsSynchronized { get { return false; } } /// /// /// Private ICollection implementation. /// object ICollection.SyncRoot { get { return this; } } /// /// /// Private IList implementation. /// bool IList.IsFixedSize { get { return true; } } /// /// /// Private IList implementation. /// bool IList.IsReadOnly { get { return true; } } /// /// /// Private IList implementation. /// object IList.this[int index] { get { return this[index]; } set { throw new NotSupportedException(); } } /// /// /// Private IList implementation. /// int IList.Add(object value) { throw new NotSupportedException(); } /// /// /// Private IList implementation. /// void IList.Clear() { throw new NotSupportedException(); } /// /// /// Private IList implementation. /// bool IList.Contains(object value) { EnsurePopulated(); return _children.Contains(value); } /// /// /// Private IList implementation. /// int IList.IndexOf(object value) { EnsurePopulated(); return _children.IndexOf(value); } /// /// /// Private IList implementation. /// void IList.Insert(int index, object value) { throw new NotSupportedException(); } /// /// /// Private IList implementation. /// void IList.Remove(object value) { throw new NotSupportedException(); } /// /// /// Private IList implementation. /// void IList.RemoveAt(int index) { throw new NotSupportedException(); } /// /// A special property descriptor that forwards onto a base /// property descriptor but allows any value to be used for the /// "component" parameter. /// private sealed class WrappedPropertyDescriptor : PropertyDescriptor { private object target; private PropertyDescriptor property; internal WrappedPropertyDescriptor(PropertyDescriptor property, object target) : base(property.Name, null) { this.property = property; this.target = target; } public override AttributeCollection Attributes { get { return property.Attributes; } } public override Type ComponentType { get { return property.ComponentType; } } public override bool IsReadOnly { get { return property.IsReadOnly; } } public override Type PropertyType { get { return property.PropertyType; } } public override bool CanResetValue(object component) { return property.CanResetValue(target); } public override object GetValue(object component) { return property.GetValue(target); } public override void ResetValue(object component) { property.ResetValue(target); } public override void SetValue(object component, object value) { property.SetValue(target, value); } public override bool ShouldSerializeValue(object component) { return property.ShouldSerializeValue(target); } } } /// /// The type converter for the designer option collection. /// internal sealed class DesignerOptionConverter : TypeConverter { public override bool GetPropertiesSupported(ITypeDescriptorContext cxt) { return true; } public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext cxt, object value, Attribute[] attributes) { PropertyDescriptorCollection props = new PropertyDescriptorCollection(null); DesignerOptionCollection options = value as DesignerOptionCollection; if (options == null) { return props; } foreach(DesignerOptionCollection option in options) { props.Add(new OptionPropertyDescriptor(option)); } foreach(PropertyDescriptor p in options.Properties) { props.Add(p); } return props; } public override object ConvertTo(ITypeDescriptorContext cxt, CultureInfo culture, object value, Type destinationType) { if (destinationType == typeof(string)) { return SR.GetString(SR.CollectionConverterText); } return base.ConvertTo(cxt, culture, value, destinationType); } private class OptionPropertyDescriptor : PropertyDescriptor { private DesignerOptionCollection _option; internal OptionPropertyDescriptor(DesignerOptionCollection option) : base(option.Name, null) { _option = option; } public override Type ComponentType { get { return _option.GetType(); } } public override bool IsReadOnly { get { return true; } } public override Type PropertyType { get { return _option.GetType(); } } public override bool CanResetValue(object component) { return false; } public override object GetValue(object component) { return _option; } public override void ResetValue(object component) { } public override void SetValue(object component, object value) { } public override bool ShouldSerializeValue(object component) { return false; } } } } }