At runtime, how can I test whether a Property is readonly? - c#

I am auto-generating code that creates a winform dialog based on configuration (textboxes, dateTimePickers etc). The controls on these dialogs are populated from a saved dataset and
needs to Set and Get properties for various control objects (custom or other).
//Upon opening of form - populate control properties with saved values
MyObject.Value = DataSource.GetValue("Value");
//Upon closing of form, save values of control properties to dataset.
DataSource.SetValue("Value") = MyObject.Value;
Now this is all fine, but what of readOnly properties? I wish to save the result of the property but need to know when to NOT generate code that will attempt to populate it.
//Open form, attempt to populate control properties.
//Code that will result in 'cannot be assigned to -- it is read only'
MyObject.HasValue = DataSource.GetValue("HasValue");
MyObject.DerivedValue = DataSource.GetValue("Total_SC2_1v1_Wins");
//Closing of form, save values.
DataSource.SetValue("HasValue") = MyObject.HasValue;
Remember that I do not know the type of object I've instantiate until runtime.
How can I (at runtime) identify a readonly property?

With PropertyDescriptor, check IsReadOnly.
With PropertyInfo, check CanWrite (and CanRead, for that matter).
You may also want to check [ReadOnly(true)] in the case of PropertyInfo (but this is already handled with PropertyDescriptor):
ReadOnlyAttribute attrib = Attribute.GetCustomAttribute(prop,
typeof(ReadOnlyAttribute)) as ReadOnlyAttribute;
bool ro = !prop.CanWrite || (attrib != null && attrib.IsReadOnly);
IMO, PropertyDescriptor is a better model to use here; it will allow custom models.

I noticed that when using PropertyInfo, the CanWrite property is true even if the setter is private. This simple check worked for me:
bool IsReadOnly = prop.SetMethod == null || !prop.SetMethod.IsPublic;

Also - See Microsoft Page
using System.ComponentModel;
// Get the attributes for the property.
AttributeCollection attributes =
TypeDescriptor.GetProperties(this)["MyProperty"].Attributes;
// Check to see whether the value of the ReadOnlyAttribute is Yes.
if(attributes[typeof(ReadOnlyAttribute)].Equals(ReadOnlyAttribute.Yes)) {
// Insert code here.
}

I needed to use this for different classes, so I created this generic function:
public static bool IsPropertyReadOnly<T>(string PropertyName)
{
MemberInfo info = typeof(T).GetMember(PropertyName)[0];
ReadOnlyAttribute attribute = Attribute.GetCustomAttribute(info, typeof(ReadOnlyAttribute)) as ReadOnlyAttribute;
return (attribute != null && attribute.IsReadOnly);
}

Related

Should I check VisualElement.IsVisible before setting it?

