Skip to content

Commit 1ecfe7d

Browse files
committed
fix(combobox): fix binding to an old DataSource when both ItemsSource and SelectedItem are bound
1 parent effe8dd commit 1ecfe7d

File tree

4 files changed

+68
-8
lines changed

4 files changed

+68
-8
lines changed

src/Uno.Foundation/Uno.Core.Extensions/Uno.Core.Extensions.Compatibility/Collections/ImmutableList.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,13 @@ public int LastIndexOf(T item, int index, int count, IEqualityComparer<T> equali
112112
return -1;
113113
}
114114

115+
/// <inheritdoc />
116+
[Pure]
117+
IImmutableList<T> IImmutableList<T>.Insert(int index, T element)
118+
{
119+
return Insert(index, element);
120+
}
121+
115122
/// <inheritdoc />
116123
[Pure]
117124
IImmutableList<T> IImmutableList<T>.Add(T value)
@@ -132,7 +139,7 @@ public IImmutableList<T> AddRange(IEnumerable<T> items)
132139

133140
/// <inheritdoc />
134141
[Pure]
135-
public IImmutableList<T> Insert(int index, T element)
142+
public ImmutableList<T> Insert(int index, T element)
136143
{
137144
var newData = new T[_data.Length + 1];
138145
newData[index] = element; // will throw out-of-range exception if index is not valid
@@ -141,7 +148,7 @@ public IImmutableList<T> Insert(int index, T element)
141148
{
142149
Array.Copy(_data, newData, index);
143150
}
144-
if (index + 1 < _data.Length)
151+
if (index < _data.Length)
145152
{
146153
Array.Copy(_data, index, newData, index + 1, _data.Length - index);
147154
}

src/Uno.UI/UI/Xaml/Controls/Primitives/Selector.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ protected override void OnApplyTemplate()
8383
"SelectedItem",
8484
typeof(object),
8585
typeof(Selector),
86-
new FrameworkPropertyMetadata(defaultValue: null)
86+
new FrameworkPropertyMetadata(defaultValue: null),
87+
new List<DependencyProperty> { ItemsControl.ItemsSourceProperty }
8788
);
8889

8990
public object SelectedItem

src/Uno.UI/UI/Xaml/DependencyProperty.cs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Collections.Immutable;
34
using System.Linq;
45
using System.Text;
56
using Windows.UI.Xaml;
@@ -51,7 +52,7 @@ public sealed partial class DependencyProperty
5152

5253
private static int _globalId;
5354

