Change .NET Property Grid readonly attribute at runtime - c#

I have a property grid that i wanna change readonly attribute of some of its items at run-time.
Changing readonly for simple items is easy and i have no problem there, my problem is i cannot change readonly for hierarchical items like latitude and longitude in picture below. I even tried to make the whole "Position" category readonly but it didn't seem to help.
Side question: Is there any way to put some items in a group (like this position item) without wrapping them in a class?

Yes! This could be helpful!
public class Member
{
string name;
bool isMarried;
string spouseName;
public string Name
{
get { return name; }
set { name = value; }
}
[System.ComponentModel.RefreshProperties(RefreshProperties.All)]
public bool IsMarried
{
get { return isMarried; }
set
{
isMarried = value;
bool newValue = !value;
PropertyDescriptor descriptor = TypeDescriptor.GetProperties(this.GetType())["SpouseName"];
ReadOnlyAttribute attrib = (ReadOnlyAttribute)descriptor.Attributes[typeof(ReadOnlyAttribute)];
FieldInfo isReadOnly = attrib.GetType().GetField("isReadOnly", BindingFlags.NonPublic | BindingFlags.Instance);
isReadOnly.SetValue(attrib, newValue);
}
}
[ReadOnly(true)]
public string SpouseName
{
get { return spouseName; }
set
{
spouseName = value;
}
}
}

Related

How to resolve the error 'Property is null or is not IEnumerable' in Xamarin

I'm trying to making the following
<platform:TablePrac.Columns>
<platform:TextColumn Caption="it"/>
<platform:TextColumn Caption="is"/>
</platform:TablePrac.Columns>
But when I run this the error 'Property Columns is null or is not IEnumerable' is occurred.
The code flow is following.
When I wrote like above .xaml code, the property named Columns set the value.
(Columns is defined like below)
public List<Column> Columns
{
set
{
columns = value;
SetValue(ColumnsProperty, value);
}
get
{
return (List<Column>)GetValue(ColumnsProperty);
}
}
public class Column
{
public string caption;
public Type type;
}
public class TextColumn : Column
{
public TextColumn() : base()
{
this.type = typeof(string);
}
public TextColumn(string cap) : base()
{
this.caption = cap;
this.type = typeof(string);
}
public string Caption
{
set { caption = value; }
get { return caption; }
}
public Type Type
{
get { return type; }
}
}
As a very similar case, defining StackLayout and making new views in it like below
<StackLayout>
<Label Text="it"/>
<Label Text="is"/>
</StackLayout>
is same in .cs code like below
StackLayout stack = new StackLayout
{
Children =
{
new Label { Text = "it"},
new Label { Text = "is"}
}
};
So, I want to make property Columns work as StackLayout in .xaml but I don't know how. I spend two days to solve it.... I need your help
Thank you.
(Plus, StackLayout and Children are defined like below
StackLayout
public class StackLayout : Layout<View>
Layout
[Xamarin.Forms.ContentProperty("Children")]
public abstract class Layout<T> : Layout, IViewContainer<T>
where T : View
{
public IList<T> Children { get; }
...
}
)
The problem is not IEnumerable but Null value.
When using BindableProperty in Xamarin.Forms, you can assign a default value to the Property. For example, give default value 'new List()' solve this problem.
Follwing is my code, if you have same problem, check it.
Before :
public static readonly BindableProperty ColumnsProperty = BindableProperty.Create("Columns", typeof(List<Column>), typeof(TablePrac));
public List<Column> Columns
{
set
{
SetValue(ColumnsProperty, value);
}
get
{
return (List<Column>)GetValue(ColumnsProperty);
}
}
After :
public static readonly BindableProperty ColumnsProperty = BindableProperty.Create("Columns", typeof(IEnumerable<Column>), typeof(TablePrac), new List<Column>());
public IEnumerable<Column> Columns
{
set
{
SetValue(ColumnsProperty, value);
}
get
{
return (IList<Column>)GetValue(ColumnsProperty);
}
}
I convert type of return value of Columns to IList since in case of 'StackLayout's Children', the type of Children is IList type. There is no other reason.

Get Nvarchar length in Database From Class