Visual elements in Xamarin Forms are made visible/invisible by setting their IsVisible property. Changing some properties or data on visual elements can cause the interface to redraw, which can be expensive.
I'm trying to improve performance on my app so I've introduced caching and tried to make some layout simplifications, but I have to make a lot of visual elements visible/invisible and wondered wht the best way would be to optimise this.
Which is the more efficient operation?
var myButton.IsVisible = true;
Or:
if(!myButton.IsVisible) myButton.IsVisible = true;
If Xamarin already checks the state and decides whether to redraw then the second option is redundant and therefore inefficient. If a redraw happens every time the property is set then it will be more efficient. I can't find any documentation to tell me which is the case.
Any good implementation of a DependencyProperty (or INotifyPropertyChanged) member should ignore successive sets if that value is the same. For that reason let IsVisible work out what to do rather than place responsiblity on callers.
If Xamarin already checks the state and decides whether to redraw then the second option is redundant and therefore inefficien
That's right. Xamarin, like other XAML-based implemenations, are visually-efficient (unlike WinForms) so setting a visual property is not likely to immediately cause a screen refresh. Additonally, the entire app window is rendered in a single blit to avoid flicker.
In Xamarin, setting button.IsVisible for example makes it way down to BindableObject.SetValueActual (Button has BindableObject in its inheritance graph) where checks are made for sameness. The new value is only applied if the value is different or if SetValueFlags.RaiseOnEqual is set.
OnPropertyChanged and PropertyChanged are called on different values or if RaiseOnEqual is set.
Xamarin source code from GitHub: (my comments)
void SetValueActual(BindableProperty property, BindablePropertyContext context, object value, bool currentlyApplying, SetValueFlags attributes, bool silent = false)
{
object original = context.Value;
bool raiseOnEqual = (attributes & SetValueFlags.RaiseOnEqual) != 0;
bool clearDynamicResources = (attributes & SetValueFlags.ClearDynamicResource) != 0;
bool clearOneWayBindings = (attributes & SetValueFlags.ClearOneWayBindings) != 0;
bool clearTwoWayBindings = (attributes & SetValueFlags.ClearTwoWayBindings) != 0;
bool same = ReferenceEquals(context.Property, BindingContextProperty) ? ReferenceEquals(value, original) : Equals(value, original);
if (!silent && (!same || raiseOnEqual))
{
property.PropertyChanging?.Invoke(this, original, value);
OnPropertyChanging(property.PropertyName);
}
if (!same || raiseOnEqual)
{
context.Value = value; // <---------- assignment
}
.
.
.
if (!silent && (!same || raiseOnEqual)) // <------- notifications guard
{
if (binding != null && !currentlyApplying)
{
_applying = true;
binding.Apply(true);
_applying = false;
}
OnPropertyChanged(property.PropertyName);
property.PropertyChanged?.Invoke(this, original, value);

How can I (generically) accept any control and set it's Text property if it has one?

As part of my progressbar control, I have added a method to accept any control passed to it which will be used as a method for 'reporting' progress or task status in addition to display on the progressbar itself :
public void SetReportObject( object obj ) {
}
The problem I am having, is when I go to set the Text value or as some controls have TextValue, there is no property available to obj and thus generates errors in the IDE preventing this from compiling.
I think, some sort of typeof should be implemented, but I am unsure on how to go about this. The object being passed can be any user control.
I am using c# in a WinForms project.
You can use reflection to set the property, without needing to know the exact type at compile time. Something like this will do:
public void SetReportObject( object obj )
{
if(obj == null) throw new ArgumentNullException("obj");
PropertyInfo textProperty = obj.GetType().GetProperty("Text");
if(textProperty == null) throw new InvalidOperationException("The control must have a Text property");
if(!textProperty.CanWrite) throw new InvalidOperationException("The control must have a setteable Text property");
textProperty.SetValue(obj, "0%", null);
}
I think you can take at least Control as a base class parameter instead of object, but will vary depending on your usage. I also doubt if this is really a good practice, but certainly do the trick.
I don't know if I understood your problem. If you are handling some type object that is not a Control, I think your api needs the consumer information.
public void SetReportObject<T>(T obj, Expression<Func<T, string>> data)
{
string yourData = "Something to Notify";
var exp = data.Body as MemberExpression;
var pInfo = exp.Member as PropertyInfo;
pInfo.SetValue(obj, yourData, null);
}
Follow an example that u have to call using a TextBox:
SetReportObject<TextBox>(textBox1, x => x.Text);
Just accept Control as your type, there is a Text property on it that all controls will implement.
public void SetReportObject( Control obj ) {
obj.Text = "This is some text";
}
However, if you are just using it for reporting progress I would recommend abstracting away the control portion and just use a IProgress<int> instead.
public void UpdateProgress(IProgress<int> progress) {
progress.Report(_currentProgress);
}
This has the added benifit of no longer needing to do a control.Invoke before you update the text from a non UI thread.

Set Control Properties using strings C#

I am currenly writing a library that allows my to customize form elements. The code below is a function that gets the name of a control, gets the name of the property and then sets the property of the control but I cant seem to get it to work for some reason. Thanks any help is appricated.
The code:
public void SetProp(string name, string prop, string value)
{
Form FormControl = Application.OpenForms[form];
Control mycontrol = FormControl.Controls.Find(name, true)[0];
PropertyInfo pInfo = mycontrol.GetType().GetProperty(prop);
TypeConverter tc = TypeDescriptor.GetConverter(pInfo.PropertyType);
var x = tc.ConvertFromString(value);
pInfo.SetValue(name, x, null);
}
Sample Call:
SetProp("greg", "Text", "hi")
You need to pass in the actual source object into the PropertyInfo.SetValue call so it can actually be modified. PropertyInfo is basically just information about the property (hence the name), it has no attachment to that specific instance.
You can get it work by changing your call like so:
pInfo.SetValue(mycontrol, x);
http://msdn.microsoft.com/en-us/library/hh194291(v=vs.110).aspx

wpf binding with different source and target

Is is possible to make a binding in WPF whereas the source and target are different properties.
A la something like
Binding="{Binding Source=MySourceProperty, Target=MyTargetProperty}"
As requested an explanation of what I need to do:
The program among other things allows editing of properties that are part of a primary key in the database. If the property just gets changed, then this will either not update the DB value or create a duplicate, depending on how I handle saving the object. A different target would allow this to work (by explicitly specifying what to update by using the 'old' value).
A Binding defined in XAML is always targeting the object and property on which it's defined.
If you define the Binding in code, you can/must specify the source and target explicitly. This is, essentially, how the Binding class works:
Binding binding = new Binding("SourceProperty"); // Sets up the source property
myBinding.Source = mySourceObject; // sets up the source object
targetProperty.SetBinding(TargetType.TargetDepProperty, binding); // This sets the target object/binding
The XAML markup extension for a binding takes care of setting up the target side of the equation automatically, so it's always the object on which you define the binding.
I'll try to answer WHAT you need instead of asked incorrect HOW
"If the property just gets changed, then this will either not update
the DB value or create a duplicate"
In your property setter you should check set { if (this.someMember != value) if the typed in value is has changed:
public event PropertyChangedEventHandler PropertyChanged;
private string someMember;
public int SomeProperty
{
get
{ return this.someMember; }
set
{
if (this.someMember != value)
{
someMember = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("SomeProperty"));
}
}
}
As aside-note (or off-topic),
you might find useful the codeproject DataContext in WPF article in its last Download the source code has a sample when updates of one VisualModel's property is reflected (synchronized with updates in the other VM's property)
Immediately after launch:
The text typed in the 1st textbox is reflected in the 2nd textbox and vice versa, the text typed in the 2nd textbox is reflected in the 1st.
The text typed in the 3d textbox is reflected in the 4th textbox (and in textblock content at bottom) and vice versa, the text typed in the 4th textbox is reflected in the 3d (and in textblock content at bottom) .
Note that the download is DataCotext Inner Objects.zip which is unzipped into directory and solution with name Bindingtoclassesstate
In the set of the public property you have access to the old value
key is the old value
value is the proposed value from the binding
you can reject the value that comes from the binding
private string key;
public string Key
{
get { return key; }
set
{
if (key == value) return;
// try data update
bool success = updateDB();
if (success) key = value; // only update if success
}
}
I would combine the above with validation to notify the user if a value was invalid.
Validation Class

How to get control properties then save them to XML and load it back?

Actually I would need 4 methodes. I'm using c# .NET 3.5 and windows forms.
Get ALL control properties (also sub properites like MenuItems etc.) from current form where name matches list name //don't works ok
Save results to XML //don't know how to save the results
Load result from XML and
finally set loaded control properties from XML. //work great
Now I'm doing this form step 1:
public static Dictionary<string, object> xmlDictionary;
public Control FindControlRecursive(Control container, List<string> properties)
{
foreach (Control controls in container.Controls)
{
Type controlType = controls.GetType();
PropertyInfo[] propertyInfos = controlType.GetProperties();
foreach (PropertyInfo controlProperty in propertyInfos)
{
foreach (string propertyName in properties)
{
if (controlProperty.Name == propertyName)
{
xmlDictionary.Add(controlProperty.Name, controlProperty.GetValue(controls, null));
}
}
}
Control foundCtrl = FindControlRecursive(controls, properties);
if (foundCtrl != null)
return foundCtrl;
}
return null;
}
Calling the metod:
List<string> propertyNames = new List<string>(); //list of all property names I want to save
propertyNames.Add("Name");
propertyNames.Add("Text");
propertyNames.Add("Size");
FindControlRecursive(this, propertyNames); //this is the current form
This method doesn't return all control properties and I dont know why.
Step 4.:
//field = some new field
//newValue = new value
FieldInfo controlField = form.GetType().GetField(field, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
object control = controlField.GetValue(form);
PropertyInfo property = control.GetType().GetProperty(newValue);
property.SetValue(control, items.Value, new object[0]);
Step 4 work great, but don't know how to iterate through XML reults.
Could you please help me to solve these problems.
Thank and best regards.
Are you aware that with Windows Forms there is an existing settings infrastructure you can use to save settings of controls and your forms? From the designer, select a control and in the properties under Application Settings, then Property Binding, you can bind any property on the control to a property that will be generated to access and save that property value. The settings can be application or user scoped. These settings will also use isolated storage, allow you to upgrade to different versions of settings to maintain user settings between versions, and many other features. So this may not directly answer your question, but may be a better solution for your particular problem. Once you bind a property you can save changes whenever you want, on a per user or per application basis like so:
Settings.Default.TextBox1 = textBox2.Text;
Settings.Default.BackColor = Color.Blue;
Settings.Default.Save();

Categories