XAF - Changing value of a PropertyEditor - c#

I'm learning XAF and I want to know how to access the value of a PropertyEditor to change it. I want to take the value from a PropertyEditor and put that value into another PropertyEditor's value. My code is like this:
Property Editor reserva = (PropertyEditor)((DetailView)View).FindItem("Reserva"); //This is a custom object
PropertyEditor dni = (PropertyEditor)((DetailView)View).FindItem("Dni");//This is a simple text editor
PropertyEditor dniReserva = (PropertyEditor)reserva.View.FindItem("Dni");//This is a variable from the custom object
dni.PropertyValue = dniReserva.ControlValue;
This does not work, any ideas? Thank you

Are you talking about copying the value of a non persistent property to another non persistent property? Because in any other case I believe there are more suitable ways to copy a value, working with the actual properties (here is usefull answer to help you with that)and not the editors.
If however you actually need this,I believe you could create a ViewController and use the PropertyEditor properties like this
foreach (PropertyEditor editor in ((DetailView)View).GetItems<PropertyEditor>())
{
var control = editor.Control as IntegerEdit;
if (control != null)
{
if (editor.Id == "Id" || editor.Caption == "Id")
{
control.Enabled = false;
}
}
}

Each property editor in XAF reads value from the specific property of the business object. This specificity reduces your task to copying the value of the specific property to another one.
In the ViewController, you can access the current business object using the View.CurrentObject property. Once the property is updated with an appropriate value, the new value will immediately appear in the property editor.
If the business object does not implement the INotifyPropertyChanged interface (for example, if you are using Entity Framework Code First), you may also want to call the View.Refresh method to make new values appear in the editor.

Related

C# .NET: Get Start-up/Designer-assigned values of Controls

I want to retrieve the designer-assigned values of controls, e.g. when I designed a TextBox with the .Text "Hello World", then change the .Text during runtime: How can I retrieve the String "Hello World" again during runtime?
My thoughts so far:
I can write a set-up routine and assign these "default" values in there alone, and call that method again when ever I want to reset the values.
Cons:
- Not as flexible (No way of resetting individual Controls)
- would need to write a routine for each implementation
- missing out on the convenience of just setting the values once in the designer
I would really like a static method/extension which would enable me to plug in my Control and get the values assigned to it within my class designer.
I started trying to implement an extension within my extension library, where I create a new instance of the main Form Class and grab the value from there - but that approach has obvious disadvantages:
- The Form Class may have different constructor signatures, like string[] args for applications with console arguments; creating instances of unknown signatures isn't trivial or a good idea in general
- It's a heavy weight approach; depending on the complexity of the project, you might not want to create piles of instances every time you want to get a Control's designer-assigned value.
- This would execute what ever code is in the constructor
So I'm really hoping I can use Reflection or something similar to get to the designer-assigned values.
But I have no idea how to go about looking for that; googling things like "visual .net get designer default control value" yielded no relevant results.
Can somebody point me in the right direction?
My test extension method currently looks something like this:
public static string getDefaultText(this Control c)
{
Form mainForm = Form.ActiveForm; // just a quick easy way to test this
Type t = mainForm.GetType();
var f = Activator.CreateInstance(t);
Control a = ((Form)f).Controls[c.Name]; // unfortunately, .Controls only contains direct children, not all descendants; recursive searching would be needed
return a.Text;
}
and works for what it is, which is to say a proof of concept of a bad approach :/
#Selvin commented with a good idea: Let me formulate a preliminary work-around from it.
Once the Form uses Localization (Set Localizable to True at design-time), a version of the Control that represents its state in the Designer is stored in the app's Resources.
This seems to include all designable properties (everything in the Properties panel/window) (Edit: apparently not).
Using a ComponentResourceManager (implementing ResourceManager), we can apply that state to a Control again during runtime using its method ApplyResources:
public static void resetControl<T>(this T c, string key = "") where T : Control // Using generic approach for the convenience of being able to use the Type T
{
Form f = c.FindForm();
ComponentResourceManager resources = new ComponentResourceManager(f.GetType()); // Manage the resources from our Form class
if (key == "")
{
resources.ApplyResources(c, c.Name); // Simply natively apply all resources
}
else // If we want to reset only one specific property...
{
// rather than trying to get to the data serialized away somewhere, I'm using this work-around
T r = (T)Activator.CreateInstance(c.GetType()); // create a new instance of the Control in question
resources.ApplyResources(r, c.Name);
setAttr(c, key, r.getAttr(key)); // setAttr and getAttr are helper extensions I always have on hand as well
}
}
public static void setAttr(this object o, string key, object val)
{
foreach (PropertyInfo prop in o.GetType().GetProperties())
{
string nam = prop.Name;
if (nam == key)
{
prop.SetValue(o, val);
return;
}
}
}
public static dynamic getAttr(this object o, string key)
{
Type myType = o.GetType();
IList<PropertyInfo> props = new List<PropertyInfo>(myType.GetProperties());
foreach (PropertyInfo prop in props)
{
if (prop.Name == key)
{
return prop.GetValue(o, null).ChangeType(prop.PropertyType);
}
}
return null;
}
Usage:
// Reset everything to the Designer state:
textBox1.resetControl();
// Reset only the TextAlign property:
textBox1.resetControl("TextAlign");
// Reset only the Dock property:
textBox1.resetControl("Dock");
// ... etc
This works reasonably well and I can write further abstractions making the process of resetting certain values easier.
I don't like having to create a new instance of a control, but I'm willing to live with it for this work-around. However, better approaches are definitely welcome.
Update: Shortcomings
It looks like an empty .Text from Designer will not overwrite a non-blank .Text using ApplyResources, it seems.
However, using a proxy Control instance like in the case of supplying "Text" as a key string in my extension method above, this still works
It looks like it doesn't work with most Controls. Testing so far only had it work on TextBoxes, Labels and CheckBoxes with the .Text property, but not the .Checked property of the CheckBox
Attempting to reset a NumericUpDown's .Text (for example because you're carpet-bombing everything with resetting "Text" in order to reset it for those who have a relevant .Text) will set its .Value to 0

