Skip to content

Commit d28a387

Browse files
committed
Fix Dropdown invalid states issue.
1 parent 6881246 commit d28a387

File tree

2 files changed

+107
-56
lines changed

2 files changed

+107
-56
lines changed

src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableDropdownField.cs

Lines changed: 100 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ namespace UnityMvvmToolkit.UITK.BindableUIElements
1515
public partial class BindableDropdownField : DropdownField, IBindableCollection, IInitializable, IDisposable
1616
{
1717
private IProperty<string> _selectedItemProperty;
18-
private IReadOnlyProperty<ObservableCollection<string>> _itemsSource;
19-
2018
private PropertyBindingData _selectedItemBindingData;
19+
2120
private PropertyBindingData _itemsSourceBindingData;
21+
private IReadOnlyProperty<ObservableCollection<string>> _itemsSource;
2222

2323
public void Initialize()
2424
{
@@ -27,102 +27,155 @@ public void Initialize()
2727

2828
public void Dispose()
2929
{
30-
choices.Clear();
30+
RemoveAllItems();
3131
}
32-
32+
3333
public void SetBindingContext(IBindingContext context, IObjectProvider objectProvider)
3434
{
35-
if (string.IsNullOrWhiteSpace(BindingItemsSourcePath) == false)
35+
if (string.IsNullOrWhiteSpace(BindingItemsSourcePath))
3636
{
37-
_itemsSourceBindingData ??= BindingItemsSourcePath.ToPropertyBindingData();
38-
_itemsSource = objectProvider
39-
.RentReadOnlyProperty<ObservableCollection<string>>(context, _itemsSourceBindingData);
40-
_itemsSource.Value.CollectionChanged += OnItemsCollectionChanged;
41-
choices = new List<string>(_itemsSource.Value);
37+
return;
4238
}
43-
39+
40+
_itemsSourceBindingData ??= BindingItemsSourcePath.ToPropertyBindingData();
41+
42+
_itemsSource =
43+
objectProvider.RentReadOnlyProperty<ObservableCollection<string>>(context, _itemsSourceBindingData);
44+
_itemsSource.Value.CollectionChanged += OnItemsCollectionChanged;
45+
46+
AddItems(_itemsSource.Value);
47+
4448
if (string.IsNullOrWhiteSpace(BindingSelectedItemPath) == false)
4549
{
4650
_selectedItemBindingData ??= BindingSelectedItemPath.ToPropertyBindingData();
51+
4752
_selectedItemProperty = objectProvider.RentProperty<string>(context, _selectedItemBindingData);
48-
_selectedItemProperty.ValueChanged += OnSelectedItemValueChanged;
49-
50-
var isContains = choices.Contains(_selectedItemProperty.Value);
51-
if (isContains == true)
53+
54+
if (string.IsNullOrWhiteSpace(_selectedItemProperty.Value))
55+
{
56+
_selectedItemProperty.Value = choices.Count > 0 ? choices[0] : default;
57+
}
58+
else
5259
{
5360
UpdateControlValue(_selectedItemProperty.Value);
5461
}
55-
56-
this.RegisterValueChangedCallback(OnControlValueChanged);
57-
_selectedItemProperty.Value = choices.Count > 0 ? choices[0] : default;
62+
63+
_selectedItemProperty.ValueChanged += OnSelectedItemValueChanged;
64+
this.RegisterValueChangedCallback(OnControlSelectedValueChanged);
5865
}
5966
}
6067

68+
public virtual void ResetBindingContext(IObjectProvider objectProvider)
69+
{
70+
if (_itemsSource is null)
71+
{
72+
return;
73+
}
74+
75+
_itemsSource.Value.CollectionChanged -= OnItemsCollectionChanged;
76+
objectProvider.ReturnReadOnlyProperty(_itemsSource);
77+
_itemsSource = null;
78+
79+
if (_selectedItemProperty is not null)
80+
{
81+
_selectedItemProperty.ValueChanged -= OnSelectedItemValueChanged;
82+
objectProvider.ReturnProperty(_selectedItemProperty);
83+
_selectedItemProperty = null;
84+
85+
this.UnregisterValueChangedCallback(OnControlSelectedValueChanged);
86+
}
87+
88+
RemoveAllItems();
89+
}
90+
6191
protected virtual void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
6292
{
6393
if (e.Action == NotifyCollectionChangedAction.Add)
6494
{
6595
foreach (string newItem in e.NewItems)
6696
{
67-
choices.Add(newItem);
97+
AddItem(newItem);
6898
}
6999
}
70100

71101
if (e.Action == NotifyCollectionChangedAction.Remove)
72102
{
73103
foreach (string oldItem in e.OldItems)
74104
{
75-
choices.Remove(oldItem);
105+
RemoveItem(oldItem);
76106
}
77107
}
78-
108+
79109
if (e.Action == NotifyCollectionChangedAction.Reset)
80110
{
81-
choices.Clear();
111+
if (_itemsSource.Value.Count == 0)
112+
{
113+
RemoveAllItems();
114+
}
115+
else
116+
{
117+
throw new InvalidOperationException("Action not supported.");
118+
}
82119
}
83120
}
84121

85-
public virtual void ResetBindingContext(IObjectProvider objectProvider)
122+
protected virtual void OnControlSelectedValueChanged(ChangeEvent<string> e)
123+
{
124+
_selectedItemProperty.Value = e.newValue;
125+
}
126+
127+
private void OnSelectedItemValueChanged(object sender, string newValue)
86128
{
87-
if (_selectedItemProperty != null)
129+
UpdateControlValue(newValue);
130+
}
131+
132+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
133+
protected virtual void UpdateControlValue(string selectedItem)
134+
{
135+
if (choices.Any() && choices.Contains(selectedItem) == false)
88136
{
89-
_selectedItemProperty.ValueChanged -= OnSelectedItemValueChanged;
90-
objectProvider.ReturnProperty(_selectedItemProperty);
91-
_selectedItemProperty = null;
92-
this.UnregisterValueChangedCallback(OnControlValueChanged);
137+
throw new InvalidOperationException($"\"{selectedItem}\" is not presented in the collection.");
93138
}
94-
95-
if (_itemsSource != null)
139+
140+
SetValueWithoutNotify(selectedItem);
141+
}
142+
143+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
144+
private void AddItems(IEnumerable<string> items)
145+
{
146+
foreach (var item in items)
96147
{
97-
_itemsSource.Value.CollectionChanged -= OnItemsCollectionChanged;
98-
choices = new List<string>();
99-
objectProvider.ReturnReadOnlyProperty(_itemsSource);
100-
_itemsSource = null;
148+
AddItem(item);
101149
}
102-
103-
UpdateControlValue(default);
104150
}
105151

106-
protected virtual void OnControlValueChanged(ChangeEvent<string> e)
152+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
153+
private void AddItem(string item)
107154
{
108-
_selectedItemProperty.Value = e.newValue;
155+
if (string.IsNullOrWhiteSpace(item))
156+
{
157+
throw new NullReferenceException("Item cannot be null or empty.");
158+
}
159+
160+
choices.Add(item);
109161
}
110162

111-
private void OnSelectedItemValueChanged(object sender, string newValue)
163+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
164+
private void RemoveItem(string item)
112165
{
113-
var isContains = choices.Contains(newValue);
114-
if (isContains == false)
166+
choices.Remove(item);
167+
168+
if (value == item)
115169
{
116-
return;
170+
value = choices.Count == 0 ? default : choices[^1];
117171
}
118-
119-
UpdateControlValue(newValue);
120172
}
121-
173+
122174
[MethodImpl(MethodImplOptions.AggressiveInlining)]
123-
protected virtual void UpdateControlValue(string newValue)
175+
private void RemoveAllItems()
124176
{
125-
SetValueWithoutNotify(newValue);
177+
choices.Clear();
178+
value = default;
126179
}
127180
}
128181
}

