Use TreeDataGrid for WalletCoinsView
MODIFY
WalletWasabi.Fluent/ViewModels/Wallets/Advanced/WalletCoins/WalletCoinViewModel.cs
WalletWasabi.Fluent/ViewModels/Wallets/Advanced/WalletCoins/WalletCoinViewModel.cs
@@ -27,4 +27,50 @@ public partial class WalletCoinViewModel : ViewModelBase
coin.WhenAnyValue(c => c.HdPubKey.AnonymitySet).Subscribe(x => AnonymitySet = x);
coin.WhenAnyValue(c => c.CoinJoinInProgress).Subscribe(x => CoinJoinInProgress = x);
}
+
+ public static Comparison<WalletCoinViewModel?> SortAscending<T>(Func<WalletCoinViewModel, T> selector)
+ {
+ return (x, y) =>
+ {
+ if (x is null && y is null)
+ {
+ return 0;
+ }
+ else if (x is null)
+ {
+ return -1;
+ }
+ else if (y is null)
+ {
+ return 1;
+ }
+ else
+ {
+ return Comparer<T>.Default.Compare(selector(x), selector(y));
+ }
+ };
+ }
+
+ public static Comparison<WalletCoinViewModel?> SortDescending<T>(Func<WalletCoinViewModel, T> selector)
+ {
+ return (x, y) =>
+ {
+ if (x is null && y is null)
+ {
+ return 0;
+ }
+ else if (x is null)
+ {
+ return 1;
+ }
+ else if (y is null)
+ {
+ return -1;
+ }
+ else
+ {
+ return Comparer<T>.Default.Compare(selector(y), selector(x));
+ }
+ };
+ }
}

MODIFY
WalletWasabi.Fluent/ViewModels/Wallets/Advanced/WalletCoins/WalletCoinsViewModel.cs
WalletWasabi.Fluent/ViewModels/Wallets/Advanced/WalletCoins/WalletCoinsViewModel.cs
using DynamicData;
using ReactiveUI;
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using Avalonia.Controls;
+using Avalonia.Controls.Models.TreeDataGrid;
+using Avalonia.Controls.Templates;
+using DynamicData.Binding;
using WalletWasabi.Fluent.ViewModels.Navigation;
+using WalletWasabi.Fluent.Views.Wallets.Advanced.WalletCoins.Columns;
namespace WalletWasabi.Fluent.ViewModels.Wallets.Advanced.WalletCoins;
@@ -18,9 +18,8 @@ public partial class WalletCoinsViewModel : RoutableViewModel
{
private readonly WalletViewModel _walletViewModel;
private readonly IObservable<Unit> _balanceChanged;
-
- private readonly ReadOnlyObservableCollection<WalletCoinViewModel> _coins;
- private readonly SourceList<WalletCoinViewModel> _confirmationWordsSourceList = new();
+ private readonly ObservableCollectionExtended<WalletCoinViewModel> _coins;
+ private readonly SourceList<WalletCoinViewModel> _coinsSourceList = new();
public WalletCoinsViewModel(WalletViewModel walletViewModel, IObservable<Unit> balanceChanged)
{
@@ -28,15 +27,79 @@ public partial class WalletCoinsViewModel : RoutableViewModel
NextCommand = CancelCommand;
_walletViewModel = walletViewModel;
_balanceChanged = balanceChanged;
+ _coins = new ObservableCollectionExtended<WalletCoinViewModel>();
- _confirmationWordsSourceList
+ _coinsSourceList
.Connect()
.ObserveOn(RxApp.MainThreadScheduler)
- .Bind(out _coins)
+ .Bind(_coins)
.Subscribe();
+
+ // [Column] [View] [Header] [Width] [MinWidth] [MaxWidth] [CanUserSort]
+ // Indicators IndicatorsColumnView - Auto - - false
+ // Amount AmountColumnView Amount Auto - - true
+ // AnonymitySet AnonymityColumnView <custom> 40 - - true
+ // Labels LabelsColumnView Labels * - - false
+
+ Source = new FlatTreeDataGridSource<WalletCoinViewModel>(_coins)
+ {
+ Columns =
+ {
+ // Indicators
+ new TemplateColumn<WalletCoinViewModel>(
+ null,
+ new FuncDataTemplate<WalletCoinViewModel>((node, ns) => new IndicatorsColumnView(), true),
+ options: new ColumnOptions<WalletCoinViewModel>
+ {
+ CanUserResizeColumn = false,
+ CanUserSortColumn = false
+ },
+ width: new GridLength(0, GridUnitType.Auto)),
+
+ // Amount
+ new TemplateColumn<WalletCoinViewModel>(
+ new AnonymitySetHeaderView(),
+ new FuncDataTemplate<WalletCoinViewModel>((node, ns) => new AmountColumnView(), true),
+ options: new ColumnOptions<WalletCoinViewModel>
+ {
+ CanUserResizeColumn = false,
+ CanUserSortColumn = true,
+ CompareAscending = WalletCoinViewModel.SortAscending(x => x.Amount),
+ CompareDescending = WalletCoinViewModel.SortDescending(x => x.Amount)
+ },
+ width: new GridLength(0, GridUnitType.Auto)),
+
+ // AnonymitySet
+ new TemplateColumn<WalletCoinViewModel>(
+ "AnonymitySet",
+ new FuncDataTemplate<WalletCoinViewModel>((node, ns) => new AnonymitySetColumnView(), true),
+ options: new ColumnOptions<WalletCoinViewModel>
+ {
+ CanUserResizeColumn = false,
+ CanUserSortColumn = true,
+ CompareAscending = WalletCoinViewModel.SortAscending(x => x.AnonymitySet),
+ CompareDescending = WalletCoinViewModel.SortDescending(x => x.AnonymitySet)
+ },
+ width: new GridLength(40, GridUnitType.Pixel)),
+
+ // Labels
+ new TemplateColumn<WalletCoinViewModel>(
+ "Labels",
+ new FuncDataTemplate<WalletCoinViewModel>((node, ns) => new LabelsColumnView(), true),
+ options: new ColumnOptions<WalletCoinViewModel>
+ {
+ CanUserResizeColumn = false,
+ CanUserSortColumn = false
+ },
+ width: new GridLength(1, GridUnitType.Star))
+ }
+ };
+
+ Source.RowSelection!.SingleSelect = true;
+
}
- public ReadOnlyObservableCollection<WalletCoinViewModel> Coins => _coins;
+ public FlatTreeDataGridSource<WalletCoinViewModel> Source { get; }
protected override void OnNavigatedTo(bool isInHistory, CompositeDisposable disposables)
{
@@ -50,14 +113,17 @@ public partial class WalletCoinsViewModel : RoutableViewModel
Update();
});
- disposables.Add(_confirmationWordsSourceList);
+ disposables.Add(_coinsSourceList);
}
private void Update()
{
var coins = _walletViewModel.Wallet.Coins.Select(c => new WalletCoinViewModel(c));
- _confirmationWordsSourceList.Clear();
- _confirmationWordsSourceList.AddRange(coins);
+ _coinsSourceList.Edit(x =>
+ {
+ x.Clear();
+ x.AddRange(coins);
+ });
}
}