Only update some properties on an EF entity that are not set to null

I've got a browser sending up JSON but it only includes the properties of a given model that have been changed. So once the WCF DataContractJsonSerializer does it's work I have an object that will have perhaps only the ID and Description fields populated.
Attaching this to the DbContext as is will result in the description field being updated but all the other fields being set to their types default value in the database. This is because if WCF doesn't see the property specified in the JSON then it'll skip over it, meaning the property in the instance will just use the types default value as per the auto-implemented property.
So this means that I need to decide on which fields have been passed up without having access to the JSON itself. In fact it could be XML on the wire so all I can work from is this partly serialized object.
What seems most logical is to use null as the special value that means this property hasn't been serializd over. So in the constructor of the POCO model I set all the properties to null.
In the Update method I then use this serialized object as a stub. I have to walk each property and if the value isn't set to null then I set it's state to modified. As far as I can tell this is working without any side effects but I'm just not sure that this is the way to do something like this.
One limitation it does add is that the client can no longer intentionally set a property to null as that update would be lost. One way around this is to have a special int value that can be set to represent null in the database and perhaps an empty string to represent null in the database and have code in the update to look for these special values and then set the entity property to null. Far from ideal and likely to be prone to bugs.
Here is the code I currently have to process the update. I would really really appreciate advice as to a better, perhaps more obvious way of doing this.
To Summerise: How can I tell which properties on an model instance have been set by the DataContractSerializer/DataContractJsonSerializer and which are just using default values from it's constructor. Using special values is problematic as the client might want to set something to an empty string, or to 0 or -1 or indeed to null.
public T Update(T obj)
{
var entity = ds.Attach(obj);
// For each property in the model
foreach (var p in typeof(T).GetProperties())
{
// Get the value of the property
var v = p.GetValue(obj, null);
// Assume null means that the property wasn't passed from the client
if (v == null)
continue;
// Set this property on the entity to modified unless it's ID which won't change
if (p.Name != "ID")
dc.Entry(entity).Property(p.Name).IsModified = true;
}
dc.SaveChanges();
return entity;
}
UPDATE: Using Hammerstein's answer below to have self tracked models, I've updated my update function as below. Unfortunately due to my use of the Required attribute on the models for pre-save validation EF throws a wobbly when using a stub instance that contains nulls for the non modified values. You would think that EF would realise as part of it's validation that some fields are set to not modified but alas it doesn't so I'm forced to do a read and then update that. Actually this might be a good candidate for a separate question to post to try and avoid the read.
public virtual T Update(T obj)
{
var entity = ds.Find(obj.ID);
((TrackedModel)obj).Modified.ForEach(p => {
var prop = dc.Entry(entity).Property(p.PropertyName);
prop.CurrentValue = p.NewValue;
prop.IsModified = true;
});
dc.SaveChanges();
return entity;
}
My solution to this problem was a tracked change model, I created an abstract base class that had a list of strings, then for each property on my model, I called a method NotifyChanged( "MyProperty") which added the property to the list.
Because model binding will only set the fields that have been posted back, you should get an accurate list of fields that changed.
Then I loop through the list, and set the values on my entity.
Not clean, but it worked for my purpose.
Update: My solution did require me to get away from auto-properties and to hand write them. In the setter, after setting the value, I call NotifyChanged. I'm using this with MVC regular model binding, I don't think I have a working example of passing an object as JSON and deserializing. You could look at JSON.NET, controlling the serialization/deserialization I believe you can tell it to ignore default property values etc.