54-
private DependencyProperty(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, bool attached)
55+
private DependencyProperty(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, bool attached, IEnumerable<DependencyProperty> dependentProperties)
5556
{
5657
_name = name;
5758
_propertyType = propertyType;
@@ -70,6 +71,13 @@ private DependencyProperty(string name, Type propertyType, Type ownerType, Prope
7071

7172
// Improve the performance of the hash code by
7273
CachedHashCode = _name.GetHashCode() ^ ownerType.GetHashCode();
74+
75+
DependentProperties = dependentProperties.ToImmutableArray();
76+
}
77+
78+
private DependencyProperty(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, bool attached):
79+
this(name, propertyType, ownerType, defaultMetadata, attached, Enumerable.Empty<DependencyProperty>())
80+
{
7381
}
7482

7583
/// <summary>
@@ -95,12 +103,13 @@ internal bool IsDependencyObjectCollection
95103
/// <param name="name">The name of the property.</param>
96104
/// <param name="propertyType">The type of the property</param>
97105
/// <param name="ownerType">The owner type of the property</param>
98-
/// <param name="typeMetadata">The metadata to use when creating the property</param>
106+
/// <param name="defaultMetadata">The metadata to use when creating the property</param>
107+
/// <param name="dependentProperties">Other properties whose bindings need to update before the new property's bindings</param>
99108
/// <returns>A dependency property instance</returns>
100109
/// <exception cref="InvalidOperationException">A property with the same name has already been declared for the ownerType</exception>
101-
public static DependencyProperty Register(string name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata)
110+
internal static DependencyProperty Register(string name, Type propertyType, Type ownerType, PropertyMetadata defaultMetadata, IEnumerable<DependencyProperty> dependentProperties)
102111
{
103-
var newProperty = new DependencyProperty(name, propertyType, ownerType, typeMetadata, attached: false);
112+
var newProperty = new DependencyProperty(name, propertyType, ownerType, defaultMetadata, attached: true, dependentProperties);
104113

105114
try
106115
{
@@ -117,6 +126,18 @@ public static DependencyProperty Register(string name, Type propertyType, Type o
117126
return newProperty;
118127
}
119128

129+
/// <summary>
130+
/// Registers a dependency property on the specified <paramref name="ownerType"/>.
131+
/// </summary>
132+
/// <param name="name">The name of the property.</param>
133+
/// <param name="propertyType">The type of the property</param>
134+
/// <param name="ownerType">The owner type of the property</param>
135+
/// <param name="typeMetadata">The metadata to use when creating the property</param>
136+
/// <returns>A dependency property instance</returns>
137+
/// <exception cref="InvalidOperationException">A property with the same name has already been declared for the ownerType</exception>
138+
public static DependencyProperty Register(string name, Type propertyType, Type ownerType, PropertyMetadata typeMetadata)
139+
=> Register(name, propertyType, ownerType, typeMetadata, Enumerable.Empty<DependencyProperty>());
140+
120141
/// <summary>
121142
/// Registers a dependency property on the specified <paramref name="ownerType"/>.
122143
/// </summary>
@@ -191,6 +212,8 @@ internal int CachedHashCode
191212
get;
192213
}
193214

215+
internal ImmutableArray<DependencyProperty> DependentProperties { get; } = ImmutableArray<DependencyProperty>.Empty;
216+
194217
/// <summary>
195218
/// Specifies a static value that is used by the dependency property system rather than null to indicate that
196219
/// the property exists, but does not have its value set by the dependency property system.

src/Uno.UI/UI/Xaml/DependencyPropertyDetailsCollection.Bindings.cs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Linq;
34
using System.Text;
45
using Uno.Collections;
56
using Uno.Extensions;
@@ -41,6 +42,7 @@ internal void ApplyTemplatedParent(FrameworkElement templatedParent)
4142
/// </summary>
4243
public void ApplyDataContext(object dataContext)
4344
{
45+
_bindings = new (_bindings.OrderBy(x => x, new BindingExpressionComparer()));
4446
var bindings = _bindings.Data;
4547

4648
for (int i = 0; i < bindings.Length; i++)
@@ -146,7 +148,12 @@ internal void SetBinding(DependencyProperty dependencyProperty, Binding binding,
146148
}
147149
else
148150
{
149-
_bindings = _bindings.Add(bindingExpression);
151+
var index = Array.BinarySearch(_bindings.Data, bindingExpression, new BindingExpressionComparer());
152+
if (index < 0)
153+
{
154+
index = ~index;
155+
}
156+
_bindings = _bindings.Insert(index, bindingExpression);
150157

151158
if (bindingExpression.TargetPropertyDetails.Property.UniqueId == DataContextPropertyDetails.Property.UniqueId)
152159
{
@@ -219,5 +226,27 @@ internal BindingExpression GetBindingExpression(DependencyProperty dependencyPro
219226

220227
return null;
221228
}
229+
230+
class BindingExpressionComparer : IComparer<BindingExpression>
231+
{
232+
public int Compare(BindingExpression expression1, BindingExpression expression2)
233+
{
234+
var property1 = expression1.TargetPropertyDetails.Property;
235+
var property2 = expression2.TargetPropertyDetails.Property;
236+
237+
if (property1.DependentProperties.Contains(property2))
238+
{
239+
return -1;
240+
}
241+
242+
if (property2.DependentProperties.Contains(property1))
243+
{
244+
return 1;
245+
}
246+
247+
// make sure we define a total order
248+
return string.Compare(property1.ToString(), property2.ToString(), StringComparison.OrdinalIgnoreCase);
249+
}
250+
}
222251
}
223252
}

0 commit comments

Comments
 (0)