Skip to content

Commit 61c79fa

Browse files
committed
Update after #18405 api review
1 parent fbb270d commit 61c79fa

File tree

3 files changed

+49
-32
lines changed

3 files changed

+49
-32
lines changed

samples/ControlCatalog/Pages/ComboBoxPage.xaml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,12 +135,14 @@
135135
</ComboBox.ItemTemplate>
136136
</ComboBox>
137137

138-
<StackPanel Spacing="10">
138+
<StackPanel Spacing="5">
139139
<ComboBox WrapSelection="{Binding WrapSelection}" PlaceholderText="Editable"
140140
ItemsSource="{Binding Values}" DisplayMemberBinding="{Binding Name}"
141-
IsEditable="True" Text="{Binding TextValue}" ItemTextBinding="{Binding Name}"
141+
IsEditable="True" Text="{Binding TextValue}"
142+
TextSearch.TextBinding="{Binding SearchText, DataType=viewModels:IdAndName}"
142143
SelectedItem="{Binding SelectedItem}" />
143144

145+
<TextBlock Text="Editable text is bound to SearchText. Display is bound to Name" />
144146
<TextBlock Text="{Binding TextValue, StringFormat=Text Value: {0}}" />
145147
<TextBlock Text="{Binding SelectedItem.Name, StringFormat=Selected Item: {0}}" />
146148
</StackPanel>

src/Avalonia.Controls/ComboBox.cs

Lines changed: 35 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,14 @@
55
using Avalonia.Controls.Primitives;
66
using Avalonia.Controls.Shapes;
77
using Avalonia.Controls.Templates;
8+
using Avalonia.Controls.Utils;
89
using Avalonia.Data;
910
using Avalonia.Input;
10-
using Avalonia.Interactivity;
1111
using Avalonia.Layout;
1212
using Avalonia.Media;
1313
using Avalonia.Metadata;
1414
using Avalonia.Reactive;
1515
using Avalonia.VisualTree;
16-
using static Avalonia.Controls.AutoCompleteBox;
1716

1817
namespace Avalonia.Controls
1918
{
@@ -89,12 +88,6 @@ public class ComboBox : SelectingItemsControl
8988
public static readonly StyledProperty<string?> TextProperty =
9089
TextBlock.TextProperty.AddOwner<ComboBox>(new(string.Empty, BindingMode.TwoWay));
9190

92-
/// <summary>
93-
/// Defines the <see cref="ItemTextBinding"/> property.
94-
/// </summary>
95-
public static readonly StyledProperty<IBinding?> ItemTextBindingProperty =
96-
AvaloniaProperty.Register<ComboBox, IBinding?>(nameof(ItemTextBinding));
97-
9891
/// <summary>
9992
/// Defines the <see cref="SelectionBoxItemTemplate"/> property.
10093
/// </summary>
@@ -118,7 +111,7 @@ public class ComboBox : SelectingItemsControl
118111

119112
private bool _isEditable;
120113
private TextBox? _inputText;
121-
private BindingEvaluator<string>? _textValueBindingEvaluator = null;
114+
private BindingEvaluator<string?>? _textValueBindingEvaluator = null;
122115

123116
/// <summary>
124117
/// Initializes static members of the <see cref="ComboBox"/> class.
@@ -129,7 +122,8 @@ static ComboBox()
129122
FocusableProperty.OverrideDefaultValue<ComboBox>(true);
130123
IsTextSearchEnabledProperty.OverrideDefaultValue<ComboBox>(true);
131124
TextProperty.Changed.AddClassHandler<ComboBox>((x, e) => x.TextChanged(e));
132-
ItemTextBindingProperty.Changed.AddClassHandler<ComboBox>((x, e) => x.ItemTextBindingChanged(e));
125+
DisplayMemberBindingProperty.Changed.AddClassHandler<ComboBox>((x, e) => x.DisplayMemberBindingChanged(e));
126+
TextSearch.TextBindingProperty.Changed.AddClassHandler<ComboBox>((x, e) => x.ItemTextBindingChanged(e));
133127
//when the items change we need to simulate a text change to validate the text being an item or not and selecting it
134128
ItemsSourceProperty.Changed.AddClassHandler<ComboBox>((x, e) => x.TextChanged(
135129
new AvaloniaPropertyChangedEventArgs<string?>(e.Sender, TextProperty, x.Text, x.Text, e.Priority)));
@@ -236,19 +230,6 @@ public string? Text
236230
set => SetValue(TextProperty, value);
237231
}
238232