C# and ASP.NET custom property attributes and determining if properties changed

I am working on a project where we want to keep a history of a particular object. On save I want a method on the object that will determine if it has changed so that I can call a method to save its current state to history. E.g. I populate a form from an object the user makes changes (or possibly not) and submits the from. I want to take my original object and a copy of that object that has been updated from the form and determine if it has changed at all. Additionally I may decide at some point that certain properties don't matter (e.g. if Name changes I won't track it).
I'm thinking the easiest/most flexible way to accomplish this would be if I could give the properties I care about a custom attribute [ChangeTracked] and then I could use reflection to get a list of all properties with that attribute and loop through them comparing A.property == B.property to determine if any have changed.
Would this work? Is there a significantly better/easier way to handle this, like some sort of built in method you can add to an object to determine if the values of any properties have changed? Whatever the solution some psudo code would be appreciated. Just as a point of clarification the solution needs to determine if the value I care about has actually changed not just if it has been assigned since it was created i.e. if I set Name="bob" and it was already "bob" before my assignment this does not count as a change.
It ain't fancy, but this is the tried and true brute force method. Just add a private property to the object named IsDirty. For properties that you want to track, just add IsDirty=True to the property Set routine. For more complicated "do I care" rules, just code them into the property set.
The page button's click event can fire a Save event that writes all the values from the textboxes and dropdowns into the object properties, then calls the object Save method, which tests the IsDirty property before doing anything.
One possible method would be to add a deep copy of the object as a private property of the object when it is loaded. (One method of deep copy)
On save you can compare the copy object to your "live" object to see if any changes have occurred.

Bind an object which has a property of type List<> to a repeater

I've created two classes in business layer.
the first one is called Users with id (int), pass (string) and privileges (Privilege) properties and the second one is called Privilege and has id (int) and privilegeName (string) properties.
I've a method that returns all the users, and I use a repeater (actually I bind it to a DataList to auto create the ItemTemplate for me and then use a repeater) and it works and displays all the properties well except for my List property. it generates instead something like this System.Collections.Generic.List`1[WebApplication2.Public.BLL.Users]
I want to display it in a friendly way like "User Privileges : Privi1, Privi2" but still I want to keep the layers of my application clean and structured, for example I won't store them in a database in the same table and just store them as a text and append it.
I hope to find a simple and good solution...Thanks in advance guys =)
PS : I don't want to display the object Privilege, I want to display privilege.privilegeName
When using repeaters, there are two approaches, one is the one suggested by Bugai13: to have a custom property that displays it. This is fine for certain types of nested data.
Your other option is to just have a repeater inside a repeater, and bind it appropriately (to what would be a list assigned to your main data object, depending on how you O/R Mapper works).
You can have the code for the custom display property not in the data model, but in your presentation layer somewhere (depending on your framework/design), so it's not a "bad" thing to do that. It's up to you, with whatever "feels" best.
Just create property at your Bussiness object, and bind it:
public string PrivilegiesString
{
get
{
var sb = new StringBuilder("User Privileges : ");
foreach(var item in privileges)
{
sb.AppendFormat("{0}, ",item.privilegeName);
}
return sb.ToString();
}
}

WPF C# - ComboBox methods return object instead of string

I'm new to WPF and I'm trying to figure out how to get the current text value of the selected item in a ComboBox. I saw in this question someone suggested doing MyComboBox.SelectedItem.Text. However, SelectedItem returns object for me, so I only have options like ToString(), Equals, etc. What's going on? I'm using .NET 3.5, developing in VS 2010. The other methods I thought might be of use, like MyComboBox.SelectedValue, also return object. SelectedIndex returns int, but I want a string value. MyComboBox is of type ComboBox. I'm accessing it in a method to handle the SelectionChanged event.
Have you tried MyComboBox.Text ? That will return you the text of the currently selected item.
You can also parse the SelectItem into the type of the datasource you've set it and get the text property you want directly from the object?
ie
MyObject obj = (MyObject)MyComboBox.SelectedItem;
string text = obj.Text;
Each Item is a Object.
The Displayed Data is Object.ToString (Item.ToString)
But you can Use any other Object member, Property or method from Object.
You have added the object to Combo, then you know Object Type and can Cast it.

Categories