I'm trying to create a wrapper around multiple objects of different types. The objects are in tools that are attached to displays in order to perform inidivual tasks (like drawing a rectangle ro circle), or peforming some measurements. Each tool lives independently on the display. The user activates the tools by clicking a button where I activate every tool by settings it IsActive property in a for loop inside the button handler.
Since each tool has several properties (which can also change from tool to tool) I was thinking about a dynamic behavior that creates wrapper around a list of tools that automatically creates the properties. I was looking at the ExpandoObject and the DynamicObject, but the problem ist that I can only add properties using the dynamic keyword but I cannot set the properties from the inside by calling TrySetMember explicitly because I'm not able to setup the SetMemberBinder object.
I also want to connect the object to the PropertyGrid to visualize the properties when they have changed.
Attached you can see my currently "achievements", where I have marked the problem with comments.
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Dynamic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace WpfApp4
{
/// <summary>
/// Wrapper-Object to wrap multiple objects including their properties
/// and make the object look as if it is a single object.
/// </summary>
public sealed class MockObject : DynamicObject, INotifyPropertyChanged
{
#region Events
/// <summary>
/// INotiyPropertyChanged-Event
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises the property change event.
/// </summary>
/// <param name="propertyName_in"></param>
private void RaisePropertyChanged([CallerMemberName]string propertyName_in = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName_in));
}
#endregion
/// <summary>
/// Keeps track of properties and values.
/// </summary>
private readonly Dictionary<string, object> Properties = new Dictionary<string, object>();
/// <summary>
/// Contains the wrapped objects.
/// </summary>
[Browsable(false)]
public ObservableCollection<object> Objects
{
get;
private set;
} = new ObservableCollection<object>();
/// <summary>
/// Creates a new instance of MockObject.
/// </summary>
public MockObject()
{
Objects.CollectionChanged += Objects_CollectionChanged;
}
/// <summary>
/// Update the wrapper when objects have been added or removed.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Objects_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case System.Collections.Specialized.NotifyCollectionChangedAction.Add:
foreach (object o in e.NewItems)
{
INotifyPropertyChanged aPropertyChanged = o as INotifyPropertyChanged;
if (aPropertyChanged == null)
continue;
aPropertyChanged.PropertyChanged += APropertyChanged_PropertyChanged;
}
UpdateProperties();
break;
case System.Collections.Specialized.NotifyCollectionChangedAction.Remove:
foreach (object o in e.OldItems)
{
INotifyPropertyChanged aPropertyChanged = o as INotifyPropertyChanged;
if (aPropertyChanged == null)
continue;
aPropertyChanged.PropertyChanged -= APropertyChanged_PropertyChanged;
}
UpdateProperties();
break;
}
}
/// <summary>
/// Updates properties and values of the wrapper.
/// </summary>
private void UpdateProperties()
{
Dictionary<string, List<object>> TouchedProperties = new Dictionary<string, List<object>>();
// Retriebve all property values from all objects
foreach (object aObject in Objects.ToArray())
{
if (aObject == null)
continue;
PropertyInfo[] aProperties = aObject.GetType().GetProperties();
foreach (PropertyInfo aProperty in aProperties)
{
List<object> Values;
if (!TouchedProperties.TryGetValue(aProperty.Name, out Values))
{
Values = new List<object>();
TouchedProperties.Add(aProperty.Name, Values);
}
Values.Add(aProperty.GetValue(aObject));
}
}
// Now clear the old properties and set in the new properties.
Properties.Clear();
dynamic ThisObject = this;
// Only take those values that have an equal amount of objects in the list
foreach (var aProps in TouchedProperties.ToArray())
{
if (aProps.Value.Count != Objects.Count)
{
TouchedProperties.Remove(aProps.Key);
continue;
}
if (aProps.Value.TrueForAll(o => object.Equals(o, aProps.Value.FirstOrDefault())))
Properties[aProps.Key] = aProps.Value.FirstOrDefault();
else
Properties[aProps.Key] = null;
// Here I fail, I do not know how to create the property
ThisObject.(aProps.Key) = Properties[aProps.Key];
// What would help is to use, but I don't know how to create the binder
this.TrySetMember(/* what the binder?*/, Properties[aProps.Key]);
RaisePropertyChanged(aProps.Key);
}
}
/// <summary>
/// React on property changes.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void APropertyChanged_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
base.TrySetMember(binder, value);
if (!Properties.ContainsKey(binder.Name))
return false;
foreach (object o in Objects.ToArray())
{
o.GetType().GetProperty(binder.Name).SetValue(o, value);
}
return true;
}
}
}
Aucun commentaire:
Enregistrer un commentaire