I have an Avalonia Templated control that has a Styled Property of “Value” that is a string. It’s purpose is to accept a series of tags that are separated by spaces. I would like to split that string and show the individual tags in an item control (or item repeater).
So – I wrote code to catch the changes to “Value” and split out the values and put them in an observable collection. I know that the collection is being populated correctly, but I cannot get it to display on the control.
I’ve tried making the collection as an observablecollection, avalonialist, styled property, direct property, etc. Also tried templatebinding vs binding to the parent for the itemscontrol.
Any thoughts on what I am doing wrong?
public class FormTagControl : TemplatedControl
{
public static readonly StyledProperty<string> ValueProperty =
AvaloniaProperty.Register<FormTagControl, string>(nameof(Value), defaultBindingMode: BindingMode.TwoWay);
public static readonly StyledProperty<bool> ShowEditModeProperty =
AvaloniaProperty.Register<FormTagControl, bool>(nameof(ShowEditMode), defaultBindingMode: BindingMode.TwoWay);
public static readonly StyledProperty<bool> ShowAddButtonProperty =
AvaloniaProperty.Register<FormTagControl, bool>(nameof(ShowEditMode), defaultBindingMode: BindingMode.TwoWay);
public static readonly StyledProperty<string> ErrorMessageProperty =
AvaloniaProperty.Register<FormTagControl, string>(nameof(ErrorMessage), defaultBindingMode: BindingMode.OneWay);
public static readonly StyledProperty<string> LabelProperty =
AvaloniaProperty.Register<FormTagControl, string>(nameof(Label), defaultBindingMode: BindingMode.OneWay);
public static readonly StyledProperty<string> WatermarkProperty =
AvaloniaProperty.Register<FormTagControl, string>(nameof(Watermark), defaultBindingMode: BindingMode.OneWay);
public ObservableCollection<string> Tags { get; set; } = new();
protected bool ShowEditMode
{
get => GetValue(ShowEditModeProperty);
set => SetValue(ShowEditModeProperty, value);
}
protected bool ShowAddButton
{
get => GetValue(ShowAddButtonProperty);
set => SetValue(ShowAddButtonProperty, value);
}
public string Value
{
get => GetValue(ValueProperty);
set => SetValue(ValueProperty, value);
}
public string ErrorMessage
{
get => GetValue(ErrorMessageProperty);
set => SetValue(ErrorMessageProperty, value);
}
public string Label
{
get => GetValue(LabelProperty);
set => SetValue(LabelProperty, value);
}
public string Watermark
{
get => GetValue(WatermarkProperty);
set => SetValue(WatermarkProperty, value);
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
var btnGoToEditMode = e.NameScope.Find<Button>("PART_GoToEditModeButton");
var btnGoToViewMode = e.NameScope.Find<Button>("PART_GoToViewModeButton");
var btnAddTags = e.NameScope.Find<Button>("PART_AddButton");
if (btnGoToEditMode is not null)
{
btnGoToEditMode.Click += (s, e) =>
{
ShowEditMode = true;
};
}
if (btnGoToViewMode is not null)
{
btnGoToViewMode.Click += (s, e) =>
{
ShowEditMode = false;
};
}
if (btnAddTags is not null)
{
btnAddTags.Click += (s, e) =>
{
ShowEditMode = true;
};
}
base.OnApplyTemplate(e);
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
if (change.Property.Name == nameof(Value))
{
if (string.IsNullOrEmpty(change.NewValue?.ToString()))
{
ShowAddButton = true;
}
else
{
ShowAddButton = false;
}
BuildTagList();
}
base.OnPropertyChanged(change);
}
protected void BuildTagList()
{
Tags.Clear();
string[] tagValues = Value.Split(' ', StringSplitOptions.RemoveEmptyEntries);
foreach (string tagValue in tagValues)
{
Tags.Add(tagValue);
}
}
}
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="https://github.com/projektanker/icons.avalonia"
xmlns:controls="using:NRS.UI.Mvvm.FormControls">
<Design.PreviewWith>
<controls:FormTagControl />
</Design.PreviewWith>
<Style Selector="controls|FormTagControl">
<!-- Set Defaults -->
<Setter Property="Template">
<ControlTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Classes="NRSLabel" Text="{TemplateBinding Label}"></TextBlock>
<Grid Grid.Row="1" IsVisible="{TemplateBinding ShowEditMode}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button i:Attached.Icon="fa-solid fa-grip" Grid.Column="0" Name="PART_GoToViewModeButton" Classes="NoStyling" Height="38">
</Button>
<TextBox Grid.Column="1" Watermark="{TemplateBinding Watermark}"
Text="{Binding Value, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"/>
</Grid>
<Grid Grid.Row="1" IsVisible="{TemplateBinding ShowEditMode,Converter={x:Static BoolConverters.Not}}">
<Button Name="PART_GoToEditModeButton" Classes="NoStyling" Height="38"
IsVisible="{TemplateBinding ShowAddButton,Converter={x:Static BoolConverters.Not}}">
<ItemsControl Margin="0 40 0 0" ItemsSource="{Binding Tags, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Button>
<Button Name="PART_AddButton" Height="38"
IsVisible="{TemplateBinding ShowAddButton}">
<Label>Add Tags</Label>
</Button>
</Grid>
<TextBlock Grid.Row="2" Classes="NRSErrorMessage" Text="{TemplateBinding ErrorMessage}" />
</Grid>
</ControlTemplate>
</Setter>
</Style>
</Styles>