separate fee slider into a different view.
MODIFY
WalletWasabi.Fluent/ViewModels/Wallets/Receive/ReceiveViewModel.cs
WalletWasabi.Fluent/ViewModels/Wallets/Receive/ReceiveViewModel.cs
@@ -24,7 +24,7 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Receive
NavBarPosition = NavBarPosition.None,
Searchable = false,
NavigationTarget = NavigationTarget.DialogScreen)]
- public partial class ReceiveViewModel : NavBarItemViewModel
+ public partial class ReceiveViewModel : RoutableViewModel
{
private readonly Wallet _wallet;
private readonly SourceList<SuggestionLabelViewModel> _suggestionLabels;
@@ -33,7 +33,7 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Receive
[AutoNotify] private HashSet<string> _suggestions;
[AutoNotify] private bool _isExistingAddressesButtonVisible;
- public ReceiveViewModel(Wallet wallet) : base(NavigationMode.Normal)
+ public ReceiveViewModel(Wallet wallet)
{
_wallet = wallet;
_labels = new ObservableCollectionExtended<string>();
@@ -69,8 +69,6 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Receive
.Bind(_suggestionLabelResults)
.Subscribe();
- SelectionMode = NavBarItemSelectionMode.Button;
-
SetupCancel(enableCancel: true, enableCancelOnEscape: true, enableCancelOnPressed: true);
EnableBack = false;

ADD
WalletWasabi.Fluent/ViewModels/Wallets/Send/FeeSliderViewModel.cs
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reactive.Disposables;
+using System.Reactive.Linq;
+using NBitcoin;
+using ReactiveUI;
+using WalletWasabi.Blockchain.Analysis.FeesEstimation;
+using WalletWasabi.Fluent.MathNet;
+using WalletWasabi.Fluent.ViewModels.Dialogs.Base;
+using WalletWasabi.Gui.Converters;
+using WalletWasabi.Wallets;
+
+namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
+{
+ [NavigationMetaData(
+ Title = "Send",
+ Caption = "",
+ IconName = "wallet_action_send",
+ NavBarPosition = NavBarPosition.None,
+ Searchable = false,
+ NavigationTarget = NavigationTarget.DialogScreen)]
+ public partial class FeeSliderViewModel : DialogViewModelBase<(FeeRate feeRate, TimeSpan confirmationTime)>
+ {
+ private readonly Wallet _wallet;
+
+ [AutoNotify] private double[] _xAxisValues;
+ [AutoNotify] private double[] _yAxisValues;
+ [AutoNotify] private string[] _xAxisLabels;
+ [AutoNotify] private double _xAxisCurrentValue = 36;
+ [AutoNotify] private int _xAxisCurrentValueIndex;
+
+ [AutoNotify(SetterModifier = AccessModifier.Private)]
+ private int _xAxisMinValue = 0;
+
+ [AutoNotify(SetterModifier = AccessModifier.Private)]
+ private int _xAxisMaxValue = 9;
+
+ private bool _updatingCurrentValue;
+ private FeeRate _feeRate;
+ private double _lastXAxisCurrentValue;
+
+ public FeeSliderViewModel(Wallet wallet)
+ {
+ _wallet = wallet;
+ _lastXAxisCurrentValue = _xAxisCurrentValue;
+
+ this.WhenAnyValue(x => x.XAxisCurrentValue)
+ .Subscribe(x =>
+ {
+ if (x > 0)
+ {
+ _feeRate = new FeeRate(GetYAxisValueFromXAxisCurrentValue(x));
+ SetXAxisCurrentValueIndex(x);
+ }
+ });
+
+ this.WhenAnyValue(x => x.XAxisCurrentValueIndex)
+ .Subscribe(SetXAxisCurrentValue);
+
+ NextCommand = ReactiveCommand.Create(() =>
+ {
+ _lastXAxisCurrentValue = XAxisCurrentValue;
+ var confirmationTime = CalculateConfirmationTime(_lastXAxisCurrentValue);
+
+ Close(DialogResultKind.Normal, (_feeRate, confirmationTime));
+ });
+ }
+
+ protected override void OnNavigatedTo(bool isInHistory, CompositeDisposable disposables)
+ {
+ base.OnNavigatedTo(isInHistory, disposables);
+
+ var feeProvider = _wallet.FeeProvider;
+ Observable
+ .FromEventPattern(feeProvider, nameof(feeProvider.AllFeeEstimateChanged))
+ .Select(x => (x.EventArgs as AllFeeEstimate)!.Estimations)
+ .ObserveOn(RxApp.MainThreadScheduler)
+ .Subscribe(UpdateFeeEstimates)
+ .DisposeWith(disposables);
+
+ if (feeProvider.AllFeeEstimate is { })
+ {
+ UpdateFeeEstimates(feeProvider.AllFeeEstimate.Estimations);
+ }
+ }
+
+ private TimeSpan CalculateConfirmationTime(double targetBlock)
+ {
+ var timeInMinutes = Math.Ceiling(targetBlock) * 10;
+ var time = TimeSpan.FromMinutes(timeInMinutes);
+ return time;
+ }
+
+ private void SetXAxisCurrentValueIndex(double xAxisCurrentValue)
+ {
+ if (!_updatingCurrentValue)
+ {
+ _updatingCurrentValue = true;
+ if (_xAxisValues is not null)
+ {
+ XAxisCurrentValueIndex = GetCurrentValueIndex(xAxisCurrentValue, _xAxisValues);
+ }
+
+ _updatingCurrentValue = false;
+ }
+ }
+
+ private void SetXAxisCurrentValue(int xAxisCurrentValueIndex)
+ {
+ if (_xAxisValues is not null)
+ {
+ if (!_updatingCurrentValue)
+ {
+ _updatingCurrentValue = true;
+ var index = _xAxisValues.Length - xAxisCurrentValueIndex - 1;
+ XAxisCurrentValue = _xAxisValues[index];
+ _updatingCurrentValue = false;
+ }
+ }
+ }
+
+ private void UpdateFeeEstimates(Dictionary<int, int> feeEstimates)
+ {
+ string[] xAxisLabels;
+ double[] xAxisValues;
+ double[] yAxisValues;
+
+ if (_wallet.Network != Network.TestNet)
+ {
+ var labels = feeEstimates.Select(x => x.Key)
+ .Select(x => FeeTargetTimeConverter.Convert(x, "m", "h", "h", "d", "d"))
+ .Reverse()
+ .ToArray();
+
+ var xs = feeEstimates.Select(x => (double)x.Key).ToArray();
+ var ys = feeEstimates.Select(x => (double)x.Value).ToArray();
+#if true
+ GetSmoothValuesSubdivide(xs, ys, out var ts, out var xts);
+ xAxisValues = ts.ToArray();
+ yAxisValues = xts.ToArray();
+#else
+ xAxisValues = xs.Reverse().ToArray();
+ yAxisValues = ys.Reverse().ToArray();
+#endif
+ xAxisLabels = labels;
+ }
+ else
+ {
+#if true
+ GetSmoothValuesSubdivide(TestNetXAxisValues, TestNetYAxisValues, out var ts, out var xts);
+ xAxisValues = ts.ToArray();
+ yAxisValues = xts.ToArray();
+#else
+ xAxisValues = xs.Reverse().ToArray();
+ yAxisValues = ys.Reverse().ToArray();
+#endif
+ var labels = TestNetXAxisValues.Select(x => x)
+ .Select(x => FeeTargetTimeConverter.Convert((int)x, "m", "h", "h", "d", "d"))
+ .Reverse()
+ .ToArray();
+ xAxisLabels = labels;
+ }
+
+ _updatingCurrentValue = true;
+ XAxisLabels = xAxisLabels;
+ XAxisValues = xAxisValues;
+ YAxisValues = yAxisValues;
+ XAxisMinValue = 0;
+ XAxisMaxValue = xAxisValues.Length - 1;
+ XAxisCurrentValue = Math.Clamp(XAxisCurrentValue, XAxisMinValue, XAxisMaxValue);
+ XAxisCurrentValueIndex = GetCurrentValueIndex(XAxisCurrentValue, XAxisValues);
+ _updatingCurrentValue = false;
+ }
+
+ private static readonly double[] TestNetXAxisValues =
+ {
+ 1,
+ 2,
+ 3,
+ 6,
+ 18,
+ 36,
+ 72,
+ 144,
+ 432,
+ 1008
+ };
+
+ private static readonly double[] TestNetYAxisValues =
+ {
+ 185,
+ 123,
+ 123,
+ 102,
+ 97,
+ 57,
+ 22,
+ 7,
+ 4,
+ 4
+ };
+
+ private int GetCurrentValueIndex(double xAxisCurrentValue, double[] xAxisValues)
+ {
+ for (var i = 0; i < xAxisValues.Length; i++)
+ {
+ if (xAxisValues[i] <= xAxisCurrentValue)
+ {
+ var index = xAxisValues.Length - i - 1;
+ return index;
+ }
+ }
+
+ return 0;
+ }
+
+ private void GetSmoothValuesSubdivide(double[] xs, double[] ys, out List<double> ts, out List<double> xts)
+ {
+ const int Divisions = 256;
+
+ ts = new List<double>();
+ xts = new List<double>();
+
+ if (xs.Length > 2)
+ {
+ var spline = CubicSpline.InterpolatePchipSorted(xs, ys);
+
+ for (var i = 0; i < xs.Length - 1; i++)
+ {
+ var a = xs[i];
+ var b = xs[i + 1];
+ var range = b - a;
+ var step = range / Divisions;
+
+ var t0 = xs[i];
+ ts.Add(t0);
+ var xt0 = spline.Interpolate(xs[i]);
+ xts.Add(xt0);
+
+ for (var t = a + step; t < b; t += step)
+ {
+ var xt = spline.Interpolate(t);
+ ts.Add(t);
+ xts.Add(xt);
+ }
+ }
+
+ var tn = xs[^1];
+ ts.Add(tn);
+ var xtn = spline.Interpolate(xs[^1]);
+ xts.Add(xtn);
+ }
+ else
+ {
+ for (var i = 0; i < xs.Length; i++)
+ {
+ ts.Add(xs[i]);
+ xts.Add(ys[i]);
+ }
+ }
+
+ ts.Reverse();
+ xts.Reverse();
+ }
+
+ private decimal GetYAxisValueFromXAxisCurrentValue(double xValue)
+ {
+ if (_xAxisValues is { } && _yAxisValues is { })
+ {
+ var x = _xAxisValues.Reverse().ToArray();
+ var y = _yAxisValues.Reverse().ToArray();
+ double t = xValue;
+ var spline = CubicSpline.InterpolatePchipSorted(x, y);
+ var interpolated = (decimal)spline.Interpolate(t);
+ return Math.Clamp(interpolated, (decimal)y[^1], (decimal)y[0]);
+ }
+
+ return XAxisMaxValue;
+ }
+ }
+}
\ No newline at end of file

MODIFY
WalletWasabi.Fluent/ViewModels/Wallets/Send/SendViewModel.cs
WalletWasabi.Fluent/ViewModels/Wallets/Send/SendViewModel.cs
using System;
-using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
@@ -15,16 +14,14 @@ using NBitcoin;
using NBitcoin.Payment;
using ReactiveUI;
using WalletWasabi.Blockchain.Analysis.Clustering;
-using WalletWasabi.Blockchain.Analysis.FeesEstimation;
using WalletWasabi.Exceptions;
using WalletWasabi.Fluent.Helpers;
-using WalletWasabi.Fluent.MathNet;
using WalletWasabi.Fluent.Models;
using WalletWasabi.Fluent.Validation;
using WalletWasabi.Fluent.ViewModels.Dialogs;
+using WalletWasabi.Fluent.ViewModels.Dialogs.Base;
using WalletWasabi.Fluent.ViewModels.NavBar;
using WalletWasabi.Fluent.ViewModels.Navigation;
-using WalletWasabi.Gui.Converters;
using WalletWasabi.Logging;
using WalletWasabi.Models;
using WalletWasabi.Tor.Http;
@@ -43,7 +40,7 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
NavBarPosition = NavBarPosition.None,
Searchable = false,
NavigationTarget = NavigationTarget.DialogScreen)]
- public partial class SendViewModel : NavBarItemViewModel
+ public partial class SendViewModel : RoutableViewModel
{
private readonly Wallet _wallet;
private readonly TransactionInfo _transactionInfo;
@@ -55,29 +52,16 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
[AutoNotify] private ObservableCollection<string> _priorLabels;
[AutoNotify] private ObservableCollection<string> _labels;
[AutoNotify] private bool _isPayJoin;
- [AutoNotify] private double[] _xAxisValues;
- [AutoNotify] private double[] _yAxisValues;
- [AutoNotify] private string[] _xAxisLabels;
- [AutoNotify] private double _xAxisCurrentValue = 36;
- [AutoNotify] private int _xAxisCurrentValueIndex;
- [AutoNotify(SetterModifier = AccessModifier.Private)] private int _xAxisMinValue = 0;
- [AutoNotify(SetterModifier = AccessModifier.Private)] private int _xAxisMaxValue = 9;
[AutoNotify] private string? _payJoinEndPoint;
private bool _parsingUrl;
- private bool _updatingCurrentValue;
- private double _lastXAxisCurrentValue;
- private FeeRate _feeRate;
- public SendViewModel(Wallet wallet) : base(NavigationMode.Normal)
+ public SendViewModel(Wallet wallet)
{
_to = "";
_wallet = wallet;
_transactionInfo = new TransactionInfo();
_labels = new ObservableCollection<string>();
- _lastXAxisCurrentValue = _xAxisCurrentValue;
-
- SelectionMode = NavBarItemSelectionMode.Button;
ExchangeRate = _wallet.Synchronizer.UsdExchangeRate;
PriorLabels = new();
@@ -91,19 +75,6 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
this.WhenAnyValue(x => x.AmountBtc)
.Subscribe(x => _transactionInfo.Amount = new Money(x, MoneyUnit.BTC));
- this.WhenAnyValue(x => x.XAxisCurrentValue)
- .Subscribe(x =>
- {
- if (x > 0)
- {
- _feeRate = new FeeRate(GetYAxisValueFromXAxisCurrentValue(x));
- SetXAxisCurrentValueIndex(x);
- }
- });
-
- this.WhenAnyValue(x => x.XAxisCurrentValueIndex)
- .Subscribe(SetXAxisCurrentValue);
-
this.WhenAnyValue(x => x.PayJoinEndPoint)
.Subscribe(endPoint =>
{
@@ -127,7 +98,7 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
AutoPasteCommand = ReactiveCommand.CreateFromTask(async () => await OnAutoPasteAsync());
var nextCommandCanExecute =
- this.WhenAnyValue(x => x.Labels, x => x.AmountBtc, x => x.To, x => x.XAxisCurrentValue).Select(_ => Unit.Default)
+ this.WhenAnyValue(x => x.Labels, x => x.AmountBtc, x => x.To).Select(_ => Unit.Default)
.Merge(Observable.FromEventPattern(Labels, nameof(Labels.CollectionChanged)).Select(_ => Unit.Default))
.Select(_ =>
{
@@ -172,12 +143,23 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
private async Task OnNextAsync()
{
+ var result = await NavigateDialogAsync(new FeeSliderViewModel(_wallet));
+
+ if (result.Kind == DialogResultKind.Normal && result.Result is { })
+ {
+ _transactionInfo.FeeRate = result.Result.feeRate;
+ _transactionInfo.ConfirmationTimeSpan = result.Result.confirmationTime;
+ }
+ else
+ {
+ return;
+ }
+
var transactionInfo = _transactionInfo;
var targetAnonymitySet = _wallet.ServiceConfiguration.GetMixUntilAnonymitySetValue();
var mixedCoins = _wallet.Coins.Where(x => x.HdPubKey.AnonymitySet >= targetAnonymitySet).ToList();
var totalMixedCoinsAmount = Money.FromUnit(mixedCoins.Sum(coin => coin.Amount), MoneyUnit.Satoshi);
transactionInfo.Coins = mixedCoins;
- transactionInfo.FeeRate = _feeRate;
if (transactionInfo.Amount > totalMixedCoinsAmount)
{
@@ -263,47 +245,14 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
}
}
- IHttpClient httpClient = Services.ExternalHttpClientFactory.NewHttpClient(() => payjoinEndPointUri, Mode.DefaultCircuit);
+ IHttpClient httpClient =
+ Services.ExternalHttpClientFactory.NewHttpClient(() => payjoinEndPointUri, Mode.DefaultCircuit);
return new PayjoinClient(payjoinEndPointUri, httpClient);
}
return null;
}
- private TimeSpan CalculateConfirmationTime(double targetBlock)
- {
- var timeInMinutes = Math.Ceiling(targetBlock) * 10;
- var time = TimeSpan.FromMinutes(timeInMinutes);
- return time;
- }
-
- private void SetXAxisCurrentValueIndex(double xAxisCurrentValue)
- {
- if (!_updatingCurrentValue)
- {
- _updatingCurrentValue = true;
- if (_xAxisValues is not null)
- {
- XAxisCurrentValueIndex = GetCurrentValueIndex(xAxisCurrentValue, _xAxisValues);
- }
- _updatingCurrentValue = false;
- }
- }
-
- private void SetXAxisCurrentValue(int xAxisCurrentValueIndex)
- {
- if (_xAxisValues is not null)
- {
- if (!_updatingCurrentValue)
- {
- _updatingCurrentValue = true;
- var index = _xAxisValues.Length - xAxisCurrentValueIndex - 1;
- XAxisCurrentValue = _xAxisValues[index];
- _updatingCurrentValue = false;
- }
- }
- }
-
private void ValidateAmount(IValidationErrors errors)
{
if (AmountBtc > Constants.MaximumNumberOfBitcoins)
@@ -403,13 +352,6 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
return result;
}
- protected override void OnNavigatedFrom(bool isInHistory)
- {
- base.OnNavigatedFrom(isInHistory);
- _lastXAxisCurrentValue = XAxisCurrentValue;
- _transactionInfo.ConfirmationTimeSpan = CalculateConfirmationTime(_lastXAxisCurrentValue);
- }
-
protected override void OnNavigatedTo(bool inHistory, CompositeDisposable disposables)
{
if (!inHistory)
@@ -419,10 +361,6 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
Labels.Clear();
ClearValidations();
}
- else
- {
- XAxisCurrentValue = _lastXAxisCurrentValue;
- }
_wallet.Synchronizer.WhenAnyValue(x => x.UsdExchangeRate)
.ObserveOn(RxApp.MainThreadScheduler)
@@ -448,179 +386,7 @@ namespace WalletWasabi.Fluent.ViewModels.Wallets.Send
PriorLabels = new ObservableCollection<string>(PriorLabels.Distinct());
- var feeProvider = _wallet.FeeProvider;
- Observable
- .FromEventPattern(feeProvider, nameof(feeProvider.AllFeeEstimateChanged))
- .Select(x => (x.EventArgs as AllFeeEstimate)!.Estimations)
- .ObserveOn(RxApp.MainThreadScheduler)
- .Subscribe(UpdateFeeEstimates)
- .DisposeWith(disposables);
-
- if (feeProvider.AllFeeEstimate is { })
- {
- UpdateFeeEstimates(feeProvider.AllFeeEstimate.Estimations);
- }
-
base.OnNavigatedTo(inHistory, disposables);
}
-
- private static readonly double[] TestNetXAxisValues =
- {
- 1,
- 2,
- 3,
- 6,
- 18,
- 36,
- 72,
- 144,
- 432,
- 1008
- };
-
- private static readonly double[] TestNetYAxisValues =
- {
- 185,
- 123,
- 123,
- 102,
- 97,
- 57,
- 22,
- 7,
- 4,
- 4
- };
-
- private void UpdateFeeEstimates(Dictionary<int, int> feeEstimates)
- {
- string[] xAxisLabels;
- double[] xAxisValues;
- double[] yAxisValues;
-
- if (_wallet.Network != Network.TestNet)
- {
- var labels = feeEstimates.Select(x => x.Key)
- .Select(x => FeeTargetTimeConverter.Convert(x, "m", "h", "h", "d", "d"))
- .Reverse()
- .ToArray();
-
- var xs = feeEstimates.Select(x => (double)x.Key).ToArray();
- var ys = feeEstimates.Select(x => (double)x.Value).ToArray();
-#if true
- GetSmoothValuesSubdivide(xs, ys, out var ts, out var xts);
- xAxisValues = ts.ToArray();
- yAxisValues = xts.ToArray();
-#else
- xAxisValues = xs.Reverse().ToArray();
- yAxisValues = ys.Reverse().ToArray();
-#endif
- xAxisLabels = labels;
- }
- else
- {
-#if true
- GetSmoothValuesSubdivide(TestNetXAxisValues, TestNetYAxisValues, out var ts, out var xts);
- xAxisValues = ts.ToArray();
- yAxisValues = xts.ToArray();
-#else
- xAxisValues = xs.Reverse().ToArray();
- yAxisValues = ys.Reverse().ToArray();
-#endif
- var labels = TestNetXAxisValues.Select(x => x)
- .Select(x => FeeTargetTimeConverter.Convert((int)x, "m", "h", "h", "d", "d"))
- .Reverse()
- .ToArray();
- xAxisLabels = labels;
- }
-
- _updatingCurrentValue = true;
- XAxisLabels = xAxisLabels;
- XAxisValues = xAxisValues;
- YAxisValues = yAxisValues;
- XAxisMinValue = 0;
- XAxisMaxValue = xAxisValues.Length - 1;
- XAxisCurrentValue = Math.Clamp(XAxisCurrentValue, XAxisMinValue, XAxisMaxValue);
- XAxisCurrentValueIndex = GetCurrentValueIndex(XAxisCurrentValue, XAxisValues);
- _updatingCurrentValue = false;
- }
-
- private int GetCurrentValueIndex(double xAxisCurrentValue, double[] xAxisValues)
- {
- for (var i = 0; i < xAxisValues.Length; i++)
- {
- if (xAxisValues[i] <= xAxisCurrentValue)
- {
- var index = xAxisValues.Length - i - 1;
- return index;
- }
- }
-
- return 0;
- }
-
- private void GetSmoothValuesSubdivide(double[] xs, double[] ys, out List<double> ts, out List<double> xts)
- {
- const int Divisions = 256;
-
- ts = new List<double>();
- xts = new List<double>();
-
- if (xs.Length > 2)
- {
- var spline = CubicSpline.InterpolatePchipSorted(xs, ys);
-
- for (var i = 0; i < xs.Length - 1; i++)
- {
- var a = xs[i];
- var b = xs[i + 1];
- var range = b - a;
- var step = range / Divisions;
-
- var t0 = xs[i];
- ts.Add(t0);
- var xt0 = spline.Interpolate(xs[i]);
- xts.Add(xt0);
-
- for (var t = a + step; t < b; t += step)
- {
- var xt = spline.Interpolate(t);
- ts.Add(t);
- xts.Add(xt);
- }
- }
-
- var tn = xs[^1];
- ts.Add(tn);
- var xtn = spline.Interpolate(xs[^1]);
- xts.Add(xtn);
- }
- else
- {
- for (var i = 0; i < xs.Length; i++)
- {
- ts.Add(xs[i]);
- xts.Add(ys[i]);
- }
- }
-
- ts.Reverse();
- xts.Reverse();
- }
-
- private decimal GetYAxisValueFromXAxisCurrentValue(double xValue)
- {
- if (_xAxisValues is { } && _yAxisValues is { })
- {
- var x = _xAxisValues.Reverse().ToArray();
- var y = _yAxisValues.Reverse().ToArray();
- double t = xValue;
- var spline = CubicSpline.InterpolatePchipSorted(x, y);
- var interpolated = (decimal)spline.Interpolate(t);
- return Math.Clamp(interpolated, (decimal)y[^1], (decimal)y[0]);
- }
-
- return XAxisMaxValue;
- }
}
}

ADD
WalletWasabi.Fluent/Views/Wallets/Send/FeeSliderView.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"
+ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+ x:Class="WalletWasabi.Fluent.Views.Wallets.Send.FeeSliderView">
+ <UserControl.Styles>
+ <!-- Slider Fee -->
+ <Style Selector="Slider.cursor">
+ <Setter Property="RenderTransform">
+ <RotateTransform Angle="-180" />
+ </Setter>
+ </Style>
+ <Style Selector="Slider.cursor /template/ RepeatButton#PART_IncreaseButton">
+ <Setter Property="Background" Value="{DynamicResource SliderTrackValueFill}" />
+ </Style>
+ <Style Selector="Slider:pressed.cursor /template/ RepeatButton#PART_IncreaseButton">
+ <Setter Property="Background" Value="{DynamicResource SliderTrackValueFillPressed}" />
+ </Style>
+ <Style Selector="Slider:pointerover.cursor /template/ RepeatButton#PART_IncreaseButton">
+ <Setter Property="Background" Value="{DynamicResource SliderTrackValueFillPointerOver}" />
+ </Style>
+ <Style Selector="Slider.cursor /template/ RepeatButton#PART_DecreaseButton">
+ <Setter Property="Background" Value="{DynamicResource SliderTrackFill}" />
+ </Style>
+ <Style Selector="Slider:pressed.cursor /template/ RepeatButton#PART_DecreaseButton">
+ <Setter Property="Background" Value="{DynamicResource SliderTrackFillPressed}" />
+ </Style>
+ <Style Selector="Slider:pointerover.cursor /template/ RepeatButton#PART_DecreaseButton">
+ <Setter Property="Background" Value="{DynamicResource SliderTrackFillPointerOver}" />
+ </Style>
+ <Style Selector="Slider:horizontal.cursor">
+ <Setter Property="Background" Value="{DynamicResource SliderTrackFill}" />
+ </Style>
+ <!-- LineChart Labels -->
+ <Style Selector="c|LineChart.xAxisLabels">
+ <Setter Property="XAxisLabelForeground" Value="{DynamicResource TextForegroundColor}" />
+ <Setter Property="XAxisLabelOpacity" Value="0.5" />
+ <Setter Property="XAxisLabelAngle" Value="0" />
+ <Setter Property="XAxisLabelOffset" Value="0,20" />
+ <Setter Property="XAxisLabelAlignment" Value="Center" />
+ <Setter Property="XAxisLabelFontFamily" Value="Arial" />
+ <Setter Property="XAxisLabelFontStyle" Value="Normal" />
+ <Setter Property="XAxisLabelFontWeight" Value="Normal" />
+ <Setter Property="XAxisLabelFontSize" Value="12" />
+ </Style>
+ <!-- LineChart Cursor -->
+ <Style Selector="c|LineChart.cursor">
+ <Setter Property="CursorStroke" Value="#72BD81" />
+ <Setter Property="CursorStrokeThickness" Value="2" />
+ <!-- <Setter Property="CursorStrokeDashStyle" Value="{x:Static DashStyle.Dash}" /> -->
+ <Setter Property="CursorStrokeLineCap" Value="Round" />
+ </Style>
+ <!-- LineChart XAxis -->
+ <Style Selector="c|LineChart.xAxis">
+ <Setter Property="XAxisOffset" Value="0,0" />
+ <Setter Property="XAxisOpacity" Value="0.5" />
+ <Setter Property="XAxisStroke" Value="{DynamicResource TextForegroundColor}" />
+ <Setter Property="XAxisStrokeThickness" Value="0.7" />
+ <Setter Property="XAxisArrowSize" Value="3.5" />
+ <Setter Property="XAxisMinViableWidth" Value="0" />
+ <Setter Property="XAxisMinViableHeight" Value="0" />
+ </Style>
+ <!-- LineChart YAxis -->
+ <Style Selector="c|LineChart.yAxis">
+ <Setter Property="YAxisOffset" Value="-10,-115" />
+ <Setter Property="YAxisOpacity" Value="0.5" />
+ <Setter Property="YAxisStroke" Value="{DynamicResource TextForegroundColor}" />
+ <Setter Property="YAxisStrokeThickness" Value="0.7" />
+ <Setter Property="YAxisArrowSize" Value="3.5" />
+ <Setter Property="YAxisMinViableWidth" Value="0" />
+ <Setter Property="YAxisMinViableHeight" Value="120" />
+ </Style>
+ <!-- LineChart YAxis Title -->
+ <Style Selector="c|LineChart.yAxisTitle">
+ <Setter Property="YAxisTitleForeground" Value="{DynamicResource TextForegroundColor}" />
+ <Setter Property="YAxisTitleOpacity" Value="0.5" />
+ <Setter Property="YAxisTitleAngle" Value="-90" />
+ <Setter Property="YAxisTitleOffset" Value="-55,-75" />
+ <Setter Property="YAxisTitleSize" Value="90,20" />
+ <Setter Property="YAxisTitleAlignment" Value="Center" />
+ <Setter Property="YAxisTitleFontFamily" Value="Arial" />
+ <Setter Property="YAxisTitleFontStyle" Value="Normal" />
+ <Setter Property="YAxisTitleFontWeight" Value="Normal" />
+ <Setter Property="YAxisTitleFontSize" Value="12" />
+ </Style>
+ <!-- LineChart Border -->
+ <Style Selector="c|LineChart.border">
+ <Setter Property="BorderBrush" Value="Transparent" />
+ <Setter Property="BorderThickness" Value="1" />
+ <Setter Property="BorderRadiusX" Value="4" />
+ <Setter Property="BorderRadiusY" Value="4" />
+ </Style>
+ </UserControl.Styles>
+ <UserControl.KeyBindings>
+ <KeyBinding Gesture="Enter" Command="{Binding NextCommand}" />
+ </UserControl.KeyBindings>
+ <c:ContentArea Title="{Binding Title}"
+ CancelContent="Cancel"
+ EnableCancel="{Binding EnableCancel}"
+ EnableBack="{Binding EnableBack}"
+ EnableNext="True" NextContent="Continue"
+ IsBusy="{Binding IsBusy}">
+ <DockPanel>
+ <Label Margin="0 10 0 20" Content="_Priority:" Target="CursorValue2" />
+ <DockPanel MinHeight="80">
+ <DockPanel.Resources>
+ <Thickness x:Key="SliderTopHeaderMargin">0,0,0,4</Thickness>
+ <GridLength x:Key="SliderPreContentMargin">0</GridLength>
+ <GridLength x:Key="SliderPostContentMargin">0</GridLength>
+ </DockPanel.Resources>
+ <Slider x:Name="CursorValue2"
+ Margin="18,0,18,0"
+ KeyboardNavigation.IsTabStop="False"
+ DockPanel.Dock="Bottom"
+ Classes="cursor"
+ Focusable="True"
+ Minimum="{Binding XAxisMinValue}"
+ Maximum="{Binding XAxisMaxValue}"
+ Value="{Binding XAxisCurrentValueIndex}" />
+ <c:LineChart x:Name="GradientLineChart"
+ XAxisPlotMode="EvenlySpaced"
+ Margin="18,10,18,0"
+ Classes="cursor border xAxisLabels yAxis yAxisTitle"
+ MinHeight="150"
+ XAxisLabels="{Binding XAxisLabels}"
+ XAxisValues="{Binding XAxisValues}"
+ XAxisCurrentValue="{Binding XAxisCurrentValue, Mode=TwoWay}"
+ YAxisValues="{Binding YAxisValues}"
+ YAxisLogarithmicScale="False"
+ AreaMargin="10,0,10,30"
+ AreaStrokeThickness="2"
+ AreaStrokeLineCap="Round"
+ AreaMinViableWidth="40"
+ AreaMinViableHeight="20"
+ Focusable="True"
+ YAxisTitle="More Expensive">
+ <c:LineChart.AreaFill>
+ <LinearGradientBrush StartPoint="50%,0%" EndPoint="50%,100%" Opacity="0.2">
+ <LinearGradientBrush.GradientStops>
+ <GradientStop Offset="0.0" Color="#FF72BD81" />
+ <GradientStop Offset="1.0" Color="#0072BD81" />
+ </LinearGradientBrush.GradientStops>
+ </LinearGradientBrush>
+ </c:LineChart.AreaFill>
+ <c:LineChart.AreaStroke>
+ <LinearGradientBrush StartPoint="0%,50%" EndPoint="100%,50%">
+ <LinearGradientBrush.GradientStops>
+ <GradientStop Offset="0.0" Color="#72BD81" />
+ <GradientStop Offset="1.0" Color="#72BD81" />
+ </LinearGradientBrush.GradientStops>
+ </LinearGradientBrush>
+ </c:LineChart.AreaStroke>
+ </c:LineChart>
+ </DockPanel>
+ </DockPanel>
+
+ </c:ContentArea>
+</UserControl>
\ No newline at end of file

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

MODIFY
WalletWasabi.Fluent/Views/Wallets/Send/SendView.axaml
WalletWasabi.Fluent/Views/Wallets/Send/SendView.axaml
x:DataType="vm:SendViewModel"
x:CompileBindings="True"
x:Class="WalletWasabi.Fluent.Views.Wallets.Send.SendView">
- <UserControl.Styles>
- <!-- Slider Fee -->
- <Style Selector="Slider.cursor">
- <Setter Property="RenderTransform">
- <RotateTransform Angle="-180" />
- </Setter>
- </Style>
- <Style Selector="Slider.cursor /template/ RepeatButton#PART_IncreaseButton">
- <Setter Property="Background" Value="{DynamicResource SliderTrackValueFill}" />
- </Style>
- <Style Selector="Slider:pressed.cursor /template/ RepeatButton#PART_IncreaseButton">
- <Setter Property="Background" Value="{DynamicResource SliderTrackValueFillPressed}" />
- </Style>
- <Style Selector="Slider:pointerover.cursor /template/ RepeatButton#PART_IncreaseButton">
- <Setter Property="Background" Value="{DynamicResource SliderTrackValueFillPointerOver}" />
- </Style>
- <Style Selector="Slider.cursor /template/ RepeatButton#PART_DecreaseButton">
- <Setter Property="Background" Value="{DynamicResource SliderTrackFill}" />
- </Style>
- <Style Selector="Slider:pressed.cursor /template/ RepeatButton#PART_DecreaseButton">
- <Setter Property="Background" Value="{DynamicResource SliderTrackFillPressed}" />
- </Style>
- <Style Selector="Slider:pointerover.cursor /template/ RepeatButton#PART_DecreaseButton">
- <Setter Property="Background" Value="{DynamicResource SliderTrackFillPointerOver}" />
- </Style>
- <Style Selector="Slider:horizontal.cursor">
- <Setter Property="Background" Value="{DynamicResource SliderTrackFill}" />
- </Style>
- <!-- LineChart Labels -->
- <Style Selector="c|LineChart.xAxisLabels">
- <Setter Property="XAxisLabelForeground" Value="{DynamicResource TextForegroundColor}" />
- <Setter Property="XAxisLabelOpacity" Value="0.5" />
- <Setter Property="XAxisLabelAngle" Value="0" />
- <Setter Property="XAxisLabelOffset" Value="0,20" />
- <Setter Property="XAxisLabelAlignment" Value="Center" />
- <Setter Property="XAxisLabelFontFamily" Value="Arial" />
- <Setter Property="XAxisLabelFontStyle" Value="Normal" />
- <Setter Property="XAxisLabelFontWeight" Value="Normal" />
- <Setter Property="XAxisLabelFontSize" Value="12" />
- </Style>
- <!-- LineChart Cursor -->
- <Style Selector="c|LineChart.cursor">
- <Setter Property="CursorStroke" Value="#72BD81" />
- <Setter Property="CursorStrokeThickness" Value="2" />
- <!-- <Setter Property="CursorStrokeDashStyle" Value="{x:Static DashStyle.Dash}" /> -->
- <Setter Property="CursorStrokeLineCap" Value="Round" />
- </Style>
- <!-- LineChart XAxis -->
- <Style Selector="c|LineChart.xAxis">
- <Setter Property="XAxisOffset" Value="0,0" />
- <Setter Property="XAxisOpacity" Value="0.5" />
- <Setter Property="XAxisStroke" Value="{DynamicResource TextForegroundColor}" />
- <Setter Property="XAxisStrokeThickness" Value="0.7" />
- <Setter Property="XAxisArrowSize" Value="3.5" />
- <Setter Property="XAxisMinViableWidth" Value="0"/>
- <Setter Property="XAxisMinViableHeight" Value="0"/>
- </Style>
- <!-- LineChart YAxis -->
- <Style Selector="c|LineChart.yAxis">
- <Setter Property="YAxisOffset" Value="-10,-115" />
- <Setter Property="YAxisOpacity" Value="0.5" />
- <Setter Property="YAxisStroke" Value="{DynamicResource TextForegroundColor}" />
- <Setter Property="YAxisStrokeThickness" Value="0.7" />
- <Setter Property="YAxisArrowSize" Value="3.5" />
- <Setter Property="YAxisMinViableWidth" Value="0"/>
- <Setter Property="YAxisMinViableHeight" Value="120"/>
- </Style>
- <!-- LineChart YAxis Title -->
- <Style Selector="c|LineChart.yAxisTitle">
- <Setter Property="YAxisTitleForeground" Value="{DynamicResource TextForegroundColor}" />
- <Setter Property="YAxisTitleOpacity" Value="0.5" />
- <Setter Property="YAxisTitleAngle" Value="-90" />
- <Setter Property="YAxisTitleOffset" Value="-55,-75" />
- <Setter Property="YAxisTitleSize" Value="90,20" />
- <Setter Property="YAxisTitleAlignment" Value="Center" />
- <Setter Property="YAxisTitleFontFamily" Value="Arial" />
- <Setter Property="YAxisTitleFontStyle" Value="Normal" />
- <Setter Property="YAxisTitleFontWeight" Value="Normal" />
- <Setter Property="YAxisTitleFontSize" Value="12" />
- </Style>
- <!-- LineChart Border -->
- <Style Selector="c|LineChart.border">
- <Setter Property="BorderBrush" Value="Transparent" />
- <Setter Property="BorderThickness" Value="1" />
- <Setter Property="BorderRadiusX" Value="4" />
- <Setter Property="BorderRadiusY" Value="4" />
- </Style>
- </UserControl.Styles>
<c:ContentArea Title="Send" Caption=""
EnableBack="{Binding EnableBack}"
EnableNext="True"
IsBusy="{Binding IsBusy}"
ScrollViewer.VerticalScrollBarVisibility="Auto">
- <DockPanel>
+ <DockPanel LastChildFill="False">
<DockPanel.Styles>
<Style Selector="DockPanel > Label">
<Setter Property="MinWidth" Value="80" />
Items="{Binding Labels}"
Suggestions="{Binding PriorLabels}" />
</DockPanel>
-
- <Separator DockPanel.Dock="Top"
- HorizontalAlignment="Stretch"
- Margin="-200 20 0 10" />
-
- <DockPanel>
- <Label Margin="0 10 0 20" Content="_Priority:" Target="CursorValue2" />
- <DockPanel MinHeight="80">
- <DockPanel.Resources>
- <Thickness x:Key="SliderTopHeaderMargin">0,0,0,4</Thickness>
- <GridLength x:Key="SliderPreContentMargin">0</GridLength>
- <GridLength x:Key="SliderPostContentMargin">0</GridLength>
- </DockPanel.Resources>
- <Slider x:Name="CursorValue2"
- Margin="18,0,18,0"
- KeyboardNavigation.IsTabStop="False"
- DockPanel.Dock="Bottom"
- Classes="cursor"
- Focusable="True"
- Minimum="{Binding XAxisMinValue}"
- Maximum="{Binding XAxisMaxValue}"
- Value="{Binding XAxisCurrentValueIndex}">
- </Slider>
- <c:LineChart x:Name="GradientLineChart"
- XAxisPlotMode="EvenlySpaced"
- Margin="18,10,18,0"
- Classes="cursor border xAxisLabels yAxis yAxisTitle"
- MinHeight="150"
- XAxisLabels="{Binding XAxisLabels}"
- XAxisValues="{Binding XAxisValues}"
- XAxisCurrentValue="{Binding XAxisCurrentValue, Mode=TwoWay}"
- YAxisValues="{Binding YAxisValues}"
- YAxisLogarithmicScale="False"
- AreaMargin="10,0,10,30"
- AreaStrokeThickness="2"
- AreaStrokeLineCap="Round"
- AreaMinViableWidth="40"
- AreaMinViableHeight="20"
- Focusable="True"
- YAxisTitle="More Expensive">
- <c:LineChart.AreaFill>
- <LinearGradientBrush StartPoint="50%,0%" EndPoint="50%,100%" Opacity="0.2">
- <LinearGradientBrush.GradientStops>
- <GradientStop Offset="0.0" Color="#FF72BD81" />
- <GradientStop Offset="1.0" Color="#0072BD81" />
- </LinearGradientBrush.GradientStops>
- </LinearGradientBrush>
- </c:LineChart.AreaFill>
- <c:LineChart.AreaStroke>
- <LinearGradientBrush StartPoint="0%,50%" EndPoint="100%,50%">
- <LinearGradientBrush.GradientStops>
- <GradientStop Offset="0.0" Color="#72BD81" />
- <GradientStop Offset="1.0" Color="#72BD81" />
- </LinearGradientBrush.GradientStops>
- </LinearGradientBrush>
- </c:LineChart.AreaStroke>
- </c:LineChart>
-
- </DockPanel>
-
- </DockPanel>
-
-
</DockPanel>
-
</c:ContentArea>
</UserControl>

separate fee slider into a different view.