I have Class use as Table in SQL server
Then properties below
[Column(Storage = "_new_name", DbType = "nvarchar (2000)")]
public string new_name { get { return _new_name; } set { _new_name = value; } }
So. Can I get Length From my Class using C#
In this case It's 2000
Thank
You can't easily, without resorting to Reflection. Attributes are meta-data, so they only decorate code with additional information required for various processes. In your case, for your ORM to identify which property maps to which column.
Assuming you have a class like this:
public class TestTable
{
private string _new_name;
private string _address;
[Column(Storage = "_new_name", DbType = "nvarchar (2000)")]
public string new_name {
get
{
return _new_name;
}
set
{
_new_name = value;
}
}
[Column(Storage = "_address", DbType = "nvarchar (5000)")]
public string address {
get
{
return _address;
}
set
{
_address = value;
}
}
}
You can read the attribute values from the properties like this:
var properties = typeof(TestTable).GetProperties();
var attributesPerProperty = new Dictionary<string, string>();
foreach (var propertyInfo in properties)
{
var attribute = System.Attribute.GetCustomAttributes(propertyInfo).FirstOrDefault();
if(attribute is ColumnAttribute)
{
var columnAttribute = (ColumnAttribute)attribute;
attributesPerProperty.Add(propertyInfo.Name, columnAttribute.DbType);
}
}
It's not an ideal way of doing things, and I've just given a rough example but if you really, really need to read this kind of information from your classes, the above will get you there.

Dynamically set readonly attribute of properties in a PropertyGrid

