-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
DependencyObjectObservableForProperty.cs
159 lines (132 loc) · 5.28 KB
/
DependencyObjectObservableForProperty.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// Copyright (c) 2023 .NET Foundation and Contributors. All rights reserved.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for full license information.
#if WINUI_TARGET
using System;
using System.Globalization;
using System.Linq.Expressions;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reflection;
using Microsoft.UI.Xaml;
using Splat;
namespace ReactiveUI
{
/// <summary>
/// Creates a observable for a property if available that is based on a DependencyProperty.
/// </summary>
public class DependencyObjectObservableForProperty : ICreatesObservableForProperty
{
/// <inheritdoc/>
public int GetAffinityForObject(Type type, string propertyName, bool beforeChanged = false)
{
if (!typeof(DependencyObject).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()))
{
return 0;
}
if (GetDependencyPropertyFetcher(type, propertyName) is null)
{
return 0;
}
return 6;
}
/// <inheritdoc/>
public IObservable<IObservedChange<object, object?>> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged = false, bool suppressWarnings = false)
{
if (sender is null)
{
throw new ArgumentNullException(nameof(sender));
}
if (sender is not DependencyObject depSender)
{
throw new ArgumentException("The sender must be a DependencyObject", nameof(sender));
}
var type = sender.GetType();
if (beforeChanged)
{
this.Log().Warn(
CultureInfo.InvariantCulture,
"Tried to bind DO {0}.{1}, but DPs can't do beforeChanged. Binding as POCO object",
type.FullName,
propertyName);
var ret = new POCOObservableForProperty();
return ret.GetNotificationForProperty(sender, expression, propertyName, beforeChanged, suppressWarnings);
}
var dpFetcher = GetDependencyPropertyFetcher(type, propertyName);
if (dpFetcher is null)
{
this.Log().Warn(
CultureInfo.InvariantCulture,
"Tried to bind DO {0}.{1}, but DP doesn't exist. Binding as POCO object",
type.FullName,
propertyName);
var ret = new POCOObservableForProperty();
return ret.GetNotificationForProperty(sender, expression, propertyName, beforeChanged, suppressWarnings);
}
return Observable.Create<IObservedChange<object, object?>>(subj =>
{
var handler = new DependencyPropertyChangedCallback((_, _) =>
subj.OnNext(new ObservedChange<object, object?>(sender, expression, default)));
var dependencyProperty = dpFetcher();
var token = depSender.RegisterPropertyChangedCallback(dependencyProperty, handler);
return Disposable.Create(() => depSender.UnregisterPropertyChangedCallback(dependencyProperty, token));
});
}
private static PropertyInfo? ActuallyGetProperty(TypeInfo typeInfo, string propertyName)
{
var current = typeInfo;
while (current is not null)
{
var ret = current.GetDeclaredProperty(propertyName);
if (ret?.IsStatic() == true)
{
return ret;
}
current = current.BaseType?.GetTypeInfo();
}
return null;
}
private static FieldInfo? ActuallyGetField(TypeInfo typeInfo, string propertyName)
{
var current = typeInfo;
while (current is not null)
{
var ret = current.GetDeclaredField(propertyName);
if (ret?.IsStatic == true)
{
return ret;
}
current = current.BaseType?.GetTypeInfo();
}
return null;
}
private static Func<DependencyProperty>? GetDependencyPropertyFetcher(Type type, string propertyName)
{
var typeInfo = type.GetTypeInfo();
// Look for the DependencyProperty attached to this property name
var pi = ActuallyGetProperty(typeInfo, propertyName + "Property");
if (pi is not null)
{
var value = pi.GetValue(null);
if (value is null)
{
return null;
}
return () => (DependencyProperty)value;
}
var fi = ActuallyGetField(typeInfo, propertyName + "Property");
if (fi is not null)
{
var value = fi.GetValue(null);
if (value is null)
{
return null;
}
return () => (DependencyProperty)value;
}
return null;
}
}
}
#endif