Custom property descriptor and disabled a property in a PropertyGrid
-
Wednesday, July 28, 2010 2:06 PM
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 : TypeDescriptionProviderIt provide a security level type descriptor :
class SecurityLevelTypeDescriptor : CustomTypeDescriptorMys custom type descriptor provide a properties descriptor collection of security level property descriptor :
class SecurityLevelPropertyDescriptor : PropertyDescriptorThis 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
Answers
-
Thursday, February 03, 2011 1:50 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 03, 2011 1:50 PM
All Replies
-
Thursday, July 29, 2010 9:05 AM
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?
-
Wednesday, August 04, 2010 12:13 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
- Proposed As Answer by Wendell - MSFT Support Wednesday, August 04, 2010 12:15 AM
-
Monday, September 13, 2010 2:28 PMI got the same problem also, so please do you got a solution for that issue ... please need an answer
-
Thursday, February 03, 2011 1:50 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 03, 2011 1:50 PM

