Re-implement QR save method as a RxCommand. Also fixes the MVVM violation in AddressViewModel.
MODIFY
WalletWasabi.Gui/Controls/QrCode.cs
WalletWasabi.Gui/Controls/QrCode.cs
using Avalonia;
using Avalonia.Controls;
+using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Metadata;
using Avalonia.Platform;
using ReactiveUI;
using System;
+using System.IO;
+using System.Reactive;
using System.Reactive.Linq;
+using System.Threading.Tasks;
using WalletWasabi.Helpers;
using WalletWasabi.Logging;
@@ -33,22 +37,41 @@ namespace WalletWasabi.Gui.Controls
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(x => FinalMatrix = AddPaddingToMatrix(x));
- this.WhenAnyValue(x => x.QRImageSavePath)
- .Where(x => !string.IsNullOrWhiteSpace(x) || !string.IsNullOrEmpty(x))
- .Where(x => FinalMatrix != null)
- .ObserveOn(RxApp.MainThreadScheduler)
- .Subscribe(x => GenerateQRCodeBitmap(x));
+ QRImageSaveCommand = ReactiveCommand.CreateFromTask<string, Unit>(SaveQRCodeAsync);
}
- private void GenerateQRCodeBitmap(string x)
+ public async Task<Unit> SaveQRCodeAsync(string address)
+ {
+ if (FinalMatrix is null)
+ {
+ return Unit.Default;
+ }
+
+ var sfd = new SaveFileDialog();
+
+ sfd.InitialFileName = $"{address}.png";
+ sfd.Directory = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
+ sfd.Filters.Add(new FileDialogFilter() { Name = "Portable Network Graphics (PNG) Image file", Extensions = { "png" } });
+
+ var visualRoot = (ClassicDesktopStyleApplicationLifetime)Application.Current.ApplicationLifetime;
+ var path = await sfd.ShowAsync(visualRoot.MainWindow);
+
+ if (!string.IsNullOrWhiteSpace(path))
+ {
+ var ext = Path.GetExtension(path);
+
+ if (string.IsNullOrWhiteSpace(ext) || ext.ToLowerInvariant() != "png")
{
+ path = $"{path}.png";
+ }
+
try
{
var pixSize = PixelSize.FromSize(CoercedSize, 1);
using var rtb = new RenderTargetBitmap(pixSize);
rtb.Render(this);
- rtb.Save(x);
+ rtb.Save(path);
}
catch (Exception ex)
{
@@ -56,18 +79,21 @@ namespace WalletWasabi.Gui.Controls
}
}
- public static readonly DirectProperty<QrCode, string> QRImageSavePathProperty =
- AvaloniaProperty.RegisterDirect<QrCode, string>(
- nameof(QRImageSavePath),
- o => o.QRImageSavePath,
- (o, v) => o.QRImageSavePath = v);
+ return Unit.Default;
+ }
+
+ public static readonly DirectProperty<QrCode, ReactiveCommand<string, Unit>> QRImageSaveCommandProperty =
+ AvaloniaProperty.RegisterDirect<QrCode, ReactiveCommand<string, Unit>>(
+ nameof(QRImageSaveCommand),
+ o => o.QRImageSaveCommand,
+ (o, v) => o.QRImageSaveCommand = v);
- private string _qRImageSavePath;
+ private ReactiveCommand<string, Unit> _qRImageSavePath;
- public string QRImageSavePath
+ public ReactiveCommand<string, Unit> QRImageSaveCommand
{
get => _qRImageSavePath;
- set => SetAndRaise(QRImageSavePathProperty, ref _qRImageSavePath, value);
+ set => SetAndRaise(QRImageSaveCommandProperty, ref _qRImageSavePath, value);
}
public static readonly DirectProperty<QrCode, bool[,]> MatrixProperty =

MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/ReceiveTabView.xaml
WalletWasabi.Gui/Controls/WalletExplorer/ReceiveTabView.xaml
<Grid>
<Expander Name="coinExpander" ExpandDirection="Down" IsExpanded="{Binding IsExpanded}" Classes="coloredExpander" Background="{Binding ElementName=coinExpander, Path=IsExpanded, Converter={StaticResource CoinItemExpanderColorConverter}}">
<StackPanel Orientation="Horizontal" Spacing="16" Margin="35 10 0 5">
- <controls:QrCode Height="220" Matrix="{Binding QrCode}" QRImageSavePath="{Binding QRImageSavePath, Mode=TwoWay}" HorizontalAlignment="Left" />
+ <controls:QrCode Height="220" Matrix="{Binding QrCode}" QRImageSaveCommand="{Binding ExecuteSaveQRCodeCommand, Mode=OneWayToSource}" HorizontalAlignment="Left" />
<Grid ColumnDefinitions="140, *" RowDefinitions="22,22" Margin="6">
<TextBlock Classes="monospaceFont" Text="Public key:" Grid.Row="0" Grid.Column="0" />
<controls:ExtendedTextBox Classes="selectableTextBlock monospaceFont Transparent" Text="{Binding PubKey}" Grid.Row="0" Grid.Column="1" />

MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/ReceiveTabViewModel.cs
WalletWasabi.Gui/Controls/WalletExplorer/ReceiveTabViewModel.cs
@@ -15,8 +15,6 @@ using WalletWasabi.Blockchain.Analysis.Clustering;
using WalletWasabi.Blockchain.Keys;
using WalletWasabi.Gui.Tabs.WalletManager;
using WalletWasabi.Gui.ViewModels;
-using WalletWasabi.KeyManagement;
-using WalletWasabi.Models;
using WalletWasabi.Logging;
using WalletWasabi.Hwi;
using WalletWasabi.Hwi.Exceptions;

MODIFY
WalletWasabi.Gui/ViewModels/AddressViewModel.cs
WalletWasabi.Gui/ViewModels/AddressViewModel.cs
using Avalonia;
-using Avalonia.Controls;
-using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Threading;
using Gma.QrCodeNet.Encoding;
using ReactiveUI;
@@ -27,8 +25,8 @@ namespace WalletWasabi.Gui.ViewModels
private double _clipboardNotificationOpacity;
private string _label;
private bool _inEditMode;
- private string _qRImageSavePath;
private ObservableAsPropertyHelper<string> _expandMenuCaption;
+ private ReactiveCommand<string, Unit> _executeSaveQRCodeCommand;
public HdPubKey Model { get; }
public Global Global { get; }
@@ -138,40 +136,16 @@ namespace WalletWasabi.Gui.ViewModels
set => this.RaiseAndSetIfChanged(ref _qrCode, value);
}
- public string QRImageSavePath
+ public ReactiveCommand<string, Unit> ExecuteSaveQRCodeCommand
{
- get => _qRImageSavePath;
- set => this.RaiseAndSetIfChanged(ref _qRImageSavePath, value);
+ get => _executeSaveQRCodeCommand;
+ set => this.RaiseAndSetIfChanged(ref _executeSaveQRCodeCommand, value, nameof(ExecuteSaveQRCodeCommand));
}
public string ExpandMenuCaption => _expandMenuCaption?.Value ?? string.Empty;
public CancellationTokenSource CancelClipboardNotification { get; set; }
- public async Task SaveQRCodeAsync()
- {
- var sfd = new SaveFileDialog();
-
- sfd.InitialFileName = $"{Address}.png";
- sfd.Directory = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
- sfd.Filters.Add(new FileDialogFilter() { Name = "Portable Network Graphics (PNG) Image file", Extensions = { "png" } });
-
- var visualRoot = (ClassicDesktopStyleApplicationLifetime)Application.Current.ApplicationLifetime;
- var path = await sfd.ShowAsync(visualRoot.MainWindow);
-
- if (!string.IsNullOrWhiteSpace(path))
- {
- var ext = Path.GetExtension(path);
-
- if (string.IsNullOrWhiteSpace(ext) || ext.ToLowerInvariant() != "png")
- {
- path = $"{path}.png";
- }
-
- QRImageSavePath = path;
- }
- }
-
public async Task TryCopyToClipboardAsync()
{
try
@@ -211,6 +185,11 @@ namespace WalletWasabi.Gui.ViewModels
}
}
+ public async Task SaveQRCodeAsync()
+ {
+ await ExecuteSaveQRCodeCommand.Execute(Address);
+ }
+
#region IDisposable Support
private volatile bool _disposedValue = false; // To detect redundant calls

Re-implement QR save method as a RxCommand. Also fixes the MVVM violation in AddressViewModel.