What is different when accessing BindingContext[dataSource] vs BindingContext[dataSource, dataMember]? - c#

We have run into a problem where
We have two instances of the same window in an MDI workspace bound to two separate object models.
The object models have their .Equals and .GetHashCode methods overwritten to be considered equal.
Calling .EndCurrentEdit() on window 2 is triggering a binding update for Window 1
Both windows are setup to use separate a BindingContext
We have discovered the problem has to do with calling
((PropertyManager)ctrl.BindingContext[dataSource]).EndCurrentEdit();
If we change that to
((PropertyManager)ctrl.BindingContext[dataSource, dataMember]).EndCurrentEdit();
It works correctly. It also works correctly if we remove our .Equals and .GetHashCode overrides so the two object models are no longer considered equal.
That doesn't make sense to me because the windows are the same, so the dataMember property would be the same too.
From this link, I believe the definition of these calls is:
public BindingManagerBase this[object dataSource] {
get {
return this[dataSource, ""];
}
}
public BindingManagerBase this[object dataSource, string dataMember] {
get {
return EnsureListManager(dataSource, dataMember);
}
internal BindingManagerBase EnsureListManager(object dataSource, string dataMember) {
BindingManagerBase bindingManagerBase = null;
if (dataMember == null)
dataMember = "";
// Check whether data source wants to provide its own binding managers
// (but fall through to old logic if it fails to provide us with one)
//
if (dataSource is ICurrencyManagerProvider) {
bindingManagerBase = (dataSource as ICurrencyManagerProvider).GetRelatedCurrencyManager(dataMember);
if (bindingManagerBase != null) {
return bindingManagerBase;
}
}
// Check for previously created binding manager
//
HashKey key = GetKey(dataSource, dataMember);
WeakReference wRef;
wRef = listManagers[key] as WeakReference;
if (wRef != null)
bindingManagerBase = (BindingManagerBase) wRef.Target;
if (bindingManagerBase != null) {
return bindingManagerBase;
}
if (dataMember.Length == 0) {
// No data member specified, so create binding manager directly on the data source
//
if (dataSource is IList || dataSource is IListSource) {
// IListSource so we can bind the dataGrid to a table and a dataSet
bindingManagerBase = new CurrencyManager(dataSource);
}
else {
// Otherwise assume simple property binding
bindingManagerBase = new PropertyManager(dataSource);
}
}
else {
// Data member specified, so get data source's binding manager, and hook a 'related' binding manager to it
//
int lastDot = dataMember.LastIndexOf(".");
string dataPath = (lastDot == -1) ? "" : dataMember.Substring(0, lastDot);
string dataField = dataMember.Substring(lastDot + 1);
BindingManagerBase formerManager = EnsureListManager(dataSource, dataPath);
PropertyDescriptor prop = formerManager.GetItemProperties().Find(dataField, true);
if (prop == null)
throw new ArgumentException(SR.GetString(SR.RelatedListManagerChild, dataField));
if (typeof(IList).IsAssignableFrom(prop.PropertyType))
bindingManagerBase = new RelatedCurrencyManager(formerManager, dataField);
else
bindingManagerBase = new RelatedPropertyManager(formerManager, dataField);
}
My dataSource is not an ICurrencyManagerProvider
What is the difference between these two calls, and why does accessing the PropertyManager by only the dataSource result in the bindings for another window with a separate BindingContext being updated?

You don't state this explicitly, so in case you haven't noticed it is the collection look up that is not working as you expect, because of the equal override.
BindingContext[datasource] is a lookup against the collection using datasource as the key.
BindingContext[datasource, datamember] is a lookup against the collection using a composite key.
It is clear from the code that BindingContext is maintaining two separate collections. One a collection on a datasource key and the other a collection based on a composite key.
Obviously your override of equal will twice place similar values to datasource in the BindingContext[datasource] collection, but will result in one collection entry, because the values/keys are the same. Whereas, it will place two entries in BindingContext[datasource, datamember].
If you inspect both collections and can get the counts you'll see that the later collection has more entries.
You have to remember that you have two separate objects that evaluate to equal, not two references to the same object. This is the crux of the problem.
It would appear that when adding the entries to the second collection (BindingContext[datasource, datamember]) datamember evaluates to unique.

When you accessing BindingContext[dataSource], you actually accessing BindingContext[dataSource, ""]. So, there are no difference except HashCode, which uses both DataSource and DataMember values for calculation, that can be seen in your link.
public override int GetHashCode() {
return dataSourceHashCode * dataMember.GetHashCode();
}
The problem in separate BindingContext objects can be that they are not filled correctly.

Related

Is a more generic solution possible?

I think what I want to do is not possible.
However, I would like to make sure that it is indeed not possible.
I use Audit.Net as a framework to add auditing to a system that has already been completed.
There are many different objects that may be sent for auditing. I have to get hold of the properties of those objects and send them to the database. In some cases, I would want the old values and the new values, and therefore I use the Target property of AuditEvent, otherwise if I just need the new values I use the CustomField property.
Is there any way to make the following more generic, so that I don't have to repeat these lines for each type of object like SimpleResult, LeaveRequest, Incident etc?
There is unfortunately no commonality between the objects being audited.
SimpleResult objOld = JsonConvert.DeserializeObject<SimpleResult>(auditEvent.Target.SerializedOld.ToString());
SimpleResult objNew = JsonConvert.DeserializeObject<SimpleResult>(auditEvent.Target.SerializedNew.ToString());
if (auditEvent.Target.Type.ToString() == "SimpleResult")
{
InsertTargetObjectFields<SimpleResult>(objOld, objNew, auditControlTableID, auditEvent);
}
Here is where I get hold of the properties and send them off to the database:
public void InsertTargetObjectFields<T>(T objOld, T objNew, int? auditControlTableID, AuditEvent auditEvent)
{
using (ESSDataContext ess_context = new ESSDataContext())
{
try
{
foreach (var property in objOld.GetType().GetProperties().Where(property => !property.GetGetMethod().GetParameters().Any()))
{
//Check for null values and get hold of oldValue and newValue
var sqlResult = ess_context.InsertAuditTable(resourceTag, dbObjectName, username, property.Name, oldValue,
newValue, auditEvent.StartDate, auditControlTableID.ToString(),
auditEvent.Environment.CallingMethodName);
}
}
}
}
I've tried using dynamic, but then I don't get the properties correctly.

Adding items to a WPF DataGrid bound to an ObservableCollection with a custom mutator in MVVM C#

This will be easiest if I first explain my code structure, then try to ask my question.
I have a base class containing some simple properties. We will call this BaseClass. I then have several other classes that extend BaseClass. Let's call these SubClass1, Subclass2, and SubClass3. Now I have this view model, and it contains this private member:
private ObservableCollection<BaseClass> objs = new ObservableCollection<BaseClass>();
The initializer of the view model contains something like this:
objs.Add(new SubClass1(attribute1, attribute2));
objs.Add(new SubClass3(attribute1, attribute2));
objs.Add(new SubClass1(attribute1, attribute2));
objs.Add(new SubClass2(attribute1, attribute2));
Now, in this case, I have 3 separate datagrids. I want all three to draw from objs, but I want one to show only objects of type SubClass1, one to show only objects of type SubClass2,and the last to show only objects of type SubClass3. I have successfully achieved this by creating a property for each as follows:
public ObservableCollection<SubClass1> SubClass1Objs
{
get
{
ObservableCollection<SubClass1> subObjs = new ObseleCollection<SubClass1>();
if (objs != null)
foreach (BaseClass obj in objs)
if(obj.GetType() == typeof(SubClass1))
subObjs .Add((SubClass1)obj);
return subObjs ;
}
}
The other 2 are identical. I bind the ItemSource of each datagrid to their property. This all works.
The user can currently edit any item in the data grid and the changes are reflected in objs. However, if the user tries to add an item to the datagrid, the items are not added to objs, and rightly so; I have no mutators(setters) for my properties. This is where my issues is. I am having trouble coming up with what the mutators would look like to add a new object to the collection. Does any one have any ideas? Thanks!
Thanks to #user49104 I was able to figure it out. I will post the answer here for anyone else who needs it.
I changed the properties of my SubClass1, SubClass2, and SubClass3 Collections to have normal more normal accessors and mutators and created a private member for each:
private ObservableCollection<SubClass1> _SubClass1Objs ;
public ObservableCollection<SubClass1> SubClass1Objs {
get { if (_SubClass1Objs== null) _SubClass1Objs = new ObservableCollection<SubClass1>(); return _SubClass1Objs; }
set { if (_SubClass1Objs!= value) { _SubClass1Objs= value; RaisePropertyChanged("SubClass1Objs"); } }
}
In the initializer of my view model, set the SubClass data :
SubClass1Objs= new ObservableCollection<SubClass1>();
if (objs != null)
foreach (BaseClass obj in objs.Where(c => c.GetType() == typeof(SubClass1)))
SubClass1Objs.Add((SubClass1)card);;
SubClass1Objs.CollectionChanged += SubClass1Objs_CollectionChanged;
Then lastly, in the CollectionChanged event, I check through to make sure no objects have been added or deleted and fix up objs:
// Check if a card was added
foreach (SubClass1 obj in SubClass1Objs)
if (!objs.Contains(obj))
objs.Add(obj);
// Check if a card has been deleted
for (int i = 0; i < objs.Where(c => c.GetType() == typeof(SubClass1)).Count(); ++i)
{
BaseClass obj = objs.Where(c => c.GetType() == typeof(SubClass1)).ElementAt(i);
if (!SubClass1Objs.Contains(obj))
objs.Remove(obj);
}

Get control from property name in viewmodel using Caliburn Micro

I want to get a reference to the TextBox bound to a given property name.
I would like to do this without changing the view.
Is there a proper way to do this using Caliburn Micro?
If not, what is a "good enough" way?
public class MweViewModel : PropertyChangedBase
{
public MweViewModel() : base()
{
PropertyChanged += (object sender, PropertyChangedEventArgs e) =>
{
// Find control (i.e. TextBox) bound to property with name e.PropertyName
TextBox textBox = ...
};
}
}
I'm not necessarily sure this is the most sensible approach (it's not something I've tried to do myself), looking at the Documentation, there's the mention of a ViewModelBinder class that's responsible for fixing up the various bindings of properties, methods, etc. to their respective ViewModels.
The BindProperties function on the ViewModelBinder is responsible for resolving the bindings between your properties, and the UI elements to which they're eventually bound. You could, define your own function based on the existing code, which kept track of all the bindings being established, so you would have a record of them you could use elsewhere in your program.
Using the existing code would give you something like this:
ViewModelBinder.BindProperties = (namedElements, viewModelType) =>
{
var unmatchedElements = new List<FrameworkElement>();
foreach (var element in namedElements)
{
var cleanName = element.Name.Trim('_');
var parts = cleanName.Split(new[] { '_' }, StringSplitOptions.RemoveEmptyEntries);
var property = viewModelType.GetPropertyCaseInsensitive(parts[0]);
var interpretedViewModelType = viewModelType;
for (int i = 1; i < parts.Length && property != null; i++)
{
interpretedViewModelType = property.PropertyType;
property = interpretedViewModelType.GetPropertyCaseInsensitive(parts[i]);
}
if (property == null)
{
unmatchedElements.Add(element);
// Log.Info("Binding Convention Not Applied: Element {0} did not match a property.", element.Name);
continue;
}
var convention = ConventionManager.GetElementConvention(element.GetType());
if (convention == null)
{
unmatchedElements.Add(element);
// Log.Warn("Binding Convention Not Applied: No conventions configured for {0}.", element.GetType());
continue;
}
var applied = convention.ApplyBinding(
interpretedViewModelType,
cleanName.Replace('_', '.'),
property,
element,
convention
);
if (applied)
{
// Log.Info("Binding Convention Applied: Element {0}.", element.Name);
}
else
{
// Log.Info("Binding Convention Not Applied: Element {0} has existing binding.", element.Name);
unmatchedElements.Add(element);
}
}
return unmatchedElements;
};
At the point when the binding is being added (when applied is set), you have all the information you require. You could then store specific bindings (e.g those relating to a TextBox).
You might use something like a static dictionary (there may be something far more appropriate depending on your requirement):
ViewModel Type Bound Property List of Bound elements
| | |
| | |
Dictionary<Type, Dictionary<PropertyInfo, List<FrameworkElement>>>
You would have to be careful about null/sanity checks.
There are a few other solutions that use helper methods to grab the bound properties/controls, although they often have to traverse the visual tree, this way, you're doing it at the point the binding is actually created.

Copy ONLY populated values from one object that are not already populated in another object of the same type

I have a requirement to copy ONLY populated values from one object that are not already populated in another object of the same type.
For example we are passed an object, it is only partially instantiated with data, we read the database to get a fully instantiated version of the object – however this may not have changes by the application committed to the database yet – hence we need to move any values from the database version into the passed in version of the object – without overwriting any values that may already exist in the passed in object (as these are the most up to date values).
The code below suggested by Adam Robinson in another post (see below very useful – thanks!) is a good starting point. However I need to extend this – as I only want to copy over values that are NOT already populated on the target object (i.e. need to check the destProperty is not null). However as an added complication, there are internal complex types declared within the object passed in, this code copies the high level sub groups over without going into the individual properties of the sub groups (i.e. any variables declared with the Root cdt I can try and check for null, but all the fields in sub cdts are simply copied over without going through the individual fields).
Any help would be greatly appreciated.
public static void CopyPropertyValues(object source, object destination)
{
var destProperties = destination.GetType().GetProperties();
foreach (var sourceProperty in source.GetType().GetProperties())
{
foreach (var destProperty in destProperties)
{
if (destProperty.Name == sourceProperty.Name &&
destProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType))
{
destProperty.SetValue(destination, sourceProperty.GetValue(
source, new object[] { }), new object[] { });
break;
}
}
}
}
First you need to determine what populated means in your book.
Once you determine that, write you own version of the following IsDefaultValue method.
What I have written will answer with true in the following manner:
if it's a bool then it needs to have the false value
if it's an int then it needs to be 0
if it's any class then needs to be null
etc
So here's my version of the method:
public static bool IsDefaultValue(object #object) {
if (null == #object)
return true;
else if (#object.GetType().IsValueType) {
var isDefault = Activator.CreateInstance(#object.GetType()).Equals(#object);
return isDefault;
} else
return false;
}
Then, presuming that you're interested only in the non indexer properties, that you have no hierarchies and that you will always call this method with objects of the same type,
you could just filter out those properties which are not default in the source but are default in the destination.
Then you could traverse the instance graph recursively for those cases when you have a non-default value in your destination properties.
Please not that what I have written here is just a demonstration of how you might be able to accomplish your task. There are intricate details in your particular scenario which you need to address yourself as they are not obvious from you question.
For instance, I assumed it would be a good idea to add a stop condition for the recursive traversal (the remainingDepth parameter)
public static void CopyPropertyValues(object source, object destination, int remainingDepth = 3) {
// we've reached the farthest point we're allowed to go to
// anything beyond this point won't be affected by this method
if (remainingDepth == 0)
return;
// just a check to make sure the following lines won't backfire
if ((null == source) || (null == destination))
throw new ArgumentNullException();
// we'll need to also check that the 2 objects are of the same type
var type = source.GetType();
if (destination.GetType() != type)
throw new ArgumentException("The two objects should be of the same type");
var properties = type.GetProperties()
// just filter out the properties which are indexers (if any)
// and also those properties which are read or write only
.Where(property => (property.GetIndexParameters().Length == 0) &&
property.CanRead && property.CanWrite);
foreach (var property in properties) {
var sourceValue = property.GetValue(source, null);
var destValue = property.GetValue(destination, null);
if (!IsDefaultValue(sourceValue))
if (IsDefaultValue(destValue))
property.SetValue(destination, sourceValue, null);
else
if (sourceValue.GetType() == destValue.GetType())
CopyPropertyValues(sourceValue, destValue, remainingDepth - 1);
}
}
Please note that property enumeration is needed just once since the objects (as you said it yourself in the comments section) are of the same type.
Also please beware of Reflection when performance is of importance.

Passing properties as parameters to be Got and Set

Well, I need to repeat same code for many properties.
I've seen examples taking Action delegates, but they don't fit quite well here.
I want something like this: (see explanation below)
Dictionary<Property, object> PropertyCorrectValues;
public bool CheckValue(Property P) { return P.Value == PropertyCorrectValues[P]; }
public void DoCorrection(Property P) { P.Value = PropertyCorrectValues[P]; }
.
I want to have a dictionary containing many properties and their respective "correct" values. (I know it's not well declared, but that's the idea). Properties are not necessarely inside my class, some of them are in objects of different assemblies.
A method bool CheckValue(Property). This method must access the actual value of the property and compare to the correct value.
And a method a void DoCorrection(Property). This one sets the property value to the correct value.
Remember I have many of those properties, I wouldn't like to call the methods by hand for each property. I'd rather iterate through the dicionary in a foreach statement.
So, the main question is in the title.
I've tried the by ref, but properties don't accept that.
Am I obligated to use reflection??? Or is there another option (if I need, reflection answer will be accepted as well).
Is there anyway I can make a dictionary with pointers in C#? Or some kind of assignment that changes the value of variable's target instead of changing the target to another value?
Thanks for the help.
You can do this using reflection. Get a list of the properties on the object of interest with typeof(Foo).GetProperties(). Your PropertyCorrectValues property can have type IDictionary<PropertyInfo, object>. Then use the GetValue and SetValue methods on PropertyInfo to perform the desired operations:
public bool CheckProperty(object myObjectToBeChecked, PropertyInfo p)
{
return p.GetValue(myObjectToBeChecked, null).Equals(PropertyCorrectValues[p]);
}
public void DoCorrection(object myObjectToBeCorrected, PropertyInfo p)
{
p.SetValue(myObjectToBeCorrected, PropertyCorrectValues[p]);
}
In addition to Ben's code I'd like to contribute the following code fragment:
Dictionary<string,object> PropertyCorrectValues = new Dictionary<string,object>();
PropertyCorrectValues["UserName"] = "Pete"; // propertyName
PropertyCorrectValues["SomeClass.AccountData"] = "XYZ"; // className.propertyName
public void CheckAndCorrectProperties(object obj) {
if (obj == null) { return; }
// find all properties for given object that need to be checked
var checkableProps = from props
in obj.GetType().GetProperties()
from corr in PropertyCorrectValues
where (corr.Key.Contains(".") == false && props.Name == corr.Key) // propertyName
|| (corr.Key.Contains(".") == true && corr.Key.StartsWith(props.DeclaringType.Name + ".") && corr.Key.EndsWith("." + props.Name)) // className.propertyName
select new { Property = props, Key = corr.Key };
foreach (var pInfo in checkableProps) {
object propValue = pInfo.Property.GetValue(obj, null);
object expectedValue = PropertyCorrectValues[pInfo.Key];
// checking for equal value
if (((propValue == null) && (expectedValue != null)) || (propValue.Equals(expectedValue) == false)) {
// setting value
pInfo.Property.SetValue(obj, expectedValue, null);
}
}
}
When using this "automatic" value correction you might also consider:
You cannot create a PropertyInfo object just by knowing the property name and independently of the declaring class; that's why I chose string for the key.
When using the same property name in different classes then you might need to change the code that is doing the actual assignment because the type between the correct value and the property type might differ.
Using the same property name in different classes will always perform the same check (see point above), so you might need a syntax for property names to restrict it to a specific class (simple dot notation, doesn't work for namespaces or inner classes, but might be extended to do so)
If needed you can replace the "check" and "assign" part with separate method calls, but it might be done inside the code block as stated in my example code.

Categories

Resources