Current progress
ADD
WalletWasabi.Fluent/ViewModels/SearchBar/ValueObject.cs
+using System.Collections.Generic;
+using System.Linq;
+
+namespace WalletWasabi.Fluent.ViewModels.SearchBar
+{
+ [Serializable]
+ public abstract class ValueObject : IComparable, IComparable<ValueObject>
+ {
+ private int? _cachedHashCode;
+
+ protected abstract IEnumerable<object> GetEqualityComponents();
+
+ public override bool Equals(object obj)
+ {
+ if (obj == null)
+ {
+ return false;
+ }
+
+ if (GetUnproxiedType(this) != GetUnproxiedType(obj))
+ {
+ return false;
+ }
+
+ var valueObject = (ValueObject)obj;
+
+ return GetEqualityComponents().SequenceEqual(valueObject.GetEqualityComponents());
+ }
+
+ public override int GetHashCode()
+ {
+ if (!_cachedHashCode.HasValue)
+ {
+ _cachedHashCode = GetEqualityComponents()
+ .Aggregate(1, (current, obj) =>
+ {
+ unchecked
+ {
+ return current * 23 + (obj?.GetHashCode() ?? 0);
+ }
+ });
+ }
+
+ return _cachedHashCode.Value;
+ }
+
+ public virtual int CompareTo(object obj)
+ {
+ Type thisType = GetUnproxiedType(this);
+ Type otherType = GetUnproxiedType(obj);
+
+ if (thisType != otherType)
+ {
+ return string.Compare(thisType.ToString(), otherType.ToString(), StringComparison.Ordinal);
+ }
+
+ var other = (ValueObject)obj;
+
+ object[] components = GetEqualityComponents().ToArray();
+ object[] otherComponents = other.GetEqualityComponents().ToArray();
+
+ for (int i = 0; i < components.Length; i++)
+ {
+ int comparison = CompareComponents(components[i], otherComponents[i]);
+ if (comparison != 0)
+ {
+ return comparison;
+ }
+ }
+
+ return 0;
+ }
+
+ private int CompareComponents(object object1, object object2)
+ {
+ if (object1 is null && object2 is null)
+ {
+ return 0;
+ }
+
+ if (object1 is null)
+ {
+ return -1;
+ }
+
+ if (object2 is null)
+ {
+ return 1;
+ }
+
+ if (object1 is IComparable comparable1 && object2 is IComparable comparable2)
+ {
+ return comparable1.CompareTo(comparable2);
+ }
+
+ return object1.Equals(object2) ? 0 : -1;
+ }
+
+ public virtual int CompareTo(ValueObject other)
+ {
+ return CompareTo(other as object);
+ }
+
+ public static bool operator ==(ValueObject a, ValueObject b)
+ {
+ if (a is null && b is null)
+ {
+ return true;
+ }
+
+ if (a is null || b is null)
+ {
+ return false;
+ }
+
+ return a.Equals(b);
+ }
+
+ public static bool operator !=(ValueObject a, ValueObject b)
+ {
+ return !(a == b);
+ }
+
+ internal static Type GetUnproxiedType(object obj)
+ {
+ const string EFCoreProxyPrefix = "Castle.Proxies.";
+ const string NHibernateProxyPostfix = "Proxy";
+
+ Type type = obj.GetType();
+ string typeString = type.ToString();
+
+ if (typeString.Contains(EFCoreProxyPrefix) || typeString.EndsWith(NHibernateProxyPostfix))
+ {
+ return type.BaseType;
+ }
+
+ return type;
+ }
+ }
+}
\ No newline at end of file

ADD
WalletWasabi.Fluent/ViewModels/SearchBar/ViewModelBase.cs
+using ReactiveUI;
+
+namespace WalletWasabi.Fluent.ViewModels.SearchBar
+{
+ public class ViewModelBase : ReactiveObject
+ {
+ }
+}

MODIFY
WalletWasabi.Fluent/Views/MainView.axaml
WalletWasabi.Fluent/Views/MainView.axaml
xmlns:wallets="clr-namespace:WalletWasabi.Fluent.Views.Wallets"
xmlns:spectrum="clr-namespace:WalletWasabi.Fluent.Controls.Spectrum"
xmlns:converters="clr-namespace:WalletWasabi.Fluent.Converters"
+ xmlns:searchBar="clr-namespace:WalletWasabi.Fluent.Views.SearchBar"
mc:Ignorable="d" d:DesignWidth="536" d:DesignHeight="459"
x:DataType="vm:MainViewModel"
x:CompileBindings="True"
</Viewbox>
<TextBlock Foreground="{DynamicResource AcrylicTrimForeground}" Classes="h8" VerticalAlignment="Center" Text="Wasabi Wallet" Margin="20 0 0 0" IsHitTestVisible="False" />
<TextBlock Foreground="{DynamicResource AcrylicTrimForeground}" Classes="h8" VerticalAlignment="Center" Opacity="0.6" Text="TESTNET PREVIEW" IsHitTestVisible="False" Margin="5 0 0 0" />
+ <searchBar:SearchBar />
</StackPanel>
</Panel>

