Make a control instead to avoid bindings spaghetti
MODIFY
WalletWasabi.Fluent/App.axaml
WalletWasabi.Fluent/App.axaml
<StyleInclude Source="avares://WalletWasabi.Fluent/Controls/Dialog.axaml" />
<StyleInclude Source="avares://WalletWasabi.Fluent/Controls/ProgressRingArc.axaml" />
<StyleInclude Source="avares://WalletWasabi.Fluent/Controls/ProgressRing.axaml" />
+ <StyleInclude Source="avares://WalletWasabi.Fluent/Controls/TagsControl.axaml" />
<StyleInclude Source="avares://WalletWasabi.Fluent/Controls/TagsBox.axaml" />
<StyleInclude Source="avares://WalletWasabi.Fluent/Controls/NavBarItem.axaml" />
<StyleInclude Source="avares://WalletWasabi.Fluent/Controls/NavBarTreeItem.axaml" />

MODIFY
WalletWasabi.Fluent/Controls/TagsBox.axaml
WalletWasabi.Fluent/Controls/TagsBox.axaml
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:c="clr-namespace:WalletWasabi.Fluent.Controls;assembly=WalletWasabi.Fluent">
+ xmlns:c="using:WalletWasabi.Fluent.Controls">
<Style Selector="c|TagsBox">
<Setter Property="Foreground" Value="{DynamicResource TextControlForeground}" />
<Setter Property="Background" Value="{DynamicResource TextControlBackground}" />
</ControlTemplate>
</Setter>
<Setter Property="(ScrollViewer.HorizontalScrollBarVisibility)" Value="Disabled" />
- <Setter Property="ItemTemplate">
- <DataTemplate>
- <Panel>
- <Panel.Styles>
- <Style Selector="Panel#PART_TagPanel Panel#PART_DeleteTagButton" >
- <Setter Property="Opacity" Value="0" />
- <Setter Property="RenderTransform" Value="scale(0.8)" />
- </Style>
- <Style Selector="Panel#PART_TagPanel:pointerover Panel#PART_DeleteTagButton" >
- <Setter Property="Opacity" Value="1" />
- <Setter Property="RenderTransform" Value="scale(1)" />
- </Style>
- </Panel.Styles>
- <Panel Name="PART_TagPanel">
- <Border Name="PART_TagItemBorder"
- Background="Transparent"
- BorderBrush="{DynamicResource TagsBoxControlForeground}"
- BorderThickness="1"
- CornerRadius="4"
- Margin="4"
- Opacity="0.4"
- Height="{Binding #PART_DummyPanel.Bounds.Height}"
- VerticalAlignment="Top" />
- <Panel Name="PART_DummyPanel"
- VerticalAlignment="Top">
- <ContentPresenter Content="{Binding .}"
- VerticalAlignment="Center"
- Margin="10,8,10,0" />
- </Panel>
- <Panel
- Name="PART_DeleteTagButton"
- Width="13"
- Height="13"
- VerticalAlignment="Top"
- HorizontalAlignment="Right">
- <Panel.Transitions>
- <Transitions>
- <DoubleTransition Property="Opacity" Duration="0:0:0.175" />
- <TransformOperationsTransition Property="RenderTransform" Duration="0:0:.135" Easing="QuadraticEaseIn" />
- </Transitions>
- </Panel.Transitions>
- <Ellipse Fill="Red"/>
- <Button Classes="" Command="{Binding $parent[c:TagsBox].DeleteTagCommand}" CommandParameter="{Binding }">Test</Button>
- </Panel>
- </Panel>
- </Panel>
- </DataTemplate>
- </Setter>
</Style>
<Style Selector="c|TagsBox:pointerover /template/ Border#PART_Border">
<Setter Property="IsReadOnly" Value="{Binding !IsInputEnabled}" />
<Setter Property="ContextMenu">
<ContextMenu>
- <MenuItem Header="Paste" Command="{Binding $parent[TextBox].Paste}" IsEnabled="{Binding $parent[TextBox].CanPaste}" InputGesture="{x:Static TextBox.PasteGesture}"/>
+ <MenuItem Header="Paste" Command="{Binding $parent[TextBox].Paste}"
+ IsEnabled="{Binding $parent[TextBox].CanPaste}"
+ InputGesture="{x:Static TextBox.PasteGesture}" />
</ContextMenu>
</Setter>
</Style>