src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/Uxmls/BindableDropdownField.Uxml.cs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@ namespace UnityMvvmToolkit.UITK.BindableUIElements
55
{
66
partial class BindableDropdownField
77
{
8-
public string BindingSelectedItemPath { get; private set; }
9-
108
public string BindingItemsSourcePath { get; private set; }
9+
public string BindingSelectedItemPath { get; private set; }
1110

1211
public new class UxmlFactory : UxmlFactory<BindableDropdownField, UxmlTraits>
1312
{
@@ -19,8 +18,8 @@ partial class BindableDropdownField
1918
{
2019
// ReSharper disable once InconsistentNaming
2120
#pragma warning disable 649
21+
[UnityEngine.SerializeField] private string BindingItemsSourcePath;
2222
[UnityEngine.SerializeField] private string BindingSelectedItemPath;
23-
[UnityEngine.SerializeField] private string BindingItemsSourcePath;
2423
#pragma warning restore 649
2524

2625
public override object CreateInstance() => new BindableDropdownField();
@@ -29,27 +28,26 @@ public override void Deserialize(object visualElement)
2928
base.Deserialize(visualElement);
3029

3130
var bindableDropdownField = visualElement.As<BindableDropdownField>();
32-
bindableDropdownField.BindingSelectedItemPath = BindingSelectedItemPath;
3331
bindableDropdownField.BindingItemsSourcePath = BindingItemsSourcePath;
32+
bindableDropdownField.BindingSelectedItemPath = BindingSelectedItemPath;
3433
}
3534
}
3635
#else
3736
public new class UxmlTraits : DropdownField.UxmlTraits
3837
{
39-
private readonly UxmlStringAttributeDescription _bindingSelectedItemPath = new()
40-
{ name = "binding-selected-item-path", defaultValue = "" };
41-
4238
private readonly UxmlStringAttributeDescription _bindingItemsSourcePath = new()
4339
{ name = "binding-items-source-path", defaultValue = "" };
4440

41+
private readonly UxmlStringAttributeDescription _bindingSelectedItemPath = new()
42+
{ name = "binding-selected-item-path", defaultValue = "" };
43+
4544
public override void Init(VisualElement visualElement, IUxmlAttributes bag, CreationContext context)
4645
{
4746
base.Init(visualElement, bag, context);
4847

4948
var bindableDropdownField = visualElement.As<BindableDropdownField>();
50-
51-
bindableDropdownField.BindingSelectedItemPath = _bindingSelectedItemPath.GetValueFromBag(bag, context);
5249
bindableDropdownField.BindingItemsSourcePath = _bindingItemsSourcePath.GetValueFromBag(bag, context);
50+
bindableDropdownField.BindingSelectedItemPath = _bindingSelectedItemPath.GetValueFromBag(bag, context);
5351
}
5452
}
5553
#endif

0 commit comments

Comments
 (0)