ADD
WalletWasabi.Fluent/Views/SearchBar/SearchBar.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.SearchBar.SearchBar"
+ x:DataType="searchBar:SearchBarViewModel"
+ xmlns:i="clr-namespace:Avalonia.Xaml.Interactivity;assembly=Avalonia.Xaml.Interactivity"
+ xmlns:ia="clr-namespace:Avalonia.Xaml.Interactions.Core;assembly=Avalonia.Xaml.Interactions"
+ xmlns:searchBar="clr-namespace:WalletWasabi.Fluent.ViewModels.SearchBar"
+ x:CompileBindings="True" x:Name="UserControl">
+
+ <Panel>
+ <TextBox Classes="" x:Name="Parent" DockPanel.Dock="Top" Text="{Binding SearchText, Mode=TwoWay}">
+ <i:Interaction.Behaviors>
+ <ia:EventTriggerBehavior EventName="GotFocus">
+ <ia:ChangePropertyAction TargetObject="{Binding #Popup}" PropertyName="IsOpen" Value="True" />
+ </ia:EventTriggerBehavior>
+ <ia:EventTriggerBehavior EventName="LostFocus">
+ <ia:ChangePropertyAction TargetObject="{Binding #Popup}" PropertyName="IsOpen" Value="False" />
+ </ia:EventTriggerBehavior>
+ </i:Interaction.Behaviors>
+ </TextBox>
+ <Popup x:Name="Popup" PlacementTarget="{Binding #Parent}">
+ <ListBox Items="{Binding Items}">
+
+ <ListBox.Styles>
+ <Style Selector="ListBox:empty">
+ <Setter Property="Template">
+ <Setter.Value>
+ <ControlTemplate>
+ <TextBlock>No results</TextBlock>
+ </ControlTemplate>
+ </Setter.Value>
+ </Setter>
+ </Style>
+ </ListBox.Styles>
+
+ <ListBox.ItemTemplate>
+ <DataTemplate x:DataType="searchBar:SearchItem">
+ <DockPanel Background="Transparent">
+
+ <DockPanel.Children>
+ <TextBlock Text="{Binding Name}" Width="100" />
+ <TextBlock Text="{Binding Description}" />
+ </DockPanel.Children>
+
+ <i:Interaction.Behaviors>
+ <ia:EventTriggerBehavior EventName="PointerPressed">
+ <ia:InvokeCommandAction Command="{Binding Command}" />
+ <ia:ChangePropertyAction TargetObject="{Binding #Popup}" PropertyName="IsOpen" Value="False"></ia:ChangePropertyAction>
+ </ia:EventTriggerBehavior>
+ </i:Interaction.Behaviors>
+
+ </DockPanel>
+ </DataTemplate>
+ </ListBox.ItemTemplate>
+
+ <i:Interaction.Behaviors>
+ <searchBar:AutoSelectFirstItemBehavior />
+ </i:Interaction.Behaviors>
+
+ </ListBox>
+ </Popup>
+ </Panel>
+</UserControl>

ADD
WalletWasabi.Fluent/Views/SearchBar/SearchBar.axaml.cs
+using System.Linq;
+using System.Reactive.Linq;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using ReactiveUI;
+using WalletWasabi.Fluent.ViewModels.Navigation;
+using WalletWasabi.Fluent.ViewModels.SearchBar;
+
+namespace WalletWasabi.Fluent.Views.SearchBar;
+
+public class SearchBar : UserControl
+{
+ public SearchBar()
+ {
+ InitializeComponent();
+ }
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+
+ var data = NavigationManager.MetaData
+ .Select(m =>
+ {
+ var reactiveCommand = ReactiveCommand.Create(
+ () => { });
+ var searchItem = new SearchItem(m.Title, m.Caption, reactiveCommand);
+ return searchItem;
+ });
+
+ var items = data.ToObservable();
+
+ DataContext = new SearchBarViewModel(items);
+ }
+}
\ No newline at end of file

Current progress