I have a PropertyGrid that I used to display the properties in a helper class. I assign the helper class to the PropertyGrid like this:
myPropertyGrid.SelectedObject = mySettingsHelper;
In the helper class I assign the ReadOnlyAttribute at design time like this:
[DisplayName("DisplayExA"),
Description("DescriptionExA"),
ReadOnlyAttribute(true)]
public string PropertyA { get; set; }
[DisplayName("DisplayExB"),
Description("DescriptionExB"),
ReadOnlyAttribute(false)]
public string PropertyB { get; set; }
[DisplayName("DisplayExC"),
Description("DescriptionExC"),
ReadOnlyAttribute(true)]
public string PropertyC { get; set; }
But now I need to be able to change this attribute on individual properties dynamically during runtime. Based on certain criteria some of these properties may need to be read-only or not read-only. How would I make the change dynamically at runtime?
EDIT:
I tried the following code but this sets the ReadOnly attribute for every instance of the object! I want to do it per object. Sometimes one object might have PropertyA read-only while a second object has PropertyA to not be read-only.
public static class PropertyReadOnlyHelper
{
public static void SetReadOnly(object container, string name, bool value)
{
try
{
PropertyDescriptor descriptor = TypeDescriptor.GetProperties(container.GetType())[name];
ReadOnlyAttribute attribute = (ReadOnlyAttribute)descriptor.Attributes[typeof(ReadOnlyAttribute)];
FieldInfo fieldToChange = attribute.GetType().GetField("isReadOnly",
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance);
fieldToChange.SetValue(attribute, value);
}
catch { }
}
}
I was able to do exactly what I need (object level assignment of the read-only attribute) using the library from this CodeProject article. What is nice is that it enables me to still use the .NET PropertyGrid and just use the custom attributes to handle the dynamic settings.
Use reflection to get the instance reference of the ReadOnlyAttribute class, then toggle the IsReadOnly property on that instance. Finally, re-select the item in the PropertyGrid if needed by settings its SelectedObjects to null and then resetting it. You might be able to do this using the PropertyGrid RefreshTabs method too, I'm not sure.
EDIT:
Unfortunately the IsReadOnly property itself is read only... in this case we'd have to use reflection to change the value of the backing field for the IsReadOnly property.
Add Readonly
TextBoxID.Attributes.Add("readonly","true");
Remove readonly
TextBoxID.Attributes.Remove("readonly");
Dynamically setting browsable or readonly attribute of a property in a PropertyGrid is often needed together and also they are similiar jobs
After a few touches, the great answer of Reza Aghaei about "Hide some properties in PropertyGrid at run-time" is also applicable for manipulating the readonly attribute.
public class CustomObjectWrapper : CustomTypeDescriptor
{
public object WrappedObject { get; private set; }
public List<string> BrowsableProperties { get; private set; }
public List<string> ReadonlyProperties { get; private set; }
public CustomObjectWrapper(object o)
: base(TypeDescriptor.GetProvider(o).GetTypeDescriptor(o))
{
WrappedObject = o;
BrowsableProperties = new List<string>() { "Text", "BackColor" };
ReadonlyProperties = new List<string>() { "Font" };
}
public override PropertyDescriptorCollection GetProperties()
{
return this.GetProperties(new Attribute[] { });
}
public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
List<PropertyDescriptor> result = new List<PropertyDescriptor>();
IEnumerable<PropertyDescriptor> properties = base.GetProperties(attributes).Cast<PropertyDescriptor>()
.Where(p => BrowsableProperties.Contains(p.Name));//unbrowsable filtering
foreach (var p in properties)
{
PropertyDescriptor resultPropertyDescriptor = null;
//handle being readonly
if (ReadonlyProperties.Contains(p.Name))
{
List<Attribute> atts = p.Attributes.Cast<Attribute>().ToList();
atts.RemoveAll(a => a.GetType().Equals(typeof(ReadOnlyAttribute)));//remove any readonly attribute
atts.Add(new ReadOnlyAttribute(true));//add "readonly=true" attribute
resultPropertyDescriptor = TypeDescriptor.CreateProperty(WrappedObject.GetType(), p, atts.ToArray());
}
else
{
resultPropertyDescriptor = TypeDescriptor.CreateProperty(WrappedObject.GetType(), p, p.Attributes.Cast<Attribute>().ToArray());
}
if (resultPropertyDescriptor != null)
result.Add(resultPropertyDescriptor);
}
return new PropertyDescriptorCollection(result.ToArray());
}
}
and the usage:
propertyGrid1.SelectedObject = new CustomObjectWrapper(myobject);
Please try the code below.
[CategoryAttribute("2. LINE"), DisplayNameAttribute("Spline Line Tension"),
DescriptionAttribute("Chart's Spline Line Tension "), ReadOnlyAttribute(false)]
public float _PG_SplineTension
{
get
{
bool lbReadyOnly = true;
SetPropertyReadOnly("_PG_SplineTension", lbReadyOnly);
return this.cfSplineTension;
}
set { this.cfSplineTension = value; }
}
private void SetPropertyReadOnly(string lsProperty, bool lbIsReadOnly)
{
PropertyDescriptor descriptor = TypeDescriptor.GetProperties(this.GetType())[lsProperty];
ReadOnlyAttribute attribute = (ReadOnlyAttribute)
descriptor.Attributes[typeof(ReadOnlyAttribute)];
FieldInfo fieldToChange = attribute.GetType().GetField("isReadOnly",
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance);
fieldToChange.SetValue(attribute, lbIsReadOnly);
}
Thanks a lot. Based on the answers I came with the following code that works just fine:
private void SetReadonly ( object o, bool value )
{
foreach ( PropertyInfo property in o.GetType().GetProperties() )
if ( property.GetCustomAttribute<ReadOnlyAttribute>() != null )
{
Attribute readOnly = TypeDescriptor.GetProperties( o.GetType() )[property.Name].Attributes[typeof( ReadOnlyAttribute )];
readOnly.GetType().GetField( nameof( ReadOnlyAttribute.IsReadOnly ), BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase ).SetValue( readOnly, value );
}
}
Check this page out:
https://www.codeproject.com/Articles/152945/Enabling-disabling-properties-at-runtime-in-the-Pr
Quoted from above post:
it is important to statically define the ReadOnly attribute of every property of the class to whatever value you want. If not, changing the attribute at runtime that way will wrongly modify the attributes of every property of the class.
Use reflection to modify the "ReadOnly" attribute of the target property during runtime to achieve your goal. The problem that setting one property applies to all is because you need to set all properties in the same PropertyGrid object with ReadOnly attribute explicitly to avoid that problem.
[RefreshProperties(System.ComponentModel.RefreshProperties.All)]
[ReadOnly(false)]
public string Country
{
get { return mCountry; }
set
{
mCountry = value;
PropertyDescriptor descriptor = TypeDescriptor.GetProperties(this.GetType())["State"];
ReadOnlyAttribute attribute = (ReadOnlyAttribute)
descriptor.Attributes[typeof(ReadOnlyAttribute)];
FieldInfo fieldToChange = attribute.GetType().GetField("isReadOnly",
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance);
fieldToChange.SetValue(attribute, mCountry != "U.S.");
}
}
[ReadOnly(true)]
public string State
{
get { return mState; }
set { mState = value; }
}

