locked
Custom property descriptor and disabled a property in a PropertyGrid RRS feed

  • Question

  • Hi,

    I have implemented a custom type to manage a security level to access to some properties of objects in function of a security level (administrator, power user, user...).

    So I have developped a security level description provider :

    class SecurityLevelTypeDescriptionProvider : TypeDescriptionProvider
    

    It provide a security level type descriptor :

    class SecurityLevelTypeDescriptor : CustomTypeDescriptor
    

    Mys custom type descriptor provide a properties descriptor collection of security level property descriptor :

    class SecurityLevelPropertyDescriptor : PropertyDescriptor
    

    This security level property descriptor manage the IsReadOnly property and SetValue method of a property in function of a security level attribute (administrator, power user, user...).

    So a property can be appears as read only for a user and as read and write for an other user

    [TypeDescriptionProvider(typeof(SecurityLevelTypeDescriptionProvider))]
    public class Configurator
    {	
    	[SecurityLevel(SecurityLevels.AdministratorLevel)]
    	public string ServerName
    	{
    		get { return sServerName; }
    		set { sServerName= value; }
    	}
    }
    

    I display my Configurator object in a PropertyGrid, in function of the user level the properties are enabled or not in the PropertyGrid.

    It work, it's fine. but it doesn't work in a case :

    If I use a specific editor to edit the property, the IsReadOnly property of SecurityLevelPropertyDescriptor is correctly executed (return true for a specific level) but the property is never disabled in the property grid :

    [Editor(typeof(FileBrowserEditor), typeof(UITypeEditor))]
    [SecurityLevel(SecurityLevels.AdministratorLevel)]
    public string InterfaceFilePath
    {
    	get { return sInterfaceFilePath; }
    	set { sInterfaceFilePath = value; }
    }
    

    or

    [Editor(typeof(FolderBrowserEditor), typeof(UITypeEditor))]
    [SecurityLevel(SecurityLevels.AdministratorLevel)]
    public string SequencePath
    {
    	get { return sSequencePath; }
    	set { sSequencePath = value; }
    }
    

    Why and How to correct this?

     

    Thanks for your help

    Wednesday, July 28, 2010 2:06 PM

Answers

  • Ho sorry for the delay, I have not seen your post before

     

    Here the msdn support solution :

    the editor style must not be Modal :

    public class FileBrowserEditor:UITypeEditor
    {
    		/// <summary>
    		/// Get the edit style for the editor
    		/// </summary>
    		/// <param name="context">Context descriptor</param>
    		/// <returns>Edit style</returns>
    		public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
    		{
    			if (context.PropertyDescriptor.IsReadOnly)
    				return UITypeEditorEditStyle.None;
    			else
    				return UITypeEditorEditStyle.Modal;
    		}
    .......
    }
    

    • Marked as answer by Troopers Thursday, February 3, 2011 1:50 PM
    Thursday, February 3, 2011 1:50 PM

