Merge pull request #268 from irihitech/taginput
Improve Taginput: Support MaxCount, AcceptReturn and Watermark
This commit is contained in:
@@ -17,6 +17,7 @@
|
|||||||
Margin="20"
|
Margin="20"
|
||||||
AllowDuplicates="True"
|
AllowDuplicates="True"
|
||||||
Separator="-"
|
Separator="-"
|
||||||
|
Watermark="Hello world"
|
||||||
Tags="{Binding Tags}" />
|
Tags="{Binding Tags}" />
|
||||||
<u:TagInput
|
<u:TagInput
|
||||||
Margin="20"
|
Margin="20"
|
||||||
@@ -25,5 +26,13 @@
|
|||||||
Separator="-"
|
Separator="-"
|
||||||
Tags="{Binding DistinctTags}" />
|
Tags="{Binding DistinctTags}" />
|
||||||
<ListBox ItemsSource="{Binding DistinctTags}" />
|
<ListBox ItemsSource="{Binding DistinctTags}" />
|
||||||
|
|
||||||
|
<u:TagInput
|
||||||
|
Margin="20"
|
||||||
|
AllowDuplicates="False"
|
||||||
|
AcceptsReturn="True"
|
||||||
|
LostFocusBehavior="Clear"
|
||||||
|
Separator="-"
|
||||||
|
Tags="{Binding DistinctTags}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -25,6 +25,12 @@
|
|||||||
BorderThickness="1"
|
BorderThickness="1"
|
||||||
CornerRadius="3">
|
CornerRadius="3">
|
||||||
<Panel HorizontalAlignment="Stretch">
|
<Panel HorizontalAlignment="Stretch">
|
||||||
|
<TextBlock
|
||||||
|
Name="{x:Static u:TagInput.PART_Watermark}"
|
||||||
|
Opacity="0.5"
|
||||||
|
IsVisible="False"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{TemplateBinding Watermark}" />
|
||||||
<ItemsControl
|
<ItemsControl
|
||||||
Name="PART_ItemsControl"
|
Name="PART_ItemsControl"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
@@ -42,6 +48,9 @@
|
|||||||
</Border>
|
</Border>
|
||||||
</ControlTemplate>
|
</ControlTemplate>
|
||||||
</Setter>
|
</Setter>
|
||||||
|
<Style Selector="^:empty /template/ TextBlock#PART_Watermark">
|
||||||
|
<Setter Property="IsVisible" Value="True"></Setter>
|
||||||
|
</Style>
|
||||||
<Style Selector="^:pointerover /template/ Border#PART_BackgroundBorder">
|
<Style Selector="^:pointerover /template/ Border#PART_BackgroundBorder">
|
||||||
<Setter Property="Border.Background" Value="{DynamicResource TextBoxPointeroverBackground}" />
|
<Setter Property="Border.Background" Value="{DynamicResource TextBoxPointeroverBackground}" />
|
||||||
</Style>
|
</Style>
|
||||||
@@ -123,10 +132,10 @@
|
|||||||
Foreground="{TemplateBinding Foreground}" />
|
Foreground="{TemplateBinding Foreground}" />
|
||||||
<ContentPresenter
|
<ContentPresenter
|
||||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||||
FontSize="12"
|
|
||||||
Foreground="{TemplateBinding Foreground}"
|
|
||||||
Content="{TemplateBinding Content}"
|
Content="{TemplateBinding Content}"
|
||||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||||
|
FontSize="12"
|
||||||
|
Foreground="{TemplateBinding Foreground}"
|
||||||
TextTrimming="CharacterEllipsis" />
|
TextTrimming="CharacterEllipsis" />
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|||||||
@@ -10,45 +10,89 @@ using Avalonia.Controls.Primitives;
|
|||||||
using Avalonia.Controls.Templates;
|
using Avalonia.Controls.Templates;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Layout;
|
|
||||||
using Avalonia.Styling;
|
using Avalonia.Styling;
|
||||||
|
using Irihi.Avalonia.Shared.Common;
|
||||||
|
using Irihi.Avalonia.Shared.Helpers;
|
||||||
|
|
||||||
namespace Ursa.Controls;
|
namespace Ursa.Controls;
|
||||||
|
|
||||||
[TemplatePart(PART_ItemsControl, typeof(ItemsControl))]
|
[TemplatePart(PART_ItemsControl, typeof(ItemsControl))]
|
||||||
|
[TemplatePart(PART_Watermark, typeof(Visual))]
|
||||||
|
[PseudoClasses(PseudoClassName.PC_Empty)]
|
||||||
public class TagInput : TemplatedControl
|
public class TagInput : TemplatedControl
|
||||||
{
|
{
|
||||||
public const string PART_ItemsControl = "PART_ItemsControl";
|
public const string PART_ItemsControl = "PART_ItemsControl";
|
||||||
|
public const string PART_Watermark = "PART_Watermark";
|
||||||
private readonly TextBox _textBox;
|
|
||||||
private ItemsControl? _itemsControl;
|
|
||||||
|
|
||||||
|
|
||||||
public static readonly StyledProperty<IList<string>> TagsProperty =
|
public static readonly StyledProperty<IList<string>> TagsProperty =
|
||||||
AvaloniaProperty.Register<TagInput, IList<string>>(
|
AvaloniaProperty.Register<TagInput, IList<string>>(
|
||||||
nameof(Tags));
|
nameof(Tags));
|
||||||
|
|
||||||
public IList<string> Tags
|
public static readonly StyledProperty<string?> WatermarkProperty = TextBox.WatermarkProperty.AddOwner<TagInput>();
|
||||||
|
|
||||||
|
|
||||||
|
public static readonly StyledProperty<bool> AcceptsReturnProperty =
|
||||||
|
TextBox.AcceptsReturnProperty.AddOwner<TagInput>();
|
||||||
|
|
||||||
|
public bool AcceptsReturn
|
||||||
{
|
{
|
||||||
get => GetValue(TagsProperty);
|
get => GetValue(AcceptsReturnProperty);
|
||||||
set => SetValue(TagsProperty, value);
|
set => SetValue(AcceptsReturnProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<int> MaxCountProperty = AvaloniaProperty.Register<TagInput, int>(
|
||||||
|
nameof(MaxCount), int.MaxValue);
|
||||||
|
|
||||||
public static readonly DirectProperty<TagInput, IList> ItemsProperty =
|
public static readonly DirectProperty<TagInput, IList> ItemsProperty =
|
||||||
AvaloniaProperty.RegisterDirect<TagInput, IList>(
|
AvaloniaProperty.RegisterDirect<TagInput, IList>(
|
||||||
nameof(Items), o => o.Items);
|
nameof(Items), o => o.Items);
|
||||||
|
|
||||||
private IList _items;
|
public static readonly StyledProperty<ControlTheme> InputThemeProperty =
|
||||||
|
AvaloniaProperty.Register<TagInput, ControlTheme>(
|
||||||
|
nameof(InputTheme));
|
||||||
|
|
||||||
public IList Items
|
public static readonly StyledProperty<IDataTemplate?> ItemTemplateProperty =
|
||||||
|
AvaloniaProperty.Register<TagInput, IDataTemplate?>(
|
||||||
|
nameof(ItemTemplate));
|
||||||
|
|
||||||
|
public static readonly StyledProperty<string> SeparatorProperty = AvaloniaProperty.Register<TagInput, string>(
|
||||||
|
nameof(Separator));
|
||||||
|
|
||||||
|
public static readonly StyledProperty<LostFocusBehavior> LostFocusBehaviorProperty =
|
||||||
|
AvaloniaProperty.Register<TagInput, LostFocusBehavior>(
|
||||||
|
nameof(LostFocusBehavior));
|
||||||
|
|
||||||
|
|
||||||
|
public static readonly StyledProperty<bool> AllowDuplicatesProperty = AvaloniaProperty.Register<TagInput, bool>(
|
||||||
|
nameof(AllowDuplicates), true);
|
||||||
|
|
||||||
|
public static readonly StyledProperty<object?> InnerLeftContentProperty =
|
||||||
|
AvaloniaProperty.Register<TagInput, object?>(
|
||||||
|
nameof(InnerLeftContent));
|
||||||
|
|
||||||
|
public static readonly StyledProperty<object?> InnerRightContentProperty =
|
||||||
|
AvaloniaProperty.Register<TagInput, object?>(
|
||||||
|
nameof(InnerRightContent));
|
||||||
|
|
||||||
|
private readonly TextBox _textBox;
|
||||||
|
|
||||||
|
private IList _items = null!;
|
||||||
|
private ItemsControl? _itemsControl;
|
||||||
|
|
||||||
|
private TextPresenter? _presenter;
|
||||||
|
private Visual? _watermark;
|
||||||
|
|
||||||
|
|
||||||
|
static TagInput()
|
||||||
{
|
{
|
||||||
get => _items;
|
InputThemeProperty.Changed.AddClassHandler<TagInput>((o, e) => o.OnInputThemePropertyChanged(e));
|
||||||
private set => SetAndRaise(ItemsProperty, ref _items, value);
|
TagsProperty.Changed.AddClassHandler<TagInput>((o, e) => o.OnTagsPropertyChanged(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
public TagInput()
|
public TagInput()
|
||||||
{
|
{
|
||||||
_textBox = new TextBox();
|
_textBox = new TextBox();
|
||||||
|
_textBox[!AcceptsReturnProperty] = this.GetObservable(AcceptsReturnProperty).ToBinding();
|
||||||
_textBox.AddHandler(KeyDownEvent, OnTextBoxKeyDown, RoutingStrategies.Tunnel);
|
_textBox.AddHandler(KeyDownEvent, OnTextBoxKeyDown, RoutingStrategies.Tunnel);
|
||||||
_textBox.AddHandler(LostFocusEvent, OnTextBox_LostFocus, RoutingStrategies.Bubble);
|
_textBox.AddHandler(LostFocusEvent, OnTextBox_LostFocus, RoutingStrategies.Bubble);
|
||||||
Items = new AvaloniaList<object>
|
Items = new AvaloniaList<object>
|
||||||
@@ -58,22 +102,29 @@ public class TagInput : TemplatedControl
|
|||||||
Tags = new ObservableCollection<string>();
|
Tags = new ObservableCollection<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTextBox_LostFocus(object? sender, RoutedEventArgs e)
|
public string? Watermark
|
||||||
{
|
{
|
||||||
switch (LostFocusBehavior)
|
get => GetValue(WatermarkProperty);
|
||||||
{
|
set => SetValue(WatermarkProperty, value);
|
||||||
case LostFocusBehavior.Add:
|
|
||||||
AddTags();
|
|
||||||
break;
|
|
||||||
case LostFocusBehavior.Clear:
|
|
||||||
_textBox.Text = "";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly StyledProperty<ControlTheme> InputThemeProperty =
|
public IList<string> Tags
|
||||||
AvaloniaProperty.Register<TagInput, ControlTheme>(
|
{
|
||||||
nameof(InputTheme));
|
get => GetValue(TagsProperty);
|
||||||
|
set => SetValue(TagsProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int MaxCount
|
||||||
|
{
|
||||||
|
get => GetValue(MaxCountProperty);
|
||||||
|
set => SetValue(MaxCountProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IList Items
|
||||||
|
{
|
||||||
|
get => _items;
|
||||||
|
private set => SetAndRaise(ItemsProperty, ref _items, value);
|
||||||
|
}
|
||||||
|
|
||||||
public ControlTheme InputTheme
|
public ControlTheme InputTheme
|
||||||
{
|
{
|
||||||
@@ -81,91 +132,93 @@ public class TagInput : TemplatedControl
|
|||||||
set => SetValue(InputThemeProperty, value);
|
set => SetValue(InputThemeProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly StyledProperty<IDataTemplate?> ItemTemplateProperty =
|
|
||||||
AvaloniaProperty.Register<TagInput, IDataTemplate?>(
|
|
||||||
nameof(ItemTemplate));
|
|
||||||
|
|
||||||
public IDataTemplate? ItemTemplate
|
public IDataTemplate? ItemTemplate
|
||||||
{
|
{
|
||||||
get => GetValue(ItemTemplateProperty);
|
get => GetValue(ItemTemplateProperty);
|
||||||
set => SetValue(ItemTemplateProperty, value);
|
set => SetValue(ItemTemplateProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly StyledProperty<string> SeparatorProperty = AvaloniaProperty.Register<TagInput, string>(
|
|
||||||
nameof(Separator));
|
|
||||||
|
|
||||||
public string Separator
|
public string Separator
|
||||||
{
|
{
|
||||||
get => GetValue(SeparatorProperty);
|
get => GetValue(SeparatorProperty);
|
||||||
set => SetValue(SeparatorProperty, value);
|
set => SetValue(SeparatorProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly StyledProperty<LostFocusBehavior> LostFocusBehaviorProperty = AvaloniaProperty.Register<TagInput, LostFocusBehavior>(
|
|
||||||
nameof(LostFocusBehavior));
|
|
||||||
|
|
||||||
public LostFocusBehavior LostFocusBehavior
|
public LostFocusBehavior LostFocusBehavior
|
||||||
{
|
{
|
||||||
get => GetValue(LostFocusBehaviorProperty);
|
get => GetValue(LostFocusBehaviorProperty);
|
||||||
set => SetValue(LostFocusBehaviorProperty, value);
|
set => SetValue(LostFocusBehaviorProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static readonly StyledProperty<bool> AllowDuplicatesProperty = AvaloniaProperty.Register<TagInput, bool>(
|
|
||||||
nameof(AllowDuplicates), defaultValue: true);
|
|
||||||
|
|
||||||
public bool AllowDuplicates
|
public bool AllowDuplicates
|
||||||
{
|
{
|
||||||
get => GetValue(AllowDuplicatesProperty);
|
get => GetValue(AllowDuplicatesProperty);
|
||||||
set => SetValue(AllowDuplicatesProperty, value);
|
set => SetValue(AllowDuplicatesProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly StyledProperty<object?> InnerLeftContentProperty =
|
|
||||||
AvaloniaProperty.Register<TagInput, object?>(
|
|
||||||
nameof(InnerLeftContent));
|
|
||||||
|
|
||||||
public object? InnerLeftContent
|
public object? InnerLeftContent
|
||||||
{
|
{
|
||||||
get => GetValue(InnerLeftContentProperty);
|
get => GetValue(InnerLeftContentProperty);
|
||||||
set => SetValue(InnerLeftContentProperty, value);
|
set => SetValue(InnerLeftContentProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly StyledProperty<object?> InnerRightContentProperty =
|
|
||||||
AvaloniaProperty.Register<TagInput, object?>(
|
|
||||||
nameof(InnerRightContent));
|
|
||||||
|
|
||||||
public object? InnerRightContent
|
public object? InnerRightContent
|
||||||
{
|
{
|
||||||
get => GetValue(InnerRightContentProperty);
|
get => GetValue(InnerRightContentProperty);
|
||||||
set => SetValue(InnerRightContentProperty, value);
|
set => SetValue(InnerRightContentProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnTextBox_LostFocus(object? sender, RoutedEventArgs e)
|
||||||
static TagInput()
|
|
||||||
{
|
{
|
||||||
InputThemeProperty.Changed.AddClassHandler<TagInput>((o, e) => o.OnInputThemePropertyChanged(e));
|
switch (LostFocusBehavior)
|
||||||
TagsProperty.Changed.AddClassHandler<TagInput>((o, e) => o.OnTagsPropertyChanged(e));
|
{
|
||||||
|
case LostFocusBehavior.Add:
|
||||||
|
AddTags(_textBox.Text);
|
||||||
|
break;
|
||||||
|
case LostFocusBehavior.Clear:
|
||||||
|
_textBox.Text = "";
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnApplyTemplate(e);
|
base.OnApplyTemplate(e);
|
||||||
_itemsControl = e.NameScope.Find<ItemsControl>(PART_ItemsControl);
|
_itemsControl = e.NameScope.Find<ItemsControl>(PART_ItemsControl);
|
||||||
|
_watermark = e.NameScope.Find<Visual>(PART_Watermark);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnLoaded(RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnLoaded(e);
|
||||||
|
if (_watermark is null) return;
|
||||||
|
_presenter = _textBox.GetTemplateChildren().OfType<TextPresenter>().FirstOrDefault();
|
||||||
|
_presenter?.GetObservable(TextPresenter.PreeditTextProperty).Subscribe(_ => CheckEmpty());
|
||||||
|
_textBox.GetObservable(TextBox.TextProperty).Subscribe(_ => CheckEmpty());
|
||||||
|
if (Tags is INotifyCollectionChanged incc)
|
||||||
|
incc.GetWeakCollectionChangedObservable().Subscribe(_ => CheckEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnInputThemePropertyChanged(AvaloniaPropertyChangedEventArgs args)
|
private void OnInputThemePropertyChanged(AvaloniaPropertyChangedEventArgs args)
|
||||||
{
|
{
|
||||||
var newTheme = args.GetNewValue<ControlTheme>();
|
var newTheme = args.GetNewValue<ControlTheme?>();
|
||||||
if (newTheme?.TargetType == typeof(TextBox))
|
if (newTheme?.TargetType == typeof(TextBox)) _textBox.Theme = newTheme;
|
||||||
{
|
}
|
||||||
_textBox.Theme = newTheme;
|
|
||||||
}
|
private void CheckEmpty()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(_presenter?.PreeditText) && string.IsNullOrEmpty(_textBox.Text) &&
|
||||||
|
Tags.Count == 0)
|
||||||
|
PseudoClasses.Set(PseudoClassName.PC_Empty, true);
|
||||||
|
else
|
||||||
|
PseudoClasses.Set(PseudoClassName.PC_Empty, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTagsPropertyChanged(AvaloniaPropertyChangedEventArgs args)
|
private void OnTagsPropertyChanged(AvaloniaPropertyChangedEventArgs args)
|
||||||
{
|
{
|
||||||
var newTags = args.GetNewValue<IList<string>>();
|
var newTags = args.GetNewValue<IList<string>?>();
|
||||||
var oldTags = args.GetOldValue<IList<string>>();
|
var oldTags = args.GetOldValue<IList<string>?>();
|
||||||
|
|
||||||
if (Items is AvaloniaList<object> avaloniaList)
|
if (Items is AvaloniaList<object> avaloniaList)
|
||||||
{
|
{
|
||||||
avaloniaList.RemoveRange(0, avaloniaList.Count - 1);
|
avaloniaList.RemoveRange(0, avaloniaList.Count - 1);
|
||||||
@@ -175,23 +228,13 @@ public class TagInput : TemplatedControl
|
|||||||
Items.Clear();
|
Items.Clear();
|
||||||
Items.Add(_textBox);
|
Items.Add(_textBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newTags != null)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < newTags.Count; i++)
|
|
||||||
{
|
|
||||||
Items.Insert(Items.Count - 1, newTags[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldTags is INotifyCollectionChanged inccold)
|
|
||||||
{
|
|
||||||
inccold.CollectionChanged-= OnCollectionChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Tags is INotifyCollectionChanged incc)
|
if (newTags != null)
|
||||||
{
|
for (var i = 0; i < newTags.Count; i++)
|
||||||
incc.CollectionChanged += OnCollectionChanged;
|
Items.Insert(Items.Count - 1, newTags[i]);
|
||||||
}
|
if (oldTags is INotifyCollectionChanged inccold) inccold.CollectionChanged -= OnCollectionChanged;
|
||||||
|
|
||||||
|
if (Tags is INotifyCollectionChanged incc) incc.CollectionChanged += OnCollectionChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||||
@@ -199,27 +242,21 @@ public class TagInput : TemplatedControl
|
|||||||
if (e.Action == NotifyCollectionChangedAction.Add)
|
if (e.Action == NotifyCollectionChangedAction.Add)
|
||||||
{
|
{
|
||||||
var items = e.NewItems;
|
var items = e.NewItems;
|
||||||
int index = e.NewStartingIndex;
|
var index = e.NewStartingIndex;
|
||||||
foreach (var item in items)
|
foreach (var item in items)
|
||||||
{
|
|
||||||
if (item is string s)
|
if (item is string s)
|
||||||
{
|
{
|
||||||
Items.Insert(index, s);
|
Items.Insert(index, s);
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (e.Action == NotifyCollectionChangedAction.Remove)
|
else if (e.Action == NotifyCollectionChangedAction.Remove)
|
||||||
{
|
{
|
||||||
var items = e.OldItems;
|
var items = e.OldItems;
|
||||||
int index = e.OldStartingIndex;
|
var index = e.OldStartingIndex;
|
||||||
foreach (var item in items)
|
foreach (var item in items)
|
||||||
{
|
if (item is string)
|
||||||
if (item is string s)
|
|
||||||
{
|
|
||||||
Items.RemoveAt(index);
|
Items.RemoveAt(index);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (e.Action == NotifyCollectionChangedAction.Reset)
|
else if (e.Action == NotifyCollectionChangedAction.Reset)
|
||||||
{
|
{
|
||||||
@@ -227,70 +264,66 @@ public class TagInput : TemplatedControl
|
|||||||
Items.Add(_textBox);
|
Items.Add(_textBox);
|
||||||
InvalidateVisual();
|
InvalidateVisual();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTextBoxKeyDown(object? sender, KeyEventArgs args)
|
private void OnTextBoxKeyDown(object? sender, KeyEventArgs args)
|
||||||
{
|
{
|
||||||
if (args.Key == Key.Enter)
|
if (!AcceptsReturn && args.Key == Key.Enter)
|
||||||
{
|
{
|
||||||
AddTags();
|
AddTags(_textBox.Text);
|
||||||
|
}
|
||||||
|
else if (AcceptsReturn && args.Key==Key.Enter)
|
||||||
|
{
|
||||||
|
var texts = _textBox.Text?.Split(["\r", "\n"], StringSplitOptions.RemoveEmptyEntries) ?? [];
|
||||||
|
foreach (var text in texts)
|
||||||
|
{
|
||||||
|
AddTags(text);
|
||||||
|
}
|
||||||
|
args.Handled = true;
|
||||||
}
|
}
|
||||||
else if (args.Key == Key.Delete || args.Key == Key.Back)
|
else if (args.Key == Key.Delete || args.Key == Key.Back)
|
||||||
{
|
if (string.IsNullOrEmpty(_textBox.Text) || _textBox.Text?.Length == 0)
|
||||||
if (string.IsNullOrEmpty(_textBox.Text)||_textBox.Text?.Length == 0)
|
|
||||||
{
|
{
|
||||||
if (Tags.Count == 0)
|
if (Tags.Count == 0) return;
|
||||||
{
|
var index = Items.Count - 2;
|
||||||
return;
|
|
||||||
}
|
|
||||||
int index = Items.Count - 2;
|
|
||||||
// Items.RemoveAt(index);
|
// Items.RemoveAt(index);
|
||||||
Tags.RemoveAt(index);
|
Tags.RemoveAt(index);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddTags()
|
private void AddTags(string? text)
|
||||||
{
|
{
|
||||||
if (!(_textBox.Text?.Length > 0)) return;
|
if (!(text?.Length > 0)) return;
|
||||||
|
if (Tags.Count >= MaxCount) return;
|
||||||
string[] values;
|
string[] values;
|
||||||
if (!string.IsNullOrEmpty(Separator))
|
if (!string.IsNullOrEmpty(Separator))
|
||||||
{
|
values = text.Split(new[] { Separator },
|
||||||
values = _textBox.Text.Split(new string[] { Separator },
|
|
||||||
StringSplitOptions.RemoveEmptyEntries);
|
StringSplitOptions.RemoveEmptyEntries);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
values = new[] { _textBox.Text };
|
values = new[] { _textBox.Text };
|
||||||
}
|
|
||||||
|
|
||||||
if (!AllowDuplicates && Tags != null)
|
if (!AllowDuplicates)
|
||||||
values = values.Distinct().Except(Tags).ToArray();
|
values = values.Distinct().Except(Tags).ToArray();
|
||||||
|
|
||||||
for (int i = 0; i < values.Length; i++)
|
for (var i = 0; i < values.Length; i++)
|
||||||
{
|
{
|
||||||
int index = Items.Count - 1;
|
var index = Items.Count - 1;
|
||||||
// Items.Insert(index, values[i]);
|
// Items.Insert(index, values[i]);
|
||||||
Tags?.Insert(index, values[i]);
|
Tags?.Insert(index, values[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
_textBox.Text = "";
|
_textBox.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Close(object o)
|
public void Close(object o)
|
||||||
{
|
{
|
||||||
if (o is Control t)
|
if (o is Control t)
|
||||||
{
|
|
||||||
if (t.Parent is ContentPresenter presenter)
|
if (t.Parent is ContentPresenter presenter)
|
||||||
{
|
{
|
||||||
int? index = _itemsControl?.IndexFromContainer(presenter);
|
var index = _itemsControl?.IndexFromContainer(presenter);
|
||||||
if (index is >= 0 && index < Items.Count - 1)
|
if (index is >= 0 && index < Items.Count - 1)
|
||||||
{
|
|
||||||
// Items.RemoveAt(index.Value);
|
// Items.RemoveAt(index.Value);
|
||||||
Tags.RemoveAt(index.Value);
|
Tags.RemoveAt(index.Value);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user