forcing a variable to hold certain values only

I am using vs 2012. I have a simple string property
string _someString;
public string MyString
{
get
{
return _someString;
}
}
I want this property to hold only certain values. So that when the client uses this property only those certain values can be used.
It sounds like what you really want is an enum:
public enum MyValues //TODO rename all the things
{
SomeValue,
SomeOtherValue,
FinalValue,
}
Then your property can be:
private MyValues value;
public MyValues MyValue
{
get { return value; }
}
If you need to get a string representation of that value just call ToString on the enum value:
string stringValue = value.ToString();
Use an enum as in :
enum MyEnum
{
AllowableValue#1,
AllowableValue#2,
...
}
public MyEnum myEnum { get; set; }
Then populate some UI element with only the values of the enum.
I suppose you want to have some validation on the setter then:
public string MyString
{
get
{
return _someString;
}
set
{
if (value == "a" || value == "b" /* ... */)
_someString = value;
else
throw new InvalidArgumentException("Invalid value!");
}
}
Make sure to set it via the property, not the actual member variable.

Automatic Property Values and Defaults [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How do you give a C# Auto-Property a default value?
I have a property in a class like so
public String fontWeight { get; set; }
I want it's default to be of "Normal"
Is there a way to do this in "automatic" style rather than the following
public String fontWeight {
get { return fontWeight; }
set { if (value!=null) { fontWeight = value; } else { fontWeight = "Normal"; } }
}
Yes you can.
If your looking for something like:
[DefaultValue("Normal")]
public String FontWeight
{
get;
set;
}
Do a google search for 'Aspect Oriented Programming using .NET'
..if this is overkill for you do this:
private string fontWeight;
public String FontWeight {
get
{
return fontWeight ?? "Normal";
}
set {fontWeight = value;}
}
No, an automatic property is just a plain getter and/or setter and a backing variable. If you want to put any kind of logic in the property, you have to use the regular property syntax.
You can use the ?? operator to make it a bit shorter, though:
private string _fontWeight;
public String FontWeight {
get { return _fontWeight; }
set { _fontWeight = value ?? "Normal"; }
}
Note that the setter is not used to initialise the property, so if you don't set the value in the constructor (or assign a value in the variable declaration), the default value is still null. You could make the check in the getter instead to get around this:
private string _fontWeight;
public String FontWeight {
get { return _fontWeight ?? "Normal"; }
set { _fontWeight = value; }
}
You will need to use a backing field.
private string fontWeight;
public String FontWeight
{
get { String.IsNullOrEmpty(fontWeight) ? "Normal" : fontWeight;}
set {fontWeight = String.IsNullOrEmpty(value) ? "Normal" : value;}
}
You either need to use a backing field and initialize that to your default value
private String fontWeight = "Normal";
public String FontWeight
{
get { return fontWeight; }
set { fontWeight = value; }
}
or, keep the auto property and call the setter in your constructor
public constructor()
{
FontWeight = "Normal";
}
public String FontWeight { get; set; }
One way to do it is using PostSharp as detailed in this answer to a similar question.
You could use the DefaultValue attribute:
[DefaultValue("Normal")]
public string FontWeight { get; set; }
Although it notes that
A DefaultValueAttribute will not cause a member to be automatically initialized with the attribute's value. You must set the initial value in your code.
so you could use this in conjunction with initialisation in the constructor or via a backing field and default handling.
You'd need either a variable like so:
string fontWeight;
public string FontWeight
{
get
{
if (string.IsNullOrEmpty(fontWeight))
fontWeight = "Normal";
return fontWeight;
}
set { fontWeight = value; }
}
Or use a Constructer to set an initial value:
class FontClass
{
public string FontWeight { get; set; }
public FontClass()
{
FontWeight = "Normal";
}
}

Categories

Resources