MODIFY
WalletWasabi.Fluent/Controls/TagsBox.axaml.cs
WalletWasabi.Fluent/Controls/TagsBox.axaml.cs
@@ -3,11 +3,11 @@ using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
-using System.Reactive;
using System.Reactive.Disposables;
using System.Windows.Input;
using Avalonia;
using Avalonia.Controls;
+using Avalonia.Controls.Generators;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
using Avalonia.Input;
@@ -44,7 +44,6 @@ namespace WalletWasabi.Fluent.Controls
private bool _isInputEnabled = true;
private IEnumerable? _suggestions;
private ICommand? _completedCommand;
- private ReactiveCommand<object, Unit>? _deleteTagCommand;
public static readonly DirectProperty<TagsBox, ICommand?> CompletedCommandProperty =
AvaloniaProperty.RegisterDirect<TagsBox, ICommand?>(
@@ -52,12 +51,6 @@ namespace WalletWasabi.Fluent.Controls
o => o.CompletedCommand,
(o, v) => o.CompletedCommand = v);
- public static readonly DirectProperty<TagsBox, ReactiveCommand<object, Unit>?> DeleteTagCommandProperty =
- AvaloniaProperty.RegisterDirect<TagsBox, ReactiveCommand<object, Unit>?>(
- nameof(DeleteTagCommand),
- o => o.DeleteTagCommand,
- (o, v) => o.DeleteTagCommand = v);
-
static TagsBox()
{
ItemsProperty.OverrideMetadata<TagsBox>(
@@ -94,12 +87,6 @@ namespace WalletWasabi.Fluent.Controls
set => SetAndRaise(CompletedCommandProperty, ref _completedCommand, value);
}
- public ReactiveCommand<object, Unit>? DeleteTagCommand
- {
- get => _deleteTagCommand;
- set => SetAndRaise(DeleteTagCommandProperty, ref _deleteTagCommand, value);
- }
-
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
@@ -143,6 +130,9 @@ namespace WalletWasabi.Fluent.Controls
}
}
+ protected override IItemContainerGenerator CreateItemContainerGenerator() =>
+ new ItemContainerGenerator<TagControl>(this,ContentControl.ContentProperty,ContentControl.ContentTemplateProperty);
+
private void CheckIsInputEnabled()
{
if (Items is IList x && ItemCountLimit > 0)
@@ -358,6 +348,17 @@ namespace WalletWasabi.Fluent.Controls
CheckIsInputEnabled();
}
+
+ internal void RemoveTargetTag(object? tag)
+ {
+ if (Items is IList x && x.Count > 0 && tag is { })
+ {
+ x.RemoveAt(x.IndexOf(tag));
+ }
+
+ CheckIsInputEnabled();
+ }
+
private void SelectTag(string tagString)
{
SelectedTag = tagString;

ADD
WalletWasabi.Fluent/Controls/TagsControl.axaml
+<Styles xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:c="using:WalletWasabi.Fluent.Controls">
+
+ <Style Selector="Button.TagDelete">
+ <Setter Property="Background" Value="#FF6DB4EF" />
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate>
+ <Grid>
+ <Ellipse Fill="{TemplateBinding Background}" />
+ <Ellipse>
+ <Ellipse.Fill>
+ <RadialGradientBrush>
+ <GradientStop Offset="0" Color="#00000000" />
+ <GradientStop Offset="0.88" Color="#00000000" />
+ <GradientStop Offset="1" Color="#80000000" />
+ </RadialGradientBrush>
+ </Ellipse.Fill>
+ </Ellipse>
+ <Ellipse Margin="10" x:Name="highlightCircle">
+ <Ellipse.Fill>
+ <LinearGradientBrush>
+ <GradientStop Offset="0" Color="#50FFFFFF" />
+ <GradientStop Offset="0.5" Color="#00FFFFFF" />
+ <GradientStop Offset="1" Color="#50FFFFFF" />
+ </LinearGradientBrush>
+ </Ellipse.Fill>
+ </Ellipse>
+ <ContentPresenter x:Name="content" HorizontalAlignment="Center" VerticalAlignment="Center" />
+ </Grid>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+
+ <Style Selector="c|TagControl">
+ <Setter Property="Foreground" Value="{DynamicResource TextControlForeground}" />
+ <Setter Property="Background" Value="{DynamicResource TextControlBackground}" />
+ <Setter Property="BorderBrush" Value="{DynamicResource TextControlBorderBrush}" />
+ <Setter Property="BorderThickness" Value="{DynamicResource TextControlBorderThemeThickness}" />
+ <Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
+ <Setter Property="Template">
+ <ControlTemplate>
+ <Panel>
+ <Panel.Styles>
+ <Style Selector="Panel#PART_TagPanel Panel#PART_DeleteTagPanel">
+ <Setter Property="Opacity" Value="0" />
+ <Setter Property="RenderTransform" Value="scale(0.8)" />
+ </Style>
+ <Style Selector="Panel#PART_TagPanel:pointerover Panel#PART_DeleteTagPanel">
+ <Setter Property="Opacity" Value="1" />
+ <Setter Property="RenderTransform" Value="scale(1)" />
+ </Style>
+ </Panel.Styles>
+ <Panel Name="PART_TagPanel">
+ <Border Name="PART_TagItemBorder"
+ Background="Transparent"
+ BorderBrush="{DynamicResource TagsBoxControlForeground}"
+ BorderThickness="1"
+ CornerRadius="4"
+ Margin="4"
+ Opacity="0.4"
+ Height="{Binding #PART_DummyPanel.Bounds.Height}"
+ VerticalAlignment="Top" />
+ <Panel Name="PART_DummyPanel"
+ VerticalAlignment="Top">
+ <ContentPresenter Content="{Binding .}"
+ VerticalAlignment="Center"
+ Margin="10,8,10,0" />
+ </Panel>
+ <Panel
+ Name="PART_DeleteTagPanel"
+ Width="13"
+ Height="13"
+ VerticalAlignment="Top"
+ HorizontalAlignment="Right">
+ <Panel.Transitions>
+ <Transitions>
+ <DoubleTransition Property="Opacity" Duration="0:0:0.175" />
+ <TransformOperationsTransition Property="RenderTransform" Duration="0:0:.135"
+ Easing="QuadraticEaseIn" />
+ </Transitions>
+ </Panel.Transitions>
+ <Button Classes="TagDelete" Name="PART_DeleteButton" />
+ </Panel>
+ </Panel>
+ </Panel>
+ </ControlTemplate>
+ </Setter>
+ </Style>
+</Styles>
\ No newline at end of file

ADD
WalletWasabi.Fluent/Controls/TagsControl.axaml.cs
+using Avalonia.Controls;
+using Avalonia.Controls.Primitives;
+using Avalonia.Interactivity;
+
+namespace WalletWasabi.Fluent.Controls
+{
+ public class TagControl : TemplatedControl
+ {
+ private TagsBox? _parentTagBox;
+
+ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
+ {
+ _parentTagBox = Parent as TagsBox;
+ var deleteButton = e.NameScope.Find<Button>("PART_DeleteButton");
+ deleteButton.Click += DeleteTag;
+ base.OnApplyTemplate(e);
+ }
+
+ private void DeleteTag(object? sender, RoutedEventArgs e)
+ {
+ _parentTagBox?.RemoveTargetTag(DataContext);
+ }
+ }
+}
\ No newline at end of file

MODIFY
WalletWasabi.Fluent/ViewModels/AddWallet/RecoverWalletViewModel.cs
WalletWasabi.Fluent/ViewModels/AddWallet/RecoverWalletViewModel.cs
@@ -26,7 +26,6 @@ namespace WalletWasabi.Fluent.ViewModels.AddWallet
[AutoNotify] private string? _selectedTag;
[AutoNotify] private IEnumerable<string>? _suggestions;
[AutoNotify] private Mnemonic? _currentMnemonics;
- [AutoNotify] private ReactiveCommand<object, Unit>? _deleteSuggestionCommand;
public RecoverWalletViewModel(
string walletName,
@@ -78,14 +77,6 @@ namespace WalletWasabi.Fluent.ViewModels.AddWallet
});
EnableAutoBusyOn(NextCommand);
-
- DeleteSuggestionCommand = ReactiveCommand.Create<object>((del) =>
- {
- if (del is RecoveryWordIndexed word)
- {
- Mnemonics.RemoveAt(Mnemonics.IndexOf(word));
- }
- });
}
private async Task OnNext(

MODIFY
WalletWasabi.Fluent/Views/AddWallet/RecoverWalletView.axaml
WalletWasabi.Fluent/Views/AddWallet/RecoverWalletView.axaml
RestrictInputToSuggestions="True"
Items="{Binding Mnemonics}"
Suggestions="{Binding Suggestions}"
- DeleteTagCommand="{Binding DeleteSuggestionCommand}"
SelectedTag="{Binding SelectedTag}"
CompletedCommand="{Binding NextCommand}">
<i:Interaction.Behaviors>

Make a control instead to avoid bindings spaghetti