ADD
WalletWasabi.Fluent/Views/Wallets/Advanced/WalletCoins/Columns/AmountColumnView.axaml
+<UserControl xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:walletcoins="clr-namespace:WalletWasabi.Fluent.ViewModels.Wallets.Advanced.WalletCoins"
+ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+ x:DataType="walletcoins:WalletCoinViewModel"
+ x:CompileBindings="True"
+ x:Class="WalletWasabi.Fluent.Views.Wallets.Advanced.WalletCoins.Columns.AmountColumnView">
+ <TextBlock Text="{Binding Amount}"
+ Margin="5 0 10 0"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Center" />
+</UserControl>

ADD
WalletWasabi.Fluent/Views/Wallets/Advanced/WalletCoins/Columns/AmountColumnView.axaml.cs
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace WalletWasabi.Fluent.Views.Wallets.Advanced.WalletCoins.Columns;
+
+public partial class AmountColumnView : UserControl
+{
+ public AmountColumnView()
+ {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+}
\ No newline at end of file

ADD
WalletWasabi.Fluent/Views/Wallets/Advanced/WalletCoins/Columns/AnonymitySetColumnView.axaml
+<UserControl xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:walletcoins="clr-namespace:WalletWasabi.Fluent.ViewModels.Wallets.Advanced.WalletCoins"
+ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+ x:DataType="walletcoins:WalletCoinViewModel"
+ x:CompileBindings="True"
+ x:Class="WalletWasabi.Fluent.Views.Wallets.Advanced.WalletCoins.Columns.AnonymitySetColumnView">
+ <TextBlock Text="{Binding AnonymitySet}"
+ Margin="5 0"
+ VerticalAlignment="Center"
+ HorizontalAlignment="Center" />
+</UserControl>

ADD
WalletWasabi.Fluent/Views/Wallets/Advanced/WalletCoins/Columns/AnonymitySetColumnView.axaml.cs
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace WalletWasabi.Fluent.Views.Wallets.Advanced.WalletCoins.Columns;
+
+public partial class AnonymitySetColumnView : UserControl
+{
+ public AnonymitySetColumnView()
+ {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+}
\ No newline at end of file

ADD
WalletWasabi.Fluent/Views/Wallets/Advanced/WalletCoins/Columns/AnonymitySetHeaderView.axaml
+<UserControl xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+ x:Class="WalletWasabi.Fluent.Views.Wallets.Advanced.WalletCoins.Columns.AnonymitySetHeaderView">
+ <PathIcon Data="{StaticResource shield_regular}"
+ Opacity="0.6"
+ HorizontalAlignment="Center"
+ VerticalAlignment="Center"
+ ToolTip.Tip="Anonymity Score" />
+</UserControl>

ADD
WalletWasabi.Fluent/Views/Wallets/Advanced/WalletCoins/Columns/AnonymitySetHeaderView.axaml.cs
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace WalletWasabi.Fluent.Views.Wallets.Advanced.WalletCoins.Columns;
+
+public partial class AnonymitySetHeaderView : UserControl
+{
+ public AnonymitySetHeaderView()
+ {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+}
\ No newline at end of file

ADD
WalletWasabi.Fluent/Views/Wallets/Advanced/WalletCoins/Columns/IndicatorsColumnView.axaml
+<UserControl xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:walletcoins="clr-namespace:WalletWasabi.Fluent.ViewModels.Wallets.Advanced.WalletCoins"
+ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+ x:DataType="walletcoins:WalletCoinViewModel"
+ x:CompileBindings="True"
+ x:Class="WalletWasabi.Fluent.Views.Wallets.Advanced.WalletCoins.Columns.IndicatorsColumnView">
+ <StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Orientation="Horizontal" Spacing="5" Margin="5 0">
+
+ <Panel>
+ <!-- Confirmed -->
+ <PathIcon IsVisible="{Binding Confirmed}"
+ Data="{StaticResource checkmark_filled}"
+ Foreground="{DynamicResource SystemAccentColor}"
+ Height="11"
+ ToolTip.Tip="Confirmed" />
+ <!-- Pending -->
+ <PathIcon IsVisible="{Binding !Confirmed}"
+ Data="{StaticResource clock_regular}"
+ Height="14"
+ Opacity="0.6"
+ ToolTip.Tip="Pending" />
+ </Panel>
+
+ <Panel>
+ <!-- CJ active -->
+ <PathIcon IsVisible="{Binding CoinJoinInProgress}"
+ Data="{StaticResource link_filled}"
+ Foreground="{DynamicResource SystemAccentColor}"
+ Height="9"
+ ToolTip.Tip="Coinjoining" />
+ <!-- Pending -->
+ <PathIcon IsVisible="{Binding !CoinJoinInProgress}"
+ Data="{StaticResource link_filled}"
+ Height="9"
+ Opacity="0.3"
+ ToolTip.Tip="Not Coinjoining" />
+ </Panel>
+
+ </StackPanel>
+</UserControl>

ADD
WalletWasabi.Fluent/Views/Wallets/Advanced/WalletCoins/Columns/IndicatorsColumnView.axaml.cs
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace WalletWasabi.Fluent.Views.Wallets.Advanced.WalletCoins.Columns;
+
+public partial class IndicatorsColumnView : UserControl
+{
+ public IndicatorsColumnView()
+ {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+}
\ No newline at end of file

ADD
WalletWasabi.Fluent/Views/Wallets/Advanced/WalletCoins/Columns/LabelsColumnView.axaml
+<UserControl xmlns="https://github.com/avaloniaui"
+ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:c="using:WalletWasabi.Fluent.Controls"
+ xmlns:walletcoins="clr-namespace:WalletWasabi.Fluent.ViewModels.Wallets.Advanced.WalletCoins"
+ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+ x:DataType="walletcoins:WalletCoinViewModel"
+ x:CompileBindings="True"
+ x:Class="WalletWasabi.Fluent.Views.Wallets.Advanced.WalletCoins.Columns.LabelsColumnView">
+ <c:TagsBox IsReadOnly="True"
+ Margin="5 0"
+ VerticalAlignment="Center"
+ Items="{Binding SmartLabel}" />
+</UserControl>

ADD
WalletWasabi.Fluent/Views/Wallets/Advanced/WalletCoins/Columns/LabelsColumnView.axaml.cs
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace WalletWasabi.Fluent.Views.Wallets.Advanced.WalletCoins.Columns;
+
+public partial class LabelsColumnView : UserControl
+{
+ public LabelsColumnView()
+ {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+}
\ No newline at end of file

MODIFY
WalletWasabi.Fluent/Views/Wallets/Advanced/WalletCoins/WalletCoinsView.axaml
WalletWasabi.Fluent/Views/Wallets/Advanced/WalletCoins/WalletCoinsView.axaml
EnableBack="{Binding EnableBack}"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
- <DataBox Items="{Binding Coins}"
- CanUserSortColumns="True">
- <DataBox.Columns>
-
- <DataBoxTemplateColumn Width="Auto"
- CanUserSort="False"
- Header="">
- <DataBoxTemplateColumn.CellTemplate>
- <DataTemplate>
- <StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Orientation="Horizontal" Spacing="5" Margin="5 0" x:DataType="walletcoins:WalletCoinViewModel">
- <Panel>
- <!-- Confirmed -->
- <PathIcon IsVisible="{Binding Confirmed}"
- Data="{StaticResource checkmark_filled}"
- Foreground="{DynamicResource SystemAccentColor}"
- Height="11"
- ToolTip.Tip="Confirmed" />
- <!-- Pending -->
- <PathIcon IsVisible="{Binding !Confirmed}"
- Data="{StaticResource clock_regular}"
- Height="14"
- Opacity="0.6"
- ToolTip.Tip="Pending" />
- </Panel>
-
- <Panel>
- <!-- CJ active -->
- <PathIcon IsVisible="{Binding CoinJoinInProgress}"
- Data="{StaticResource link_filled}"
- Foreground="{DynamicResource SystemAccentColor}"
- Height="9"
- ToolTip.Tip="Coinjoining" />
- <!-- Pending -->
- <PathIcon IsVisible="{Binding !CoinJoinInProgress}"
- Data="{StaticResource link_filled}"
- Height="9"
- Opacity="0.3"
- ToolTip.Tip="Not Coinjoining" />
- </Panel>
-
-
- </StackPanel>
- </DataTemplate>
- </DataBoxTemplateColumn.CellTemplate>
- </DataBoxTemplateColumn>
-
- <DataBoxTemplateColumn Width="Auto"
- CanUserSort="False"
- Header="Amount">
- <DataBoxTemplateColumn.CellTemplate>
- <DataTemplate>
- <TextBlock Text="{Binding Amount}" Margin="5 0 10 0" VerticalAlignment="Center" HorizontalAlignment="Center" x:DataType="walletcoins:WalletCoinViewModel" />
- </DataTemplate>
- </DataBoxTemplateColumn.CellTemplate>
- </DataBoxTemplateColumn>
-
- <DataBoxTemplateColumn Width="40"
- CanUserSort="False">
- <DataBoxTemplateColumn.Header>
- <PathIcon Data="{StaticResource shield_regular}" Opacity="0.6" HorizontalAlignment="Center" VerticalAlignment="Center" ToolTip.Tip="Anonymity Score" />
- </DataBoxTemplateColumn.Header>
- <DataBoxTemplateColumn.CellTemplate>
- <DataTemplate>
- <TextBlock Text="{Binding AnonymitySet}" Margin="5 0" VerticalAlignment="Center" HorizontalAlignment="Center" x:DataType="walletcoins:WalletCoinViewModel" />
- </DataTemplate>
- </DataBoxTemplateColumn.CellTemplate>
- </DataBoxTemplateColumn>
-
- <DataBoxTemplateColumn Width="*"
- CanUserSort="False"
- Header="Labels">
- <DataBoxTemplateColumn.CellTemplate>
- <DataTemplate>
- <c:TagsBox IsReadOnly="True"
- Margin="5 0"
- VerticalAlignment="Center"
- Items="{Binding SmartLabel}"
- x:DataType="walletcoins:WalletCoinViewModel" />
- </DataTemplate>
- </DataBoxTemplateColumn.CellTemplate>
- </DataBoxTemplateColumn>
- </DataBox.Columns>
- </DataBox>
+ <TreeDataGrid Source="{Binding Source}"
+ CanUserSortColumns="True" />
</c:ContentArea>
</UserControl>

Use TreeDataGrid for WalletCoinsView