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
Related
I'm trying to come up with a generic way to find the value of a property of a control in Silverlight application given key of a property is specified.
Here is what I'm trying to do :
public class FetchPropertyFromControlCommand
{
var frameworkElement = broker.Find<FrameworkElement>(ReferenceOfControol);
var properties = frameworkElement.GetProperties();
if (properties.ContainsKey(propertyName))
{
var output = String.Empty;
var propertyValue = properties.TryGetValue(propertyName, out output);
}
return false;
}
Here frameworkElement.Getproperties lists the properties with key and value. But Value holds the class name and not the actual value what I get when I spy the element. That is BorderBrush="#FFCBCBCB"
[]
Could anyone tell me why GetProperties method doesn't show the actual value what a UI spy shows for a property of a control. Is there any other way of achieving this?
I have a WPF MainWindow Form with a ListBox and a TextBox that looks like this:
Figure A. WPF MainWindow with Sample Text.
Now, the Load Assembly... OnClick button event allows me to select a .NET Assembly and load it up using DnLib
Then, if I want to display the Methods bodies I would do it like so:
Assembly asm = Assembly.LoadFile(filename);
foreach (Module mod in asm.GetModules())
{
foreach (Type types in mod.GetTypes())
{
foreach (MethodInfo mdInfo in types.GetMethods())
{
listBox.Items.Add(mdInfo.Name);
}
}
}
This adds each found Method name to the ListBox on the left, resulting like so:
Figure B. Showing the ListBox Filled with Methods Names
Now the trick part, I would like to for whichever method I select from the ListBox to display its respective MethodBody IL on the TextBox
How can I achieve such thing?
«Phew!» Finally Solved it!
Here's the solution for whoever tries to do the same thing in the future.
Make an instance of 'List' and then iterate through the methods and assign the names to such list, then whenever your SelectedItem index value changes, I can simply call GetMethodBodyByName and then I can surely solve this issue
Here's how to implement the function GetMethodBodyByName:
public string GetMethodBodyByName(string methodName)
{
ModuleDefMD md = ModuleDefMD.Load(filename);
foreach (TypeDef type in md.Types)
{
foreach (MethodDef method in type.Methods)
{
for (int i = 0; i < type.Methods.Count; i++)
{
if (method.HasBody)
{
if (method.Name == methodName)
{
var instr = method.Body.Instructions;
return String.Join("\r\n", instr);
}
}
}
}
}
return "";
}
The idea is that 'GetMethodBodyByName' will receive the method name as a parameter, then it will iterate through methods and see if a method matches the given name, then if found, the function will just simply iterate through that method and output the method's body.
Here's how my ListBox_SelectedItemChanged event looks like:
private void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
textBox.Text = "";
textBox.Text = GetMethodBodyByName(method[listBox.SelectedIndex].Name);
}
That's All Folks!
Note: Be careful when doing this approach as if when you request names, different methods can have the same names. But that's a cake for another day, I'm done for now! take care bye-bye!
Working our way up for the Ultimate Solution!
The WPF MainWindow Forms carry with themselves two little useful properties, they are: Tag and Content, the idea is the following one:
With the Tag and Content Property we can assign any values to it that later it can be retrieved On-The-Fly without having to depend on Methods names specifically for this task.
So you would instead of looping each method and get its name respectively you can just do the way I did:
Iterate through the Method, and assign its body to the Tag property, and its name to the Content property, as this last property is the one that handles the actual Title property, so disregarding anything you do with the method in the future and even if it had the same name of another one, it will work no matter what.
How Can We Implement It?
Simply:
<...>
// Inside Method Body iteration routine...
<...>
var instr = mdInfo.Body.Instructions;
// Allocate in a new `ListBoxItem` each method and add it to the current listbox with their
// ... respective Tag and Content information... // Many Thanks Kao :D
newItem = new ListBoxItem();
newItem.Content = mdInfo.Name;
newItem.Tag = string.Join("\r\n", instr);
method.Add(mdInfo);
listBox.Items.Add(newItem);
Then on your SelectedItem Index-Value-Changed Event put this:
MSILTextBox.Clear();
// Retrieve them given the selected index...
// ... the returned value will be the Tag content of the ...
// ... previously saved item.
string getTag= ((ListBoxItem)listBox.SelectedItem).Tag.ToString();
MSILTextBox.Text = getTag;
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.
I'm trying to update textedit control through the Client class object databindings with INotifyPropertyChanged implementation and i can't get it to work. The object behind (datasource) updates but the textedit still remains blank. If i type the text into the editbox the datasource gets updated. Would you help please? Here's the relevant code i'm using:
public class Client : NotifyProperyChangedBase
{
private string _firstname;
public string Firstname
{
get
{
return this._firstname;
}
set
{
this.CheckPropertyChanged<string>("Firstname", ref _firstname, ref value);
}
}
}
public Client ClientA = new Client();
Binding fname = new Binding("Text", ClientA, "Firstname", true, DataSourceUpdateMode.OnPropertyChanged);
ultraTextEditor_firstname.DataBindings.Add(fname);
ClientA.Firstname = "testN"; <== editbox remains blank ...
Am I missing something here? Thanks in advance, Peter.
I am assuming your base is implemented something along the lines of this example. If I am incorrect in my assumption, you will need to provide the implementation of your NotifyProperyChangedBase class.
You may also want to review the Binding(String, Object, String, Boolean, DataSourceUpdateMode) constructor documentation, as it discusses the control events the binding attempts to locate.
Looking at that example, you will want to try something like this:
System.ComponentModel.BindingList<Client> bindings = new System.ComponentModel.BindingList<Client>();
Client clientA = bindings.AddNew();
clientA.Firstname = "John";
textEditControl.DataSource = bindings;
// This change presumably will be refelected in control
clientA.Firstname = "Jane";
Update:
After reviewing the documentation on the Add method of the ControlBindingsCollection class; I believe that the data source of the Binding needs to implement the IListSource interface in order to properly participate in the binding (all MSDN examples are DataSet or DataTable which implement this interface).
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);
}