All replies

  • To be more explicit, i have created a test project :

     

    - I create a security level attribute to set the security level on a property :

    using System;
    
    namespace SecurityLevelTest
    {
    	/// <summary>
    	/// Defines the security levels used to determine which level permit to edit a setting property
    	/// </summary>
    	public enum SecurityLevels
    	{
    		/// <summary>
    		/// Unknown level
    		/// </summary>
    		Unknown = 0,
    		/// <summary>
    		/// Operator level
    		/// </summary>
    		OperatorLevel = 1,
    		/// <summary>
    		/// Maintenance level
    		/// </summary>
    		MaintenanceLevel = 2,
    		/// <summary>
    		/// Administrator level
    		/// </summary>
    		AdministratorLevel = 3
    	}
    
    	/// <summary>
    	/// Defines the properties and methods of a security level attribute
    	/// </summary>
    	[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
    	public class SecurityLevelAttribute:System.Attribute
    	{
    		// Variables
    		private SecurityLevels slLevel;
    
    		/// <summary>
    		/// Create a new security level attribute
    		/// </summary>
    		public SecurityLevelAttribute()
    		{
    			this.slLevel = SecurityLevels.Unknown;
    		}
    
    		/// <summary>
    		/// Create a new security level attribute
    		/// </summary>
    		/// <param name="slLevel">Security level</param>
    		public SecurityLevelAttribute(SecurityLevels slLevel)
    		{
    			this.slLevel = slLevel;
    		}
    
    		/// <summary>
    		/// Get or set the security level
    		/// </summary>
    		public SecurityLevels Level
    		{
    			get { return slLevel; }
    			set { slLevel = value; }
    		}
    	}
    }
    

    - I create a type description provider to provide my specific type descriptor:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    
    namespace SecurityLevelTest
    {
    	/// <summary>
    	/// Define the properties and methods of a security level type description provider.
    	/// This class is used to provide the management of our security level type descriptor
    	/// </summary>
    	internal class SecurityLevelTypeDescriptionProvider:TypeDescriptionProvider
    	{
    		// Variables
    		private static readonly Dictionary<Type, ICustomTypeDescriptor> dDescriptors = new Dictionary<Type, ICustomTypeDescriptor>();
    
    		/// <summary>
    		/// Create a security level type description provider
    		/// </summary>
    		public SecurityLevelTypeDescriptionProvider() : base(TypeDescriptor.GetProvider(typeof(object))) { }
    
    
    		/// <summary>
    		/// Get the type descriptor
    		/// </summary>
    		/// <param name="objectType">Object type</param>
    		/// <param name="instance">Object instance</param>
    		/// <returns>Type descriptor</returns>
    		public sealed override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
    		{
    			ICustomTypeDescriptor ictdDescriptor;
    			// Create a new security level type descriptor with the default type descriptor
    			ictdDescriptor = new SecurityLevelTypeDescriptor(base.GetTypeDescriptor(objectType, null));
    			return ictdDescriptor;
    		}
    	}
    }
    
    

     

    - I create my specific type descriptor to manage my specific property descriptor

    using System;
    using System.ComponentModel;
    
    namespace SecurityLevelTest
    {
    	/// <summary>
    	/// Define the properties and methods of a security level type descriptor.
    	/// This class is used to replace the default property descriptor by our security level property descriptor
    	/// </summary>
    	internal class SecurityLevelTypeDescriptor : CustomTypeDescriptor
    	{
    		// Variables
    		private readonly PropertyDescriptorCollection pdcPropertyDescriptors;
    		
    		/// <summary>
    		/// Create a security level type descriptor
    		/// </summary>
    		/// <param name="ctdParentTypeDescriptor">Parent type descriptor</param>
    		internal SecurityLevelTypeDescriptor(ICustomTypeDescriptor ctdParentTypeDescriptor) : base(ctdParentTypeDescriptor)
    		{
    			// Create a properties collection
    			pdcPropertyDescriptors = new PropertyDescriptorCollection(null);
    			// Get the properties from the parent type descriptor
    			PropertyDescriptorCollection pdcParentPropertyDescriptors = ctdParentTypeDescriptor.GetProperties();
    			// For each property
    			foreach(PropertyDescriptor pdPropertyDescriptor in pdcParentPropertyDescriptors)
    				// Create our security level property descriptor and add it to the properties collection
    				pdcPropertyDescriptors.Add(new SecurityLevelPropertyDescriptor(pdPropertyDescriptor));
    		}
    
    		/// <summary>
    		/// Get the properties
    		/// </summary>
    		/// <returns>Properties collection</returns>
    		public override PropertyDescriptorCollection GetProperties()
    		{
    			// Return the property collection
    			return pdcPropertyDescriptors;
    		}
    
    		/// <summary>
    		/// Get the properties filtered on an attributes list
    		/// </summary>
    		/// <param name="attributes">Attributes list</param>
    		/// <returns>Properties collection</returns>
    		public sealed override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
    		{
    			// Create a properties collection
    			PropertyDescriptorCollection pdcFilteredPropertyDescriptors = new PropertyDescriptorCollection(null);
    			// For each property from the type
    			foreach(PropertyDescriptor pdPropertyDescriptor in pdcPropertyDescriptors)
    				// If the attributes match
    				if(pdPropertyDescriptor.Attributes.Contains(attributes))
    					// Add the property to the collection
    					pdcFilteredPropertyDescriptors.Add(pdPropertyDescriptor);
    			// Return the collection
    			return pdcFilteredPropertyDescriptors;
    		}
    	}
    }
    

    - I create my specific property descriptor to manage the ISReadOnly property

    using System;
    using System.ComponentModel;
    
    namespace SecurityLevelTest
    {
    	/// <summary>
    	/// Define the properties and methods of a security level property descriptor.
    	/// This class is used to manage the readonly property with the security level attribute
    	/// </summary>
    	internal class SecurityLevelPropertyDescriptor:PropertyDescriptor
    	{
    		// Variables
    		private PropertyDescriptor pdDefaultPropertyDescriptor;
    		private static SecurityLevels slSecurityLevel = SecurityLevels.Unknown;
    
    		public static SecurityLevels SecurityLevel
    		{
    			get { return slSecurityLevel; }
    			set { slSecurityLevel = value; }
    		}
    
    		/// <summary>
    		/// Create a security level property descriptor
    		/// </summary>
    		/// <param name="pdPropertyDescriptor">Parent property descriptor</param>
    		public SecurityLevelPropertyDescriptor(PropertyDescriptor pdPropertyDescriptor)
    			: base(pdPropertyDescriptor)
    		{
    			this.pdDefaultPropertyDescriptor = pdPropertyDescriptor;
    		}
    
    		/// <summary>
    		/// Get if the property is read only
    		/// </summary>
    		public override bool IsReadOnly
    		{
    			get
    			{
    				// Get the security level needed to edit the property
    				SecurityLevelAttribute slaSecurityLevel = (SecurityLevelAttribute)base.Attributes[typeof(SecurityLevelAttribute)];
    				// Return true if the security level needed is greater than the current security level
    				return slaSecurityLevel.Level > slSecurityLevel;
    			}
    		}
    
    		/// <summary>
    		/// Check if the property can reset the value
    		/// </summary>
    		/// <param name="component">Source component</param>
    		/// <returns>True if the property can reset the value, else False</returns>
    		public override bool CanResetValue(object component)
    		{
    			// Call the method of the default descriptor
    			return pdDefaultPropertyDescriptor.CanResetValue(component);
    		}
    
    		/// <summary>
    		/// Get the value
    		/// </summary>
    		/// <param name="component">Source component</param>
    		/// <returns>Value of the property for the source component</returns>
    		public override object GetValue(object component)
    		{
    			// Call the method of the default descriptor
    			return pdDefaultPropertyDescriptor.GetValue(component);
    		}
    
    		/// <summary>
    		/// Reset the value
    		/// </summary>
    		/// <param name="component">Source component</param>
    		public override void ResetValue(object component)
    		{
    			// Call the method of the default descriptor
    			pdDefaultPropertyDescriptor.ResetValue(component);
    		}
    
    		/// <summary>
    		/// Set the value
    		/// </summary>
    		/// <param name="component">Source component</param>
    		/// <param name="value">Value</param>
    		public override void SetValue(object component, object value)
    		{
    			// If the property is not read only
    			if(!IsReadOnly)
    			{
    				// Call the method of the default descriptor
    				pdDefaultPropertyDescriptor.SetValue(component, value);
    			}
    		}
    
    		/// <summary>
    		/// Check if the property should serialize the value
    		/// </summary>
    		/// <param name="component">Source component</param>
    		/// <returns>True if the value should be serialized, else False</returns>
    		public override bool ShouldSerializeValue(object component)
    		{
    			// Call the method of the default descriptor
    			return pdDefaultPropertyDescriptor.ShouldSerializeValue(component);
    		}
    
    		/// <summary>
    		/// Get the property type
    		/// </summary>
    		public override Type PropertyType
    		{
    			// Call the property of the default descriptor
    			get { return pdDefaultPropertyDescriptor.PropertyType; }
    		}
    
    		/// <summary>
    		/// Get the component type
    		/// </summary>
    		public override Type ComponentType
    		{
    			// Call the property of the default descriptor
    			get { return pdDefaultPropertyDescriptor.ComponentType; }
    		}
    	}
    }
    
    

     

    - I create a class to use the specific type descriptor and the security level attribute :

    using System;
    using System.ComponentModel;
    using System.Drawing.Design;
    
    namespace SecurityLevelTest
    {
    	[TypeDescriptionProvider(typeof(SecurityLevelTypeDescriptionProvider))]
    	public class Configurator
    	{
    		private string sSequencePath;
    		private string sLogFileName;
    		private string sInterfaceFilePath;
    
    		//[Editor(typeof(FolderBrowserEditor), typeof(UITypeEditor))]
    		[SecurityLevel(SecurityLevels.AdministratorLevel)]
    		public string SequencePath
    		{
    			get { return sSequencePath; }
    			set { sSequencePath = value; }
    		}
    
    		[SecurityLevel(SecurityLevels.MaintenanceLevel)]
    		public string LogFileName
    		{
    			get { return sLogFileName; }
    			set { sLogFileName = value; }
    		}
    
    		//[Editor(typeof(FileBrowserEditor), typeof(UITypeEditor))]
    		[SecurityLevel(SecurityLevels.AdministratorLevel)]
    		public string InterfaceFilePath
    		{
    			get { return sInterfaceFilePath; }
    			set { sInterfaceFilePath = value; }
    		}
    	}
    }
    

    - I create a winform to test this in a property grid (create form with a property grig and 2 buttons)

    public partial class Form1:Form
    	{
    		public Form1()
    		{
    			InitializeComponent();
    		}
    
    		private void Form1_Load(object sender, EventArgs e)
    		{
    			Configurator cConfig = new Configurator();
    			pgPropertiesGrid.SelectedObject = cConfig;
    		}
    
    		private void btnSetMaintenanceLevel_Click(object sender, EventArgs e)
    		{
    			SecurityLevelPropertyDescriptor.SecurityLevel = SecurityLevels.MaintenanceLevel;
    			pgPropertiesGrid.Refresh();
    		}
    
    		private void btnSetAdministratorLevel_Click(object sender, EventArgs e)
    		{
    			SecurityLevelPropertyDescriptor.SecurityLevel = SecurityLevels.AdministratorLevel;
    			pgPropertiesGrid.Refresh();
    		}
    	}
    

    - Run

    - Look! Its' work!!!

    - Now, we add a specific editor, we must inherit from UITypeEditor to create a folder editor and a file editor :

     

    using System;
    using System.ComponentModel;
    using System.Drawing.Design;
    using System.IO;
    using System.Windows.Forms;
    
    namespace SecurityLevelTest
    {
    	/// <summary>
    	/// Defines the properties and methods of a folder browser editor called by the PropertyGrid control to edit some properties with the EditorAttribute
    	/// </summary>
    	public class FolderBrowserEditor:UITypeEditor
    	{
    		/// <summary>
    		/// Get the edit style for the editor
    		/// </summary>
    		/// <param name="context">Context descriptor</param>
    		/// <returns>Edit style</returns>
    		public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
    		{
    			return UITypeEditorEditStyle.Modal;
    		}
    
    		/// <summary>
    		/// Edit a value
    		/// </summary>
    		/// <param name="context">Context descriptor</param>
    		/// <param name="provider">Provider</param>
    		/// <param name="value">Edited value</param>
    		/// <returns>Selected folder</returns>
    		public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
    		{
    			// Get the current value
    			string sPath = Convert.ToString(value);
    			// Create a browser dialog
    			using(FolderBrowserDialog fbdDialog = new FolderBrowserDialog())
    			{
    				// If there is a current path, select the path in the browser dialog
    				if(!string.IsNullOrEmpty(sPath) && Directory.Exists(sPath))
    					fbdDialog.SelectedPath = sPath;
    
    				// If there is a context
    				if(context != null && context.PropertyDescriptor != null)
    				{
    					// Get the [Description], [DisplayName] or [Name] to display in the browser dialog
    					string sCaption = context.PropertyDescriptor.Description;
    					if(string.IsNullOrEmpty(sCaption))
    						sCaption = context.PropertyDescriptor.DisplayName;
    					if(string.IsNullOrEmpty(sCaption))
    						sCaption = context.PropertyDescriptor.Name;
    					// Display in the browser dialog
    					fbdDialog.Description = sCaption;
    				}
    				// If the folder selection has been validated
    				if(fbdDialog.ShowDialog() == DialogResult.OK)
    					// Get the selected folder
    					sPath = fbdDialog.SelectedPath;
    			}
    			// Return the selected folder
    			return sPath;
    		}
    	}
    
    	/// <summary>
    	/// Defines the properties and methods of a file browser editor called by the PropertyGrid control to edit some properties with the EditorAttribute
    	/// </summary>
    	public class FileBrowserEditor:UITypeEditor
    	{
    		/// <summary>
    		/// Get the edit style for the editor
    		/// </summary>
    		/// <param name="context">Context descriptor</param>
    		/// <returns>Edit style</returns>
    		public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
    		{
    			return UITypeEditorEditStyle.Modal;
    		}
    
    		/// <summary>
    		/// Edit a value
    		/// </summary>
    		/// <param name="context">Context descriptor</param>
    		/// <param name="provider">Provider</param>
    		/// <param name="value">Edited value</param>
    		/// <returns>Selected file</returns>
    		public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
    		{
    			// Get the current value
    			string sFilePath = Convert.ToString(value);
    			// Create an open file dialog
    			using(OpenFileDialog ofdDialog = new OpenFileDialog())
    			{
    				// init the dialog
    				ofdDialog.AddExtension = true;
    				ofdDialog.CheckFileExists = true;
    				ofdDialog.CheckPathExists = true;
    				ofdDialog.Multiselect = false;
    				ofdDialog.ShowHelp = false;
    
    				// If there is a current file path, select the path in the open file dialog
    				if(!string.IsNullOrEmpty(sFilePath) && File.Exists(sFilePath))
    					ofdDialog.InitialDirectory = Path.GetDirectoryName(sFilePath);
    
    				// If there is a context
    				if(context != null && context.PropertyDescriptor != null)
    				{
    					// Get the [Description], [DisplayName] or [Name] to display in the browser dialog
    					string sCaption = context.PropertyDescriptor.Description;
    					if(string.IsNullOrEmpty(sCaption))
    						sCaption = context.PropertyDescriptor.DisplayName;
    					if(string.IsNullOrEmpty(sCaption))
    						sCaption = context.PropertyDescriptor.Name;
    					// Display in the open file dialog
    					ofdDialog.Title = sCaption;
    				}
    				// If the file selection has been validated
    				if(ofdDialog.ShowDialog() == DialogResult.OK)
    					// Get the selected file
    					sFilePath = ofdDialog.FileName;
    			}
    			// Return the selected file
    			return sFilePath;
    		}
    	}
    }
    

    - Then we test with this ( uncomment the Editor attribute in Configurator class)

    - Run

    - Oh ____! It doesn't work!!!

     

    Why?

    Thursday, July 29, 2010 9:05 AM
  • Dear Customer,

    Your question falls into the paid support category which requires a more in-depth level of support.  Please visit the below link to see the various paid support options that are available to better meet your needs. http://support.microsoft.com/default.aspx?id=fh;en-us;offerprophone

    Thanks

    Wendell

    Wednesday, August 4, 2010 12:13 AM
  • I got the same problem also, so please do you got a solution for that issue ... please need an answer
    Monday, September 13, 2010 2:28 PM
  • Ho sorry for the delay, I have not seen your post before

     

    Here the msdn support solution :

    the editor style must not be Modal :

    public class FileBrowserEditor:UITypeEditor
    {
    		/// <summary>
    		/// Get the edit style for the editor
    		/// </summary>
    		/// <param name="context">Context descriptor</param>
    		/// <returns>Edit style</returns>
    		public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
    		{
    			if (context.PropertyDescriptor.IsReadOnly)
    				return UITypeEditorEditStyle.None;
    			else
    				return UITypeEditorEditStyle.Modal;
    		}
    .......
    }
    

    • Marked as answer by Troopers Thursday, February 3, 2011 1:50 PM
    Thursday, February 3, 2011 1:50 PM