Interfaces are no types, so when implementing them, their property attributes are not inherited. (which would be true of a base-class)
So, I'm currently wrapping my head around how to make this happen. Following example should pretty much explain what I mean, but unfortunately is not working as expected.
Reason is obvisously to centralize Instructions / Attributes without having to copy / paste them over and over.
public interface IPermissionBasedControl
{
[Browsable(true), Category("PermissionSystem"), Description("AccessKey required in order to use this control.")]
String PermissionKey { get; set; }
}
public class ExtendedTextBox : TextBox, IReadOnlyControl, IDataBindable, IPermissionBasedControl
{
...
[AttributeProvider(typeof(IPermissionBasedControl))]
public string PermissionKey { get; set; }
...
}
Expectation: Visual Studio Designer will pick the Category, Browsable and Description Attribute from IPermissionBasedControl and display the property PermissionKey accordingly in the Designers Property-Window, when working with a ExtendedTextBox.
Actual result: Nothing happens, Default Category Misc is applied, no Description, Browsable default yes is applied.
Is it not working, because Visual Studio Designer does not respect the AttributProvider-Attribute?
Is it not working, because AttributeProvider cannot target an Interface?
I've also tried method access to no success:
public class ExtendedTextBox : TextBox, IReadOnlyControl, IDataBindable, IPermissionBasedControl
{
...
[AttributeProvider(nameof(IPermissionBasedControl), nameof(IPermissionBasedControl.PermissionKey))]
public string PermissionKey { get; set; }
...
}
Okay, It seems like I had a wrong interpretation about what AttributeProvider is doing:
AttributeProviders description is Enables Attribute Redirection - but this doesn't mean, that it will (in my example) attach the Attributes of IPermissionBasedControl.PermissionKey to ExtendedTextBox.PermissionKey, the AttributeProvider is having effect on the Attributes of the type of the property.
i.e.: If I would specify
[AttributeProvider(typeof(Color))]
public object PermissionKey { get; set; }
the VisualStudio Designer would now Apply the Attributes of Color to the Value-Field for picking the object. (And therefore show a color-picker)
So, technically spoken, the AttributeProvider redirects Attributes for the type of the property, not for the property itself.
Related
I want to dynamically add attributes to properties of a given class, because the project will get bigger and the procedure is totally repeatinglly, something like so :
public class MyAttr : Attribute {
public MyAttr () {
foreach(var prop in properties) {
prop.Attributes.Add([Display(Name = nameof(prop.Name), ResourceType = typeof(Labels))]);
}
}
}
And use it on classes :
[MyAttr]
public class SomeClass {
public string Name {get; set;}
public string Description {get; set;}
}
of course the code above is not a real program, but I tried to make it simple.
Is it possible to do this? or if not, is there a workaround?
Update: I know there's an attribute called CallerMemberName that retrieves name of a given property, but how to get the property itself and add more attributes to it?
I think you might be able to make something work combining the below answers from other posts.
Access object properties from Attribute: Can C# Attributes access the Target Class?
Add attributes at runtime: How to add an attribute to a property at runtime by creating new types at run-time.
Unless you're going to be making regular changes across all the properties it may just be better to deal with the repetitive work up front. There are inevitably risks/issues down the road when doing stuff across the board like this.
If you define the following class:
public class Test
{
public string Something { get; set; }
public string AnotherThing { get; set; }
public string DefaultStringValue { get; set; }
}
then, when you list the properties with IntelliSense, DefaultStringValue appears first in the list, despite this not being in alphabetical order:
If you change the property name to anything else, normal service is resumed:
Anyone know why this is? I'm curious...
(Note: I'm also using Resharper Ultimate 2018.1.2)
This is IntelliSense (and/or ReSharper) being, well, intelli-gent, and trying to help you re-use things you've used previously. If you start again from nothing, you'll find that things are alphabetical:
I'm betting that at some point in the past, you've made use of the DefaultStringValue property, and not the others, so it's suggesting it first:
Having renamed it, if you enter the line t.DefaultStringValu = "x";, then delete it again, next time you enter t. and invoke IntelliSense, DefaultStringValu will now be top of the list:
I would like to know if there is a maneer (may be with data annotation) to set the value of
property.DisplayPropertyName
.
Do you have any idea please?!!
Actually I think you mean to ask how you can choose a different property as the display property (i.e., the property that is used to briefly denote objects of your class in the UI).
The Entity Framework Service that generates the metadata for scaffolding choose the first property of string type (as far as I know), but you can change that by applying the DisplayColumn attribute to your class:
[DisplayColumn("Alias")]
public class Person {
public string Name { get; set; }
public string Alias { get; set; }
}
Note, however, that this only works for properties that are actually mapped to a column in the database. (I was trying to use a computed property, not mapped, but that led to an error message while scaffolding.)
That is quite easy to do:
[DisplayName("RenamedProperty")]
you can even add a description
[Description("Demonstrates DisplayNameAttribute.")]
or perhaps you would like it to reference your resources so it adapts itself to the selected language:
[Display(Name = "Description", ResourceType = typeof(Resources))]
I want to display multiple instances of one class in my PropertyGrid. The class looks like this:
public class Parameter
{
[Description("the name")]
public string Name { get; set; }
[Description("the value"), ReadOnly(true)]
public string Value { get; set; }
[Description("the description")]
public string Description { get; set; }
}
I have many instances of that class in a TreeView. When I select one of them in my TreeView, the properties show up in the PropertyGrid as expected. So far so good, but I want to customise this behaviour in the following way:
For each single instance I want to be able to prevent the user from modifying a specific property. By setting ReadOnly(true) within my class (as you can see in the example above), all Value properties will be disabled on a class-level.
After some research I found the following solution which gives me the opportunity to enable/disable a specific property at runtime:
PropertyDescriptor descriptor = TypeDescriptor.GetProperties(this)["Value"];
ReadOnlyAttribute attr =
(ReadOnlyAttribute)descriptor.Attributes[typeof(ReadOnlyAttribute)];
FieldInfo isReadOnly = attr.GetType().GetField(
"isReadOnly", BindingFlags.NonPublic | BindingFlags.Instance);
isReadOnly.SetValue(attr, false);
This approach works just fine but unfortunately also on class-level only. This means if I set the Value's isReadOnly to false, all of my Parameter-objects have the Value property writeable. But I want this ONLY on that one particular object (thus object-level). I really don't want to create separate classes for read/write and readonly properties.
As I am running out of ideas, your help is much appreciated :)
Thanks in advance!
EDIT: I need the readonly properties to be grayed-out, so the user can see that it's not allowed or possible to edit them.
EDIT: Linked article has been removed (I hope just temporary). You can fine a viable alternative in answers to How to add property-level Attribute to the TypeDescriptor at runtime?. Basically you have to add (at run-time) ReadOnlyAttribute through a TypeDescriptor for that property.
Take a look at this old but nice article on CodeProject, it contains a lot of useful tools for the PropertyGrid.
Basically you provide a class or a delegate that will be used to get the attributes of your properties. Because it will be invoked passing the instance of the object you want to get attributes for then you'll be able to return (or not) the ReadOnlyAttribute with a per object basis. Shortly: apply a PropertyAttributesProviderAttribute to your property, write your own provider and replace attributes from the PropertyAttributes collection based on the object itself (and not on the class)
You can wrap the object with a custom type descriptor, but I think that would be overkill, because you have to create a new typedescriptor-derived class.
So, a simplest solution would be to have a flag, something like:
public class Parameter
{
private string thevalue;
[Browsable(false)]
public bool CanEditValue { get; set; }
[Description("the name")]
public string Name { get; set; }
[Description("the description")]
public string Description { get; set; }
[Description("the value"), ReadOnly(true)]
public string Value {
get { return this.thevalue; }
set { if (this.CanEditValue) this.thevalue = value; }
}
}
I have a class with a set of properties As given below.
class ContactInfo
{
[ReadOnly(true)]
[Category("Contact Info")]
public string Mobile { get; set; }
[Category("Contact Info")]
public string Name{ get; set; }
}
The objects of this class is being assigned to a property grid, so that the users can update an existing contact. you can see that Mobile is marked as ReadOnly.
But, when I want to add an entirely new Contact, I would want the users to be able to edit the contact Mobile also. For that I need to remove the Readonly property dynamically from the Type, before assigning the object to the property grid. Is it possible?
You can not remove the attribute at runtime, but you can use reflection to change the ReadOnly attribute's ReadOnly private backing field to False. Making it the equivalent of [ReadOnly(false)]
See this article for details:
http://codinglight.blogspot.com/2008/10/changing-attribute-parameters-at.html
Edit: fixed link
I have to agree w/ Omu; you're really talking about two classes (view models) in this case, to support your two different views. Something like
CreateContactViewModel and EditContactViewModel
it's not possible at the moment to remove attributes dinamycally (at runtime)
as a suggestion you can do 2 classes: one with the attributes and one without
The CodingLight.com blog moved to blogspot (the above link is broken).
See http://codinglight.blogspot.com/2008/10/changing-attribute-parameters-at.html.
Moreover, SysAdmin's followup did not mention the [RefreshProperties(RefreshProperties.All)] attribute that seems to be necessary for an actually-working solution.
Finally, I believe that even David Morton (author of the quoted article) missed one very important thing: if the class (ContactInfo, in SysAdmin's followup example) does not have at least one property with the [ReadOnly] attribute defined at compile time, then when the "isReadOnly" FieldInfo is set to true at runtime the result is that the whole class turns read-only.
I followed up the suggestion by Legenden. Here is what I came up with
class ContactInfo
{
[ReadOnly(true)]
[Category("Contact Info")]
public string Mobile { get; set; }
[Category("Contact Info")]
public string Name{ get; set; }
public void SetMobileEdit(bool allowEdit)
{
PropertyDescriptor descriptor = TypeDescriptor.GetProperties(this.GetType())["Mobile"];
ReadOnlyAttribute attrib = (ReadOnlyAttribute)descriptor.Attributes[typeof(ReadOnlyAttribute)];
FieldInfo isReadOnly = attrib.GetType().GetField("isReadOnly", BindingFlags.NonPublic | BindingFlags.Instance);
isReadOnly.SetValue(attrib, !allowEdit);
}
}