From e5129a1eda14199335d9ed5ddfa0cdda5a9144d3 Mon Sep 17 00:00:00 2001 From: ChebanovDD Date: Sat, 17 Jun 2023 20:16:11 +0800 Subject: [PATCH 1/8] Minor changes. --- .../UITK/BindableUIElements/BindableScrollView.T.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableScrollView.T.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableScrollView.T.cs index 9279c96..b0269a8 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableScrollView.T.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableScrollView.T.cs @@ -52,6 +52,11 @@ public virtual void Dispose() public virtual void SetBindingContext(IBindingContext context, IObjectProvider objectProvider) { + if (string.IsNullOrWhiteSpace(BindingItemsSourcePath)) + { + return; + } + _itemsSourceBindingData ??= BindingItemsSourcePath.ToPropertyBindingData(); _itemTemplate ??= objectProvider.GetCollectionItemTemplate(); @@ -101,7 +106,7 @@ protected virtual void UnbindItem(VisualElement item, int index, TItemBindingCon item.ResetChildsBindingContext(objectProvider); } - private void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + protected virtual void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.Action == NotifyCollectionChangedAction.Add) { From fa2e92eb830aa75b00b957d99aedb448d5fc4146 Mon Sep 17 00:00:00 2001 From: ChebanovDD Date: Sat, 17 Jun 2023 20:16:43 +0800 Subject: [PATCH 2/8] Fix unbind issue. --- .../BindableUIElements/BindableListView.T.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableListView.T.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableListView.T.cs index fb62d52..aa1720e 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableListView.T.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableListView.T.cs @@ -40,6 +40,11 @@ public virtual void Dispose() public virtual void SetBindingContext(IBindingContext context, IObjectProvider objectProvider) { + if (string.IsNullOrWhiteSpace(BindingItemsSourcePath)) + { + return; + } + _itemsSourceBindingData ??= BindingItemsSourcePath.ToPropertyBindingData(); _itemTemplate ??= objectProvider.GetCollectionItemTemplate(); @@ -95,14 +100,22 @@ protected virtual void UnbindItem(VisualElement item, int index, [CanBeNull] TIt item.ResetChildsBindingContext(objectProvider); } - private void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + protected virtual void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { - RefreshItems(); // TODO: Do not refresh all items. + if (e.Action == NotifyCollectionChangedAction.Remove) + { + Rebuild(); + } + else + { + RefreshItems(); + } } private VisualElement OnMakeItem() { var item = MakeItem(_itemTemplate); + _itemAssets.Add(item); return item; From e3de7566fc0b64c2876172f66308bf82512bbd55 Mon Sep 17 00:00:00 2001 From: Vasilev <35258579+VasilevMaxim@users.noreply.github.com> Date: Mon, 26 Jun 2023 05:25:04 +0300 Subject: [PATCH 3/8] Bindable dropdown (#31) Add a bindable Dropdown element. --- README.md | 38 ++++++ .../BindableUGUIElements/BindableDropdown.cs | 127 +++++++++++++++++ .../BindableDropdown.cs.meta | 11 ++ .../BindableDropdownField.cs | 128 ++++++++++++++++++ .../BindableDropdownField.cs.meta | 3 + .../Uxmls/BindableDropdownField.Uxml.cs | 57 ++++++++ .../Uxmls/BindableDropdownField.Uxml.cs.meta | 3 + 7 files changed, 367 insertions(+) create mode 100644 src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UGUI/BindableUGUIElements/BindableDropdown.cs create mode 100644 src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UGUI/BindableUGUIElements/BindableDropdown.cs.meta create mode 100644 src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableDropdownField.cs create mode 100644 src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableDropdownField.cs.meta create mode 100644 src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/Uxmls/BindableDropdownField.Uxml.cs create mode 100644 src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/Uxmls/BindableDropdownField.Uxml.cs.meta diff --git a/README.md b/README.md index ac16635..dad7b2f 100644 --- a/README.md +++ b/README.md @@ -840,6 +840,44 @@ The `BindableButton` can be bound to the following commands: To pass a parameter to the viewmodel, see the [ParameterValueConverter](#parametervalueconverterttargettype) section. +#### BindableDropdownField + +The `BindableDropdownField` allows you to work with dropdown. To set the binding of the selected value, you need to write `binding-selected-item-path`, and to set the binding of all dropdown elements, you need to use `binding-items-source-path`. +Moreover, you can set them independently of each other. + +The following example demonstrates how to bind to a collection of strings with `BindableDropdownField`. + +In XML, you need to write the following: + +```xml + + + +``` +And in the C# class the following: + +```csharp +public class DropdownFieldViewModel : IBindingContext +{ + public DropdownFieldViewModel() + { + TextValues = new ReadOnlyProperty>(new ObservableCollection() + { + "Value1", + "Value2", + "Value3" + }); + + SelectedValue = new Property(); + SelectedValue.Value = "Value1"; + } + + public IProperty SelectedValue { get; } + + public IReadOnlyProperty> Choices { get; } +} +``` + #### BindableListView The `BindableListView` control is the most efficient way to create lists. It uses virtualization and creates VisualElements only for visible items. Use the `binding-items-source-path` of the `BindableListView` to bind to an `ObservableCollection`. diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UGUI/BindableUGUIElements/BindableDropdown.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UGUI/BindableUGUIElements/BindableDropdown.cs new file mode 100644 index 0000000..3f0da78 --- /dev/null +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UGUI/BindableUGUIElements/BindableDropdown.cs @@ -0,0 +1,127 @@ +#if UNITYMVVMTOOLKIT_TEXTMESHPRO_SUPPORT + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Linq; +using System.Runtime.CompilerServices; +using TMPro; +using UnityEngine; +using UnityMvvmToolkit.Core; +using UnityMvvmToolkit.Core.Extensions; +using UnityMvvmToolkit.Core.Interfaces; + +namespace UnityMvvmToolkit.UGUI.BindableUGUIElements +{ + [RequireComponent(typeof(TMP_Dropdown))] + public class BindableDropdown : MonoBehaviour, IBindableElement + { + [SerializeField] private TMP_Dropdown _dropdown; + [SerializeField] private string _bindingSelectedItemPath; + [SerializeField] private string _bindingItemsSourcePath; + + private IProperty _selectedItemProperty; + private IReadOnlyProperty> _itemsSource; + + private PropertyBindingData _selectedItemBindingData; + private PropertyBindingData _itemsSourceBindingData; + + public void SetBindingContext(IBindingContext context, IObjectProvider objectProvider) + { + if (string.IsNullOrWhiteSpace(_bindingItemsSourcePath) == false) + { + _itemsSourceBindingData ??= _bindingItemsSourcePath.ToPropertyBindingData(); + _itemsSource = objectProvider + .RentReadOnlyProperty>(context, _itemsSourceBindingData); + _itemsSource.Value.CollectionChanged += OnItemsCollectionChanged; + _dropdown.options = new List(_itemsSource.Value.Select(value => new TMP_Dropdown.OptionData(value))); + } + + if (string.IsNullOrWhiteSpace(_bindingSelectedItemPath) == false) + { + _selectedItemBindingData ??= _bindingSelectedItemPath.ToPropertyBindingData(); + _selectedItemProperty = objectProvider.RentProperty(context, _selectedItemBindingData); + _selectedItemProperty.ValueChanged += OnPropertySelectedItemChanged; + + var foundIndex = _dropdown.options.FindIndex(option => option.text == _selectedItemProperty.Value); + if (foundIndex != -1) + { + UpdateControlValue(foundIndex); + } + + _dropdown.onValueChanged.AddListener(OnControlValueChanged); + _selectedItemProperty.Value = _dropdown.options.Count > 0 ? _dropdown.options[0].text : default; + } + } + + private void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if (e.Action == NotifyCollectionChangedAction.Add) + { + foreach (string newItem in e.NewItems) + { + _dropdown.options.Add(new TMP_Dropdown.OptionData(newItem)); + } + } + + if (e.Action == NotifyCollectionChangedAction.Remove) + { + foreach (string oldItem in e.OldItems) + { + _dropdown.options.Remove(new TMP_Dropdown.OptionData(oldItem)); + } + } + + if (e.Action == NotifyCollectionChangedAction.Reset) + { + _dropdown.options.Clear(); + } + } + + public virtual void ResetBindingContext(IObjectProvider objectProvider) + { + if (_itemsSource != null) + { + _itemsSource.Value.CollectionChanged -= OnItemsCollectionChanged; + objectProvider.ReturnReadOnlyProperty(_itemsSource); + _itemsSource = null; + _dropdown.options = new List(); + } + + if (_selectedItemProperty != null) + { + _selectedItemProperty.ValueChanged -= OnPropertySelectedItemChanged; + objectProvider.ReturnProperty(_selectedItemProperty); + _selectedItemProperty = null; + _dropdown.onValueChanged.RemoveListener(OnControlValueChanged); + } + + UpdateControlValue(default); + } + + protected virtual void OnControlValueChanged(int index) + { + _selectedItemProperty.Value = _dropdown.options[index].text; + } + + private void OnPropertySelectedItemChanged(object sender, string newValue) + { + var foundIndex = _dropdown.options.FindIndex(option => option.text == newValue); + if (foundIndex == -1) + { + return; + } + + UpdateControlValue(foundIndex); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected virtual void UpdateControlValue(int newValue) + { + _dropdown.SetValueWithoutNotify(newValue); + } + } +} + +#endif \ No newline at end of file diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UGUI/BindableUGUIElements/BindableDropdown.cs.meta b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UGUI/BindableUGUIElements/BindableDropdown.cs.meta new file mode 100644 index 0000000..05c1cb9 --- /dev/null +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UGUI/BindableUGUIElements/BindableDropdown.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f7044586e66117e48948637bf2c66eaa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableDropdownField.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableDropdownField.cs new file mode 100644 index 0000000..cb05421 --- /dev/null +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableDropdownField.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Linq; +using System.Runtime.CompilerServices; +using UnityEngine.UIElements; +using UnityMvvmToolkit.Common.Interfaces; +using UnityMvvmToolkit.Core; +using UnityMvvmToolkit.Core.Extensions; +using UnityMvvmToolkit.Core.Interfaces; + +namespace UnityMvvmToolkit.UITK.BindableUIElements +{ + public partial class BindableDropdownField : DropdownField, IBindableCollection, IInitializable, IDisposable + { + private IProperty _selectedItemProperty; + private IReadOnlyProperty> _itemsSource; + + private PropertyBindingData _selectedItemBindingData; + private PropertyBindingData _itemsSourceBindingData; + + public void Initialize() + { + choices = new List(); + } + + public void Dispose() + { + choices.Clear(); + } + + public void SetBindingContext(IBindingContext context, IObjectProvider objectProvider) + { + if (string.IsNullOrWhiteSpace(BindingItemsSourcePath) == false) + { + _itemsSourceBindingData ??= BindingItemsSourcePath.ToPropertyBindingData(); + _itemsSource = objectProvider + .RentReadOnlyProperty>(context, _itemsSourceBindingData); + _itemsSource.Value.CollectionChanged += OnItemsCollectionChanged; + choices = new List(_itemsSource.Value); + } + + if (string.IsNullOrWhiteSpace(BindingSelectedItemPath) == false) + { + _selectedItemBindingData ??= BindingSelectedItemPath.ToPropertyBindingData(); + _selectedItemProperty = objectProvider.RentProperty(context, _selectedItemBindingData); + _selectedItemProperty.ValueChanged += OnSelectedItemValueChanged; + + var isContains = choices.Contains(_selectedItemProperty.Value); + if (isContains == true) + { + UpdateControlValue(_selectedItemProperty.Value); + } + + this.RegisterValueChangedCallback(OnControlValueChanged); + _selectedItemProperty.Value = choices.Count > 0 ? choices[0] : default; + } + } + + protected virtual void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if (e.Action == NotifyCollectionChangedAction.Add) + { + foreach (string newItem in e.NewItems) + { + choices.Add(newItem); + } + } + + if (e.Action == NotifyCollectionChangedAction.Remove) + { + foreach (string oldItem in e.OldItems) + { + choices.Remove(oldItem); + } + } + + if (e.Action == NotifyCollectionChangedAction.Reset) + { + choices.Clear(); + } + } + + public virtual void ResetBindingContext(IObjectProvider objectProvider) + { + if (_selectedItemProperty != null) + { + _selectedItemProperty.ValueChanged -= OnSelectedItemValueChanged; + objectProvider.ReturnProperty(_selectedItemProperty); + _selectedItemProperty = null; + this.UnregisterValueChangedCallback(OnControlValueChanged); + } + + if (_itemsSource != null) + { + _itemsSource.Value.CollectionChanged -= OnItemsCollectionChanged; + choices = new List(); + objectProvider.ReturnReadOnlyProperty(_itemsSource); + _itemsSource = null; + } + + UpdateControlValue(default); + } + + protected virtual void OnControlValueChanged(ChangeEvent e) + { + _selectedItemProperty.Value = e.newValue; + } + + private void OnSelectedItemValueChanged(object sender, string newValue) + { + var isContains = choices.Contains(newValue); + if (isContains == false) + { + return; + } + + UpdateControlValue(newValue); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected virtual void UpdateControlValue(string newValue) + { + SetValueWithoutNotify(newValue); + } + } +} \ No newline at end of file diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableDropdownField.cs.meta b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableDropdownField.cs.meta new file mode 100644 index 0000000..065981e --- /dev/null +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableDropdownField.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8c46923c0a364c539ce1f2895dec04ed +timeCreated: 1686411067 \ No newline at end of file diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/Uxmls/BindableDropdownField.Uxml.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/Uxmls/BindableDropdownField.Uxml.cs new file mode 100644 index 0000000..f89007b --- /dev/null +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/Uxmls/BindableDropdownField.Uxml.cs @@ -0,0 +1,57 @@ +using UnityEngine.UIElements; +using UnityMvvmToolkit.UITK.Extensions; + +namespace UnityMvvmToolkit.UITK.BindableUIElements +{ + partial class BindableDropdownField + { + public string BindingSelectedItemPath { get; private set; } + + public string BindingItemsSourcePath { get; private set; } + + public new class UxmlFactory : UxmlFactory + { + } + +#if UNITY_2023_2_OR_NEWER + [System.Serializable] + public new class UxmlSerializedData : DropdownField.UxmlSerializedData + { + // ReSharper disable once InconsistentNaming + #pragma warning disable 649 + [UnityEngine.SerializeField] private string BindingSelectedItemPath; + [UnityEngine.SerializeField] private string BindingItemsSourcePath; + #pragma warning restore 649 + + public override object CreateInstance() => new BindableDropdownField(); + public override void Deserialize(object visualElement) + { + base.Deserialize(visualElement); + + var bindableDropdownField = visualElement.As(); + bindableDropdownField.BindingSelectedItemPath = BindingSelectedItemPath; + bindableDropdownField.BindingItemsSourcePath = BindingItemsSourcePath; + } + } +#else + public new class UxmlTraits : DropdownField.UxmlTraits + { + private readonly UxmlStringAttributeDescription _bindingSelectedItemPath = new() + { name = "binding-selected-item-path", defaultValue = "" }; + + private readonly UxmlStringAttributeDescription _bindingItemsSourcePath = new() + { name = "binding-items-source-path", defaultValue = "" }; + + public override void Init(VisualElement visualElement, IUxmlAttributes bag, CreationContext context) + { + base.Init(visualElement, bag, context); + + var bindableDropdownField = visualElement.As(); + + bindableDropdownField.BindingSelectedItemPath = _bindingSelectedItemPath.GetValueFromBag(bag, context); + bindableDropdownField.BindingItemsSourcePath = _bindingItemsSourcePath.GetValueFromBag(bag, context); + } + } +#endif + } +} \ No newline at end of file diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/Uxmls/BindableDropdownField.Uxml.cs.meta b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/Uxmls/BindableDropdownField.Uxml.cs.meta new file mode 100644 index 0000000..32d3949 --- /dev/null +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/Uxmls/BindableDropdownField.Uxml.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6c58f7387a96411e88df2f3f4963bb93 +timeCreated: 1686411216 \ No newline at end of file From 978fd2935d6ae48a8d98302a65cd0360357f4b12 Mon Sep 17 00:00:00 2001 From: ChebanovDD Date: Mon, 26 Jun 2023 19:38:29 +0800 Subject: [PATCH 4/8] Resolve #35. Fix ListView remove item issue. --- .../BindableUIElements/BindableListView.T.cs | 53 +++++++++++-------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableListView.T.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableListView.T.cs index aa1720e..e610974 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableListView.T.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableListView.T.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; -using JetBrains.Annotations; using UnityEngine.UIElements; using UnityMvvmToolkit.Common.Interfaces; using UnityMvvmToolkit.Core; @@ -18,14 +17,17 @@ public abstract partial class BindableListView : ListView, private VisualTreeAsset _itemTemplate; private PropertyBindingData _itemsSourceBindingData; - private IReadOnlyProperty> _itemsSource; + private ObservableCollection _itemsSource; + private IReadOnlyProperty> _itemsSourceProperty; private IObjectProvider _objectProvider; private List _itemAssets; + private Dictionary _activeItems; public virtual void Initialize() { _itemAssets = new List(); + _activeItems = new Dictionary(); } public virtual void Dispose() @@ -36,6 +38,7 @@ public virtual void Dispose() } _itemAssets.Clear(); + _activeItems.Clear(); } public virtual void SetBindingContext(IBindingContext context, IObjectProvider objectProvider) @@ -50,11 +53,12 @@ public virtual void SetBindingContext(IBindingContext context, IObjectProvider o _objectProvider = objectProvider; - _itemsSource = objectProvider + _itemsSourceProperty = objectProvider .RentReadOnlyProperty>(context, _itemsSourceBindingData); - _itemsSource.Value.CollectionChanged += OnItemsCollectionChanged; + _itemsSource = _itemsSourceProperty.Value; + _itemsSource.CollectionChanged += OnItemsCollectionChanged; - itemsSource = _itemsSource.Value; + itemsSource = _itemsSource; makeItem += OnMakeItem; bindItem += OnBindItem; unbindItem += OnUnbindItem; @@ -62,23 +66,25 @@ public virtual void SetBindingContext(IBindingContext context, IObjectProvider o public virtual void ResetBindingContext(IObjectProvider objectProvider) { - if (_itemsSource is null) + if (_itemsSourceProperty is null) { return; } - _itemsSource.Value.CollectionChanged -= OnItemsCollectionChanged; + _itemsSource.CollectionChanged -= OnItemsCollectionChanged; makeItem -= OnMakeItem; bindItem -= OnBindItem; unbindItem -= OnUnbindItem; itemsSource = Array.Empty(); - objectProvider.ReturnReadOnlyProperty(_itemsSource); + objectProvider.ReturnReadOnlyProperty(_itemsSourceProperty); - _itemsSource = null; _itemTemplate = null; _objectProvider = null; + + _itemsSource = null; + _itemsSourceProperty = null; } protected virtual VisualElement MakeItem(VisualTreeAsset itemTemplate) @@ -94,7 +100,7 @@ protected virtual void BindItem(VisualElement item, int index, TItemBindingConte item.SetChildsBindingContext(bindingContext, objectProvider); } - protected virtual void UnbindItem(VisualElement item, int index, [CanBeNull] TItemBindingContext bindingContext, + protected virtual void UnbindItem(VisualElement item, int index, TItemBindingContext bindingContext, IObjectProvider objectProvider) { item.ResetChildsBindingContext(objectProvider); @@ -102,14 +108,14 @@ protected virtual void UnbindItem(VisualElement item, int index, [CanBeNull] TIt protected virtual void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { - if (e.Action == NotifyCollectionChangedAction.Remove) +#if UNITY_2021 + if (e.Action is NotifyCollectionChangedAction.Remove or NotifyCollectionChangedAction.Reset) { Rebuild(); + return; } - else - { - RefreshItems(); - } +#endif + RefreshItems(); } private VisualElement OnMakeItem() @@ -123,18 +129,21 @@ private VisualElement OnMakeItem() private void OnBindItem(VisualElement item, int index) { - BindItem(item, index, _itemsSource.Value[index], _objectProvider); + var itemId = item.GetHashCode(); + var itemBindingContext = _itemsSource[index]; + + _activeItems.Add(itemId, itemBindingContext); + + BindItem(item, index, itemBindingContext, _objectProvider); } private void OnUnbindItem(VisualElement item, int index) { - if (index >= 0 && index < itemsSource.Count) - { - UnbindItem(item, index, _itemsSource.Value[index], _objectProvider); - } - else + var itemId = item.GetHashCode(); + + if (_activeItems.Remove(itemId, out var itemBindingContext)) { - UnbindItem(item, index, default, _objectProvider); + UnbindItem(item, index, itemBindingContext, _objectProvider); } } } From 21ba331a8d78b4b9961954ba1f0c2fe647fd6c64 Mon Sep 17 00:00:00 2001 From: ChebanovDD Date: Mon, 26 Jun 2023 19:45:12 +0800 Subject: [PATCH 5/8] Resolve #36. Fix ScrollView clear collection issue. --- .../Runtime/Common/BindableElementData.cs | 25 +++++++++ .../Common/BindableElementData.cs.meta | 3 ++ .../BindableScrollView.T.cs | 54 +++++++++++++++---- .../Extensions/VisualElementExtensions.cs | 29 +++++++--- 4 files changed, 94 insertions(+), 17 deletions(-) create mode 100644 src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Common/BindableElementData.cs create mode 100644 src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Common/BindableElementData.cs.meta diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Common/BindableElementData.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Common/BindableElementData.cs new file mode 100644 index 0000000..982609b --- /dev/null +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Common/BindableElementData.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using UnityMvvmToolkit.Core.Interfaces; + +namespace UnityMvvmToolkit.Common +{ + public sealed class BindableElementData : IDisposable + { + private readonly List _bindableElements; + + public BindableElementData(List bindableElements) + { + _bindableElements = bindableElements; + } + + public IBindingContext BindingContext { get; set; } + public IReadOnlyList BindableElements => _bindableElements; + + public void Dispose() + { + _bindableElements.Clear(); + BindingContext = default; + } + } +} \ No newline at end of file diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Common/BindableElementData.cs.meta b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Common/BindableElementData.cs.meta new file mode 100644 index 0000000..fe58f86 --- /dev/null +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/Common/BindableElementData.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0338651671e94fb7b83932e9aed12fc4 +timeCreated: 1687771988 \ No newline at end of file diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableScrollView.T.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableScrollView.T.cs index b0269a8..e17ca43 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableScrollView.T.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableScrollView.T.cs @@ -21,7 +21,8 @@ public abstract partial class BindableScrollView : ScrollVi private VisualTreeAsset _itemTemplate; private PropertyBindingData _itemsSourceBindingData; - private IReadOnlyProperty> _itemsSource; + private ObservableCollection _itemsSource; + private IReadOnlyProperty> _itemsSourceProperty; private IObjectProvider _objectProvider; @@ -44,7 +45,7 @@ public virtual void Dispose() } else { - ClearItems(_itemsSource.Value); + ClearItems(_itemsSource); } _itemAssetsPool.Dispose(); @@ -62,29 +63,32 @@ public virtual void SetBindingContext(IBindingContext context, IObjectProvider o _objectProvider = objectProvider; - _itemsSource = objectProvider + _itemsSourceProperty = objectProvider .RentReadOnlyProperty>(context, _itemsSourceBindingData); - _itemsSource.Value.CollectionChanged += OnItemsCollectionChanged; + _itemsSource = _itemsSourceProperty.Value; + _itemsSource.CollectionChanged += OnItemsCollectionChanged; - AddItems(_itemsSource.Value); + AddItems(_itemsSource); } public virtual void ResetBindingContext(IObjectProvider objectProvider) { - if (_itemsSource is null) + if (_itemsSourceProperty is null) { return; } - _itemsSource.Value.CollectionChanged -= OnItemsCollectionChanged; + _itemsSource.CollectionChanged -= OnItemsCollectionChanged; - ClearItems(_itemsSource.Value); + ClearItems(_itemsSource); - objectProvider.ReturnReadOnlyProperty(_itemsSource); + objectProvider.ReturnReadOnlyProperty(_itemsSourceProperty); - _itemsSource = null; _itemTemplate = null; _objectProvider = null; + + _itemsSource = null; + _itemsSourceProperty = null; } protected virtual VisualElement MakeItem(VisualTreeAsset itemTemplate) @@ -125,6 +129,18 @@ protected virtual void OnItemsCollectionChanged(object sender, NotifyCollectionC RemoveItem((TItemBindingContext) oldItem); } } + + if (e.Action == NotifyCollectionChangedAction.Reset) + { + if (_itemsSource.Count == 0) + { + ClearItems(); + } + else + { + throw new InvalidOperationException("Action not supported."); + } + } } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -173,6 +189,24 @@ private void ClearItems(IReadOnlyList items) } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ClearItems() + { + foreach (var (_, item) in _itemAssets) + { + _itemsCount--; + + if (_objectProvider is not null) + { + UnbindItem(item, _itemsCount, item.GetBindingContext(), _objectProvider); + } + + _itemAssetsPool.Release(item); + } + + _itemAssets.Clear(); + } + private VisualElement OnPoolInstantiateItem() { return MakeItem(_itemTemplate); diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/Extensions/VisualElementExtensions.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/Extensions/VisualElementExtensions.cs index 27f84fa..5a4992b 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/Extensions/VisualElementExtensions.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/Extensions/VisualElementExtensions.cs @@ -3,6 +3,7 @@ using System.Runtime.CompilerServices; using JetBrains.Annotations; using UnityEngine.UIElements; +using UnityMvvmToolkit.Common; using UnityMvvmToolkit.Common.Interfaces; using UnityMvvmToolkit.Core.Interfaces; @@ -14,7 +15,7 @@ public static partial class VisualElementExtensions public static VisualElement InstantiateBindableElement(this VisualTreeAsset visualTreeAsset) { var visualElement = visualTreeAsset.Instantiate(); - visualElement.userData = visualElement.GetBindableChilds(); + visualElement.userData = new BindableElementData(visualElement.GetBindableChilds()); return visualElement; } @@ -42,7 +43,7 @@ public static VisualElement InitializeBindableElement(this VisualElement visualE initializable.Initialize(); } - var bindableElements = (List) visualElement.userData; + var bindableElements = ((BindableElementData) visualElement.userData).BindableElements; for (var i = 0; i < bindableElements.Count; i++) { @@ -55,11 +56,21 @@ public static VisualElement InitializeBindableElement(this VisualElement visualE return visualElement; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static TBindingContext GetBindingContext(this VisualElement visualElement) + where TBindingContext : IBindingContext + { + return (TBindingContext) ((BindableElementData) visualElement.userData).BindingContext; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void SetChildsBindingContext(this VisualElement visualElement, IBindingContext bindingContext, IObjectProvider objectProvider) { - var bindableElements = (List) visualElement.userData; + var bindableElementData = (BindableElementData) visualElement.userData; + bindableElementData.BindingContext = bindingContext; + + var bindableElements = bindableElementData.BindableElements; for (var i = 0; i < bindableElements.Count; i++) { @@ -70,7 +81,10 @@ public static void SetChildsBindingContext(this VisualElement visualElement, IBi [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ResetChildsBindingContext(this VisualElement visualElement, IObjectProvider objectProvider) { - var bindableElements = (List) visualElement.userData; + var bindableElementData = (BindableElementData) visualElement.userData; + bindableElementData.BindingContext = default; + + var bindableElements = bindableElementData.BindableElements; for (var i = 0; i < bindableElements.Count; i++) { @@ -82,7 +96,8 @@ public static void ResetChildsBindingContext(this VisualElement visualElement, I public static void DisposeBindableElement(this VisualElement visualElement, [CanBeNull] IObjectProvider objectProvider = null) { - var bindableElements = (List) visualElement.userData; + var bindableElementData = (BindableElementData) visualElement.userData; + var bindableElements = bindableElementData.BindableElements; for (var i = 0; i < bindableElements.Count; i++) { @@ -104,8 +119,8 @@ public static void DisposeBindableElement(this VisualElement visualElement, disposable.Dispose(); } - bindableElements.Clear(); - visualElement.userData = null; + bindableElementData.Dispose(); + visualElement.userData = default; } [MethodImpl(MethodImplOptions.AggressiveInlining)] From 6881246c3cb4bd23b2e3d1ae31763236119f3b5b Mon Sep 17 00:00:00 2001 From: ChebanovDD Date: Fri, 30 Jun 2023 13:39:53 +0800 Subject: [PATCH 6/8] Minor changes. --- .../Runtime/UITK/BindableUIElements/BindableButton.cs | 2 +- .../Runtime/UITK/BindableUIElements/BindableLabel.cs | 2 +- .../Runtime/UITK/BindableUIElements/BindableTextField.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableButton.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableButton.cs index a303a4c..69702a8 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableButton.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableButton.cs @@ -34,7 +34,7 @@ public virtual void SetBindingContext(IBindingContext context, IObjectProvider o public virtual void ResetBindingContext(IObjectProvider objectProvider) { - if (_command == null) + if (_command is null) { return; } diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableLabel.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableLabel.cs index 4fa9dcf..ddb736d 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableLabel.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableLabel.cs @@ -28,7 +28,7 @@ public virtual void SetBindingContext(IBindingContext context, IObjectProvider o public virtual void ResetBindingContext(IObjectProvider objectProvider) { - if (_textProperty == null) + if (_textProperty is null) { return; } diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableTextField.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableTextField.cs index 9ee08b7..c7a7bca 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableTextField.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableTextField.cs @@ -29,7 +29,7 @@ public virtual void SetBindingContext(IBindingContext context, IObjectProvider o public virtual void ResetBindingContext(IObjectProvider objectProvider) { - if (_valueProperty == null) + if (_valueProperty is null) { return; } From d28a38795c349f52aa0a1f4737dffa39bab0a5d5 Mon Sep 17 00:00:00 2001 From: ChebanovDD Date: Fri, 30 Jun 2023 13:41:27 +0800 Subject: [PATCH 7/8] Fix Dropdown invalid states issue. --- .../BindableDropdownField.cs | 147 ++++++++++++------ .../Uxmls/BindableDropdownField.Uxml.cs | 16 +- 2 files changed, 107 insertions(+), 56 deletions(-) diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableDropdownField.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableDropdownField.cs index cb05421..ef40e6d 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableDropdownField.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/BindableDropdownField.cs @@ -15,10 +15,10 @@ namespace UnityMvvmToolkit.UITK.BindableUIElements public partial class BindableDropdownField : DropdownField, IBindableCollection, IInitializable, IDisposable { private IProperty _selectedItemProperty; - private IReadOnlyProperty> _itemsSource; - private PropertyBindingData _selectedItemBindingData; + private PropertyBindingData _itemsSourceBindingData; + private IReadOnlyProperty> _itemsSource; public void Initialize() { @@ -27,44 +27,74 @@ public void Initialize() public void Dispose() { - choices.Clear(); + RemoveAllItems(); } - + public void SetBindingContext(IBindingContext context, IObjectProvider objectProvider) { - if (string.IsNullOrWhiteSpace(BindingItemsSourcePath) == false) + if (string.IsNullOrWhiteSpace(BindingItemsSourcePath)) { - _itemsSourceBindingData ??= BindingItemsSourcePath.ToPropertyBindingData(); - _itemsSource = objectProvider - .RentReadOnlyProperty>(context, _itemsSourceBindingData); - _itemsSource.Value.CollectionChanged += OnItemsCollectionChanged; - choices = new List(_itemsSource.Value); + return; } - + + _itemsSourceBindingData ??= BindingItemsSourcePath.ToPropertyBindingData(); + + _itemsSource = + objectProvider.RentReadOnlyProperty>(context, _itemsSourceBindingData); + _itemsSource.Value.CollectionChanged += OnItemsCollectionChanged; + + AddItems(_itemsSource.Value); + if (string.IsNullOrWhiteSpace(BindingSelectedItemPath) == false) { _selectedItemBindingData ??= BindingSelectedItemPath.ToPropertyBindingData(); + _selectedItemProperty = objectProvider.RentProperty(context, _selectedItemBindingData); - _selectedItemProperty.ValueChanged += OnSelectedItemValueChanged; - - var isContains = choices.Contains(_selectedItemProperty.Value); - if (isContains == true) + + if (string.IsNullOrWhiteSpace(_selectedItemProperty.Value)) + { + _selectedItemProperty.Value = choices.Count > 0 ? choices[0] : default; + } + else { UpdateControlValue(_selectedItemProperty.Value); } - - this.RegisterValueChangedCallback(OnControlValueChanged); - _selectedItemProperty.Value = choices.Count > 0 ? choices[0] : default; + + _selectedItemProperty.ValueChanged += OnSelectedItemValueChanged; + this.RegisterValueChangedCallback(OnControlSelectedValueChanged); } } + public virtual void ResetBindingContext(IObjectProvider objectProvider) + { + if (_itemsSource is null) + { + return; + } + + _itemsSource.Value.CollectionChanged -= OnItemsCollectionChanged; + objectProvider.ReturnReadOnlyProperty(_itemsSource); + _itemsSource = null; + + if (_selectedItemProperty is not null) + { + _selectedItemProperty.ValueChanged -= OnSelectedItemValueChanged; + objectProvider.ReturnProperty(_selectedItemProperty); + _selectedItemProperty = null; + + this.UnregisterValueChangedCallback(OnControlSelectedValueChanged); + } + + RemoveAllItems(); + } + protected virtual void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.Action == NotifyCollectionChangedAction.Add) { foreach (string newItem in e.NewItems) { - choices.Add(newItem); + AddItem(newItem); } } @@ -72,57 +102,80 @@ protected virtual void OnItemsCollectionChanged(object sender, NotifyCollectionC { foreach (string oldItem in e.OldItems) { - choices.Remove(oldItem); + RemoveItem(oldItem); } } - + if (e.Action == NotifyCollectionChangedAction.Reset) { - choices.Clear(); + if (_itemsSource.Value.Count == 0) + { + RemoveAllItems(); + } + else + { + throw new InvalidOperationException("Action not supported."); + } } } - public virtual void ResetBindingContext(IObjectProvider objectProvider) + protected virtual void OnControlSelectedValueChanged(ChangeEvent e) + { + _selectedItemProperty.Value = e.newValue; + } + + private void OnSelectedItemValueChanged(object sender, string newValue) { - if (_selectedItemProperty != null) + UpdateControlValue(newValue); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected virtual void UpdateControlValue(string selectedItem) + { + if (choices.Any() && choices.Contains(selectedItem) == false) { - _selectedItemProperty.ValueChanged -= OnSelectedItemValueChanged; - objectProvider.ReturnProperty(_selectedItemProperty); - _selectedItemProperty = null; - this.UnregisterValueChangedCallback(OnControlValueChanged); + throw new InvalidOperationException($"\"{selectedItem}\" is not presented in the collection."); } - - if (_itemsSource != null) + + SetValueWithoutNotify(selectedItem); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void AddItems(IEnumerable items) + { + foreach (var item in items) { - _itemsSource.Value.CollectionChanged -= OnItemsCollectionChanged; - choices = new List(); - objectProvider.ReturnReadOnlyProperty(_itemsSource); - _itemsSource = null; + AddItem(item); } - - UpdateControlValue(default); } - protected virtual void OnControlValueChanged(ChangeEvent e) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void AddItem(string item) { - _selectedItemProperty.Value = e.newValue; + if (string.IsNullOrWhiteSpace(item)) + { + throw new NullReferenceException("Item cannot be null or empty."); + } + + choices.Add(item); } - private void OnSelectedItemValueChanged(object sender, string newValue) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void RemoveItem(string item) { - var isContains = choices.Contains(newValue); - if (isContains == false) + choices.Remove(item); + + if (value == item) { - return; + value = choices.Count == 0 ? default : choices[^1]; } - - UpdateControlValue(newValue); } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected virtual void UpdateControlValue(string newValue) + private void RemoveAllItems() { - SetValueWithoutNotify(newValue); + choices.Clear(); + value = default; } } } \ No newline at end of file diff --git a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/Uxmls/BindableDropdownField.Uxml.cs b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/Uxmls/BindableDropdownField.Uxml.cs index f89007b..ca8ad5d 100644 --- a/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/Uxmls/BindableDropdownField.Uxml.cs +++ b/src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit/Runtime/UITK/BindableUIElements/Uxmls/BindableDropdownField.Uxml.cs @@ -5,9 +5,8 @@ namespace UnityMvvmToolkit.UITK.BindableUIElements { partial class BindableDropdownField { - public string BindingSelectedItemPath { get; private set; } - public string BindingItemsSourcePath { get; private set; } + public string BindingSelectedItemPath { get; private set; } public new class UxmlFactory : UxmlFactory { @@ -19,8 +18,8 @@ partial class BindableDropdownField { // ReSharper disable once InconsistentNaming #pragma warning disable 649 + [UnityEngine.SerializeField] private string BindingItemsSourcePath; [UnityEngine.SerializeField] private string BindingSelectedItemPath; - [UnityEngine.SerializeField] private string BindingItemsSourcePath; #pragma warning restore 649 public override object CreateInstance() => new BindableDropdownField(); @@ -29,27 +28,26 @@ public override void Deserialize(object visualElement) base.Deserialize(visualElement); var bindableDropdownField = visualElement.As(); - bindableDropdownField.BindingSelectedItemPath = BindingSelectedItemPath; bindableDropdownField.BindingItemsSourcePath = BindingItemsSourcePath; + bindableDropdownField.BindingSelectedItemPath = BindingSelectedItemPath; } } #else public new class UxmlTraits : DropdownField.UxmlTraits { - private readonly UxmlStringAttributeDescription _bindingSelectedItemPath = new() - { name = "binding-selected-item-path", defaultValue = "" }; - private readonly UxmlStringAttributeDescription _bindingItemsSourcePath = new() { name = "binding-items-source-path", defaultValue = "" }; + private readonly UxmlStringAttributeDescription _bindingSelectedItemPath = new() + { name = "binding-selected-item-path", defaultValue = "" }; + public override void Init(VisualElement visualElement, IUxmlAttributes bag, CreationContext context) { base.Init(visualElement, bag, context); var bindableDropdownField = visualElement.As(); - - bindableDropdownField.BindingSelectedItemPath = _bindingSelectedItemPath.GetValueFromBag(bag, context); bindableDropdownField.BindingItemsSourcePath = _bindingItemsSourcePath.GetValueFromBag(bag, context); + bindableDropdownField.BindingSelectedItemPath = _bindingSelectedItemPath.GetValueFromBag(bag, context); } } #endif From a8756354ac682b503fb4f765fc348e978d16698d Mon Sep 17 00:00:00 2001 From: Dima Date: Fri, 30 Jun 2023 13:55:42 +0800 Subject: [PATCH 8/8] Update readme file. --- README.md | 44 +++++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index dad7b2f..355a3ed 100644 --- a/README.md +++ b/README.md @@ -770,6 +770,7 @@ The included UI elements are: - [BindableLabel](#bindablelabel) - [BindableTextField](#bindabletextfield) - [BindableButton](#bindablebutton) +- [BindableDropdownField](#bindabledropdownfield) - [BindableListView](#bindablelistview) - [BindableScrollView](#bindablescrollview) - [BindingContextProvider](#bindingcontextprovider) @@ -842,42 +843,35 @@ To pass a parameter to the viewmodel, see the [ParameterValueConverter](#paramet #### BindableDropdownField -The `BindableDropdownField` allows you to work with dropdown. To set the binding of the selected value, you need to write `binding-selected-item-path`, and to set the binding of all dropdown elements, you need to use `binding-items-source-path`. -Moreover, you can set them independently of each other. - -The following example demonstrates how to bind to a collection of strings with `BindableDropdownField`. - -In XML, you need to write the following: - -```xml - - - -``` -And in the C# class the following: +The `BindableDropdownField` allows the user to pick a choice from a list of options. The `BindingSelectedItemPath` attribute is optional. ```csharp public class DropdownFieldViewModel : IBindingContext { public DropdownFieldViewModel() { - TextValues = new ReadOnlyProperty>(new ObservableCollection() + var items = new ObservableCollection { - "Value1", - "Value2", - "Value3" - }); - - SelectedValue = new Property(); - SelectedValue.Value = "Value1"; + "Value 1", + "Value 2", + "Value 3" + }; + + Items = new ReadOnlyProperty>(items); + SelectedItem = new Property("Value 1"); } - - public IProperty SelectedValue { get; } - - public IReadOnlyProperty> Choices { get; } + + public IReadOnlyProperty> Items { get; } + public IProperty SelectedItem { get; } } ``` +```xml + + + +``` + #### BindableListView The `BindableListView` control is the most efficient way to create lists. It uses virtualization and creates VisualElements only for visible items. Use the `binding-items-source-path` of the `BindableListView` to bind to an `ObservableCollection`.