239-
/// <summary>
240-
/// Gets or sets the <see cref="T:Avalonia.Data.Binding" /> that
241-
/// is used to get the text for editing of an item.
242-
/// </summary>
243-
/// <value>The <see cref="T:Avalonia.Data.IBinding" /> object used
244-
/// when binding to a collection property.</value>
245-
[AssignBinding, InheritDataTypeFromItems(nameof(ItemsSource), AncestorType = typeof(ComboBox))]
246-
public IBinding? ItemTextBinding
247-
{
248-
get => GetValue(ItemTextBindingProperty);
249-
set => SetValue(ItemTextBindingProperty, value);
250-
}
251-
252233
protected override void OnInitialized()
253234
{
254235
EnsureTextValueBinderOrThrow();
@@ -592,6 +573,9 @@ private void UpdateFlowDirection()
592573

593574
private void UpdateInputTextFromSelection(object? item)
594575
{
576+
//if we are modifying the text box which has deselected a value we don't want to update the textbox value
577+
if (_skipNextTextChanged)
578+
return;
595579
SetCurrentValue(TextProperty, GetItemTextValue(item));
596580
}
597581

@@ -655,22 +639,43 @@ public void Clear()
655639
SelectedIndex = -1;
656640
}
657641

658-
private void ItemTextBindingChanged(AvaloniaPropertyChangedEventArgs e)
642+
private void ItemTextBindingChanged(AvaloniaPropertyChangedEventArgs e)
643+
=> HandleTextValueBindingValueChanged(e, null);
644+
645+
private void DisplayMemberBindingChanged(AvaloniaPropertyChangedEventArgs e)
646+
=> HandleTextValueBindingValueChanged(null, e);
647+
648+
private void HandleTextValueBindingValueChanged(AvaloniaPropertyChangedEventArgs? textSearchPropChange,
649+
AvaloniaPropertyChangedEventArgs? displayMemberPropChange)
659650
{
660-
_textValueBindingEvaluator = e.NewValue is IBinding binding
661-
? new(binding) : null;
651+
IBinding? textValueBinding;
652+
//prioritise using the TextSearch.TextBindingProperty if possible
653+
if (textSearchPropChange == null && TextSearch.GetTextBinding(this) is IBinding textSearchBinding)
654+
textValueBinding = textSearchBinding;
655+
656+
else if (textSearchPropChange != null && textSearchPropChange.NewValue is IBinding eventTextSearchBinding)
657+
textValueBinding = eventTextSearchBinding;
658+
659+
else if (displayMemberPropChange != null && displayMemberPropChange.NewValue is IBinding eventDisplayMemberBinding)
660+
textValueBinding = eventDisplayMemberBinding;
661+
662+
else
663+
textValueBinding = null;
662664

663-
if(IsInitialized)
665+
_textValueBindingEvaluator = BindingEvaluator<string?>.TryCreate(textValueBinding);
666+
667+
if (IsInitialized)
664668
EnsureTextValueBinderOrThrow();
665669

666-
if(_textValueBindingEvaluator != null)
670+
//if the binding is set we want to set the initial value for the selected item so the text box has the correct value
671+
if (_textValueBindingEvaluator != null)
667672
_textValueBindingEvaluator.Value = GetItemTextValue(SelectedValue);
668673
}
669674

670675
private void EnsureTextValueBinderOrThrow()
671676
{
672677
if (IsEditable && _textValueBindingEvaluator == null)
673-
throw new InvalidOperationException($"When {nameof(ComboBox)}.{nameof(IsEditable)} is true you must set the text value binding using {nameof(ItemTextBinding)}");
678+
throw new InvalidOperationException($"When {nameof(ComboBox)}.{nameof(IsEditable)} is true you must either set {nameof(ComboBox)}.{nameof(DisplayMemberBinding)} or set the text value binding using attached property {nameof(TextSearch)}.{nameof(TextSearch.TextBindingProperty)}");
674679
}
675680

676681
private bool _skipNextTextChanged = false;
@@ -706,7 +711,7 @@ private string GetItemTextValue(object? item)
706711
if (_textValueBindingEvaluator == null)
707712
return string.Empty;
708713

709-
return _textValueBindingEvaluator.GetDynamicValue(item) ?? item?.ToString() ?? string.Empty;
714+
return TextSearch.GetEffectiveText(item, _textValueBindingEvaluator);
710715
}
711716
}
712717
}

src/Avalonia.Controls/Utils/BindingEvaluator.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@ internal sealed class BindingEvaluator<T> : StyledElement, IDisposable
1919
public static readonly StyledProperty<T> ValueProperty =
2020
AvaloniaProperty.Register<BindingEvaluator<T>, T>("Value");
2121

22+
/// <summary>
23+
/// Gets or sets the data item value.
24+
/// </summary>
25+
public T Value
26+
{
27+
get => GetValue(ValueProperty);
28+
set => SetValue(ValueProperty, value);
29+
}
30+
2231
public T Evaluate(object? dataContext)
2332
{
2433
// Only update the DataContext if necessary
@@ -49,6 +58,7 @@ public void Dispose()
4958
DataContext = null;
5059
}
5160

61+
[return: NotNullIfNotNull(nameof(binding))]
5262
public static BindingEvaluator<T>? TryCreate(IBinding? binding)
5363
{
5464
if (binding is null)

0 commit comments

Comments
 (0)