Hello everyone,
I am able to succesfully bind an Eto Control to a regular ViewModel, with a known set of properties, using
Control.Bind<T>(controlProperty, ViewModel, viewmodelProperty, DualBindingMode.TwoWay);
However, I am dealing with a scenario where the number of properties in the viewmodel varies based on a selected object. The solution to this was to implement the ViewModel as a dynamic object which implements ICustomTypeDescriptor
and INotifyPropertyChanged
, basically acting as a dictionary which raises the OnPropertyChanged event whenever a key is modified. The problem with this is that Eto Control binds don’t seem to work with it. The code for the dynamic object viewmodel is available below.
Am I missing something here? Is it possible to bind to a dynamic object? Any help is appreciated, thank you.
public class DynamicViewModel : DynamicObject, ICustomTypeDescriptor, INotifyPropertyChanged
{
private readonly Object thisLock = new Object();
public event PropertyChangedEventHandler PropertyChanged;
private Dictionary<string, object> dictionary = new Dictionary<string, object>();
ISynchronizeInvoke syncronzeInvoke;
public DynamicViewModel(ISynchronizeInvoke value = null)
{
syncronzeInvoke = value;
}
public object this[string name]
{
get
{
lock (thisLock) { return dictionary[name]; }
}
set
{
object oldValue = null;
lock (thisLock)
{
if (dictionary.ContainsKey(name))
oldValue = dictionary[name];
dictionary[name] = value;
}
if (oldValue != value)
OnPropertyChanged(name);
}
}
private void OnPropertyChanged(string name)
{
var handler = PropertyChanged;
if (handler != null)
{
if (syncronzeInvoke != null && syncronzeInvoke.InvokeRequired)
syncronzeInvoke.Invoke(handler, new object[] { this, new PropertyChangedEventArgs(name) });
else
handler(this, new PropertyChangedEventArgs(name));
}
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = this[binder.Name];
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
this[binder.Name] = value;
return true;
}
public string GetComponentName()
{
return TypeDescriptor.GetComponentName(this, true);
}
public EventDescriptor GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(this, true);
}
public string GetClassName()
{
return TypeDescriptor.GetClassName(this, true);
}
public EventDescriptorCollection GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(this, attributes, true);
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
{
return TypeDescriptor.GetEvents(this, true);
}
public TypeConverter GetConverter()
{
return TypeDescriptor.GetConverter(this, true);
}
public object GetPropertyOwner(PropertyDescriptor pd)
{
return dictionary;
}
public AttributeCollection GetAttributes()
{
return TypeDescriptor.GetAttributes(this, true);
}
public object GetEditor(Type editorBaseType)
{
return TypeDescriptor.GetEditor(this, editorBaseType, true);
}
public PropertyDescriptor GetDefaultProperty()
{
return null;
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
{
return this.GetProperties(new Attribute[] { });
}
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
var properties = new List<MyCustomPropertyDescriptor>();
foreach (var e in dictionary)
properties.Add(new MyCustomPropertyDescriptor(e.Key, (e.Value?.GetType()) ?? typeof(object)));
return new PropertyDescriptorCollection(properties.ToArray());
}
public void ResetPropertyChangedEvent()
{
PropertyChanged = null;
}
}
public class MyCustomPropertyDescriptor : PropertyDescriptor
{
Type type;
string key;
public MyCustomPropertyDescriptor(string key, Type type)
: base(key, null)
{
this.type = type;
this.key = key;
}
void PropertyChanged(object sender, EventArgs e)
{
OnValueChanged(sender, e);
}
public override void AddValueChanged(object component, EventHandler handler)
{
base.AddValueChanged(component, handler);
((INotifyPropertyChanged)component).PropertyChanged += PropertyChanged;
}
public override void RemoveValueChanged(object component, EventHandler handler)
{
base.RemoveValueChanged(component, handler);
((INotifyPropertyChanged)component).PropertyChanged -= PropertyChanged;
}
public override Type PropertyType
{
get { return type; }
}
public override void SetValue(object component, object value)
{
((DynamicViewModel)component)[key] = value;
}
public override object GetValue(object component)
{
return ((DynamicViewModel)component)[key];
}
public override bool IsReadOnly
{
get { return false; }
}
public override Type ComponentType
{
get { return null; }
}
public override bool CanResetValue(object component)
{
return false;
}
public override void ResetValue(object component)
{
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
}
public sealed class VMSingleton : DynamicViewModel
{
private VMSingleton() { }
private static VMSingleton _instance;
public static VMSingleton GetInstance()
{
if (_instance == null)
{
_instance = new VMSingleton();
}
return _instance;
}
public static void SetNull()
{
_instance = null;
}
}