Skip to content

Commit 10a0f6a

Browse files
maxkatz6danwalmsley
authored andcommitted
Merge pull request AvaloniaUI#8119 from AvaloniaUI/feature/ireflectabletype
Added support for IReflectableType in InpcPropertyAccessorPlugin
1 parent 81698eb commit 10a0f6a

File tree

3 files changed

+252
-7
lines changed

3 files changed

+252
-7
lines changed

src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public class InpcPropertyAccessorPlugin : IPropertyAccessorPlugin
1717
new Dictionary<(Type, string), PropertyInfo?>();
1818

1919
/// <inheritdoc/>
20-
public bool Match(object obj, string propertyName) => GetFirstPropertyWithName(obj.GetType(), propertyName) != null;
20+
public bool Match(object obj, string propertyName) => GetFirstPropertyWithName(obj, propertyName) != null;
2121

2222
/// <summary>
2323
/// Starts monitoring the value of a property on an object.
@@ -36,7 +36,7 @@ public class InpcPropertyAccessorPlugin : IPropertyAccessorPlugin
3636
if (!reference.TryGetTarget(out var instance) || instance is null)
3737
return null;
3838

39-
var p = GetFirstPropertyWithName(instance.GetType(), propertyName);
39+
var p = GetFirstPropertyWithName(instance, propertyName);
4040

4141
if (p != null)
4242
{
@@ -50,8 +50,16 @@ public class InpcPropertyAccessorPlugin : IPropertyAccessorPlugin
5050
}
5151
}
5252

53-
private PropertyInfo? GetFirstPropertyWithName(Type type, string propertyName)
53+
private const BindingFlags PropertyBindingFlags =
54+
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance;
55+
56+
private PropertyInfo? GetFirstPropertyWithName(object instance, string propertyName)
5457
{
58+
if (instance is IReflectableType reflectableType)
59+
return reflectableType.GetTypeInfo().GetProperty(propertyName, PropertyBindingFlags);
60+
61+
var type = instance.GetType();
62+
5563
var key = (type, propertyName);
5664

5765
if (!_propertyLookup.TryGetValue(key, out var propertyInfo))
@@ -66,10 +74,7 @@ public class InpcPropertyAccessorPlugin : IPropertyAccessorPlugin
6674
{
6775
PropertyInfo? found = null;
6876

69-
const BindingFlags bindingFlags =
70-
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance;
71-
72-
var properties = type.GetProperties(bindingFlags);
77+
var properties = type.GetProperties(PropertyBindingFlags);
7378

7479
foreach (PropertyInfo propertyInfo in properties)
7580
{

tests/Avalonia.Markup.UnitTests/Data/BindingTests.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,25 @@ public void Combined_OneTime_And_OneWayToSource_Bindings_Should_Release_Subscrip
617617

618618
Assert.Equal(0, source.SubscriberCount);
619619
}
620+
621+
[Fact]
622+
public void Binding_Can_Resolve_Property_From_IReflectableType_Type()
623+
{
624+
var source = new DynamicReflectableType { ["Foo"] = "foo" };
625+
var target = new TwoWayBindingTest { DataContext = source };
626+
var binding = new Binding
627+
{
628+
Path = "Foo",
629+
};
630+
631+
target.Bind(TwoWayBindingTest.TwoWayProperty, binding);
632+
633+
Assert.Equal("foo", target.TwoWay);
634+
source["Foo"] = "bar";
635+
Assert.Equal("bar", target.TwoWay);
636+
target.TwoWay = "baz";
637+
Assert.Equal("baz", source["Foo"]);
638+
}
620639

621640
private class StyledPropertyClass : AvaloniaObject
622641
{
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
using System.ComponentModel;
5+
using System.Globalization;
6+
using System.Reflection;
7+
using Moq;
8+
9+
namespace Avalonia.Markup.UnitTests.Data;
10+
11+
class DynamicReflectableType : IReflectableType, INotifyPropertyChanged, IEnumerable<KeyValuePair<string, object>>
12+
{
13+
private Dictionary<string, object> _dic = new();
14+
15+
public TypeInfo GetTypeInfo()
16+
{
17+
return new FakeTypeInfo();
18+
}
19+
20+
public void Add(string key, object value)
21+
{
22+
_dic.Add(key, value);
23+
24+
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(key));
25+
}
26+
27+
public object this[string key]
28+
{
29+
get => _dic[key];
30+
set
31+
{
32+
_dic[key] = value;
33+
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(key));
34+
}
35+
}
36+
37+
public event PropertyChangedEventHandler PropertyChanged;
38+
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
39+
{
40+
return _dic.GetEnumerator();
41+
}
42+
43+
IEnumerator IEnumerable.GetEnumerator()
44+
{
45+
return ((IEnumerable)_dic).GetEnumerator();
46+
}
47+
48+
49+
class FakeTypeInfo : TypeInfo
50+
{
51+
protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types,
52+
ParameterModifier[] modifiers)
53+
{
54+
var propInfo = new Mock<PropertyInfo>();
55+
propInfo.SetupGet(x => x.Name).Returns(name);
56+
propInfo.SetupGet(x => x.PropertyType).Returns(typeof(object));
57+
propInfo.SetupGet(x => x.CanWrite).Returns(true);
58+
propInfo.Setup(x => x.GetValue(It.IsAny<object>(), It.IsAny<object[]>()))
59+
.Returns((object target, object [] _) => ((DynamicReflectableType)target)._dic.GetValueOrDefault(name));
60+
propInfo.Setup(x => x.SetValue(It.IsAny<object>(), It.IsAny<object>(), It.IsAny<object[]>()))
61+
.Callback((object target, object value, object [] _) =>
62+
{
63+
((DynamicReflectableType)target)._dic[name] = value;
64+
});
65+
return propInfo.Object;
66+
}
67+
68+
#region NotSupported
69+
70+
71+
public override object[] GetCustomAttributes(bool inherit)
72+
{
73+
throw new NotSupportedException();
74+
}
75+
76+
public override object[] GetCustomAttributes(Type attributeType, bool inherit)
77+
{
78+
throw new NotSupportedException();
79+
}
80+
81+
public override bool IsDefined(Type attributeType, bool inherit)
82+
{
83+
throw new NotSupportedException();
84+
}
85+
86+
public override Module Module { get; }
87+
public override string Namespace { get; }
88+
public override string Name { get; }
89+
protected override TypeAttributes GetAttributeFlagsImpl()
90+
{
91+
throw new NotSupportedException();
92+
}
93+
94+
protected override ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention,
95+
Type[] types, ParameterModifier[] modifiers)
96+
{
97+
throw new NotSupportedException();
98+
}
99+
100+
public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr)
101+
{
102+
throw new NotSupportedException();
103+
}
104+
105+
public override Type GetElementType()
106+
{
107+
throw new NotSupportedException();
108+
}
109+
110+
public override EventInfo GetEvent(string name, BindingFlags bindingAttr)
111+
{
112+
throw new NotSupportedException();
113+
}
114+
115+
public override EventInfo[] GetEvents(BindingFlags bindingAttr)
116+
{
117+
throw new NotSupportedException();
118+
}
119+
120+
public override FieldInfo GetField(string name, BindingFlags bindingAttr)
121+
{
122+
throw new NotSupportedException();
123+
}
124+
125+
public override FieldInfo[] GetFields(BindingFlags bindingAttr)
126+
{
127+
throw new NotSupportedException();
128+
}
129+
130+
public override MemberInfo[] GetMembers(BindingFlags bindingAttr)
131+
{
132+
throw new NotSupportedException();
133+
}
134+
135+
protected override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention,
136+
Type[] types, ParameterModifier[] modifiers)
137+
{
138+
throw new NotSupportedException();
139+
}
140+
141+
public override MethodInfo[] GetMethods(BindingFlags bindingAttr)
142+
{
143+
throw new NotSupportedException();
144+
}
145+
146+
public override PropertyInfo[] GetProperties(BindingFlags bindingAttr)
147+
{
148+
throw new NotSupportedException();
149+
}
150+
151+
public override object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args,
152+
ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters)
153+
{
154+
throw new NotSupportedException();
155+
}
156+
157+
public override Type UnderlyingSystemType { get; }
158+
159+
protected override bool IsArrayImpl()
160+
{
161+
throw new NotSupportedException();
162+
}
163+
164+
protected override bool IsByRefImpl()
165+
{
166+
throw new NotSupportedException();
167+
}
168+
169+
protected override bool IsCOMObjectImpl()
170+
{
171+
throw new NotSupportedException();
172+
}
173+
174+
protected override bool IsPointerImpl()
175+
{
176+
throw new NotSupportedException();
177+
}
178+
179+
protected override bool IsPrimitiveImpl()
180+
{
181+
throw new NotSupportedException();
182+
}
183+
184+
public override Assembly Assembly { get; }
185+
public override string AssemblyQualifiedName { get; }
186+
public override Type BaseType { get; }
187+
public override string FullName { get; }
188+
public override Guid GUID { get; }
189+
190+
191+
192+
protected override bool HasElementTypeImpl()
193+
{
194+
throw new NotSupportedException();
195+
}
196+
197+
public override Type GetNestedType(string name, BindingFlags bindingAttr)
198+
{
199+
throw new NotSupportedException();
200+
}
201+
202+
public override Type[] GetNestedTypes(BindingFlags bindingAttr)
203+
{
204+
throw new NotSupportedException();
205+
}
206+
207+
public override Type GetInterface(string name, bool ignoreCase)
208+
{
209+
throw new NotSupportedException();
210+
}
211+
212+
public override Type[] GetInterfaces()
213+
{
214+
throw new NotSupportedException();
215+
}
216+
217+
218+
#endregion
219+
220+
}
221+
}

0 commit comments

Comments
 (0)