-
Notifications
You must be signed in to change notification settings - Fork 1.8k
/
Copy pathAppThemeBinding.cs
170 lines (150 loc) · 4.06 KB
/
AppThemeBinding.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
160
161
162
163
164
165
166
167
168
169
170
#nullable disable
using System;
using Microsoft.Maui.ApplicationModel;
using Microsoft.Maui.Controls.Internals;
using Microsoft.Maui.Controls.Xaml.Diagnostics;
namespace Microsoft.Maui.Controls
{
class AppThemeBinding : BindingBase
{
WeakReference<BindableObject> _weakTarget;
BindableProperty _targetProperty;
bool _attached;
SetterSpecificity specificity;
internal override BindingBase Clone()
{
var clone = new AppThemeBinding
{
Light = Light,
_isLightSet = _isLightSet,
Dark = Dark,
_isDarkSet = _isDarkSet,
Default = Default
};
if (DebuggerHelper.DebuggerIsAttached && VisualDiagnostics.GetSourceInfo(this) is SourceInfo info)
VisualDiagnostics.RegisterSourceInfo(clone, info.SourceUri, info.LineNumber, info.LinePosition);
return clone;
}
internal override void Apply(bool fromTarget)
{
base.Apply(fromTarget);
ApplyCore();
SetAttached(true);
}
internal override void Apply(object context, BindableObject bindObj, BindableProperty targetProperty, bool fromBindingContextChanged, SetterSpecificity specificity)
{
_weakTarget = new WeakReference<BindableObject>(bindObj);
_targetProperty = targetProperty;
base.Apply(context, bindObj, targetProperty, fromBindingContextChanged, specificity);
this.specificity = specificity;
ApplyCore(false);
SetAttached(true);
}
internal override void Unapply(bool fromBindingContextChanged = false)
{
SetAttached(false);
base.Unapply(fromBindingContextChanged);
_weakTarget = null;
_targetProperty = null;
}
void OnRequestedThemeChanged(object sender, AppThemeChangedEventArgs e)
=> ApplyCore(true);
void ApplyCore(bool dispatch = false)
{
if (_weakTarget == null || !_weakTarget.TryGetTarget(out var target))
{
SetAttached(false);
return;
}
if (dispatch)
target.Dispatcher.DispatchIfRequired(Set);
else
Set();
void Set()
{
var value = GetValue();
if (value is DynamicResource dynamicResource)
target.SetDynamicResource(_targetProperty, dynamicResource.Key, specificity);
else
{
if (!BindingExpression.TryConvert(ref value, _targetProperty, _targetProperty.ReturnType, true))
{
BindingDiagnostics.SendBindingFailure(this, null, target, _targetProperty, "AppThemeBinding", BindingExpression.CannotConvertTypeErrorMessage, value, _targetProperty.ReturnType);
return;
}
target.SetValueCore(_targetProperty, value, Internals.SetValueFlags.ClearDynamicResource, BindableObject.SetValuePrivateFlags.Default | BindableObject.SetValuePrivateFlags.Converted, specificity);
}
};
}
object _light;
object _dark;
bool _isLightSet;
bool _isDarkSet;
public object Light
{
get => _light;
set
{
_light = value;
_isLightSet = true;
}
}
public object Dark
{
get => _dark;
set
{
_dark = value;
_isDarkSet = true;
}
}
public object Default { get; set; }
// Ideally this will get reworked to not use `Application.Current` at all
// https://github.com/dotnet/maui/issues/8713
// But I'm going with a simple nudge for now so that we can get our
// device tests back to a working state and address issues
// of the more crashing variety
object GetValue()
{
Application app;
if (_weakTarget?.TryGetTarget(out var target) == true &&
target is VisualElement ve &&
ve?.Window?.Parent is Application a)
{
app = a;
}
else
{
app = Application.Current;
}
AppTheme appTheme;
if (app == null)
appTheme = AppInfo.RequestedTheme;
else
appTheme = app.RequestedTheme;
return appTheme switch
{
AppTheme.Dark => _isDarkSet ? Dark : Default,
_ => _isLightSet ? Light : Default,
};
}
void SetAttached(bool value)
{
var app = Application.Current;
if (app != null && _attached != value)
{
if (value)
{
// Going from false -> true
app.RequestedThemeChanged += OnRequestedThemeChanged;
}
else
{
// Going from true -> false
app.RequestedThemeChanged -= OnRequestedThemeChanged;
}
_attached = value;
}
}
}
}