CoinViewModel dispose implemetation
MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/CoinJoinTabViewModel.cs
WalletWasabi.Gui/Controls/WalletExplorer/CoinJoinTabViewModel.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
+using System.Reactive.Disposables;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Controls;
@@ -22,7 +23,7 @@ using static WalletWasabi.Models.ServiceConfiguration;
namespace WalletWasabi.Gui.Controls.WalletExplorer
{
- public class CoinJoinTabViewModel : WalletActionViewModel
+ public class CoinJoinTabViewModel : WalletActionViewModel, IDisposable
{
private CoinListViewModel _coinsList;
private long _roundId;
@@ -43,6 +44,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
private string _dequeueButtonText;
private const string DequeueButtonTextString = "Dequeue Selected Coins";
private const string DequeuingButtonTextString = "Dequeuing coins...";
+ private CompositeDisposable _disposables = new CompositeDisposable();
public CoinJoinTabViewModel(WalletViewModel walletViewModel)
: base("CoinJoin", walletViewModel)
@@ -72,8 +74,6 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
CoinsList = new CoinListViewModel();
}
- //CoinsList = new CoinListViewModel(coins);
-
AmountQueued = Money.Zero;// Global.ChaumianClient.State.SumAllQueuedCoinAmounts();
Global.ChaumianClient.CoinQueued += ChaumianClient_CoinQueued;
@@ -101,25 +101,25 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
EnqueueCommand = ReactiveCommand.Create(async () =>
{
await DoEnqueueAsync(CoinsList.Coins.Where(c => c.IsSelected));
- });
+ }).DisposeWith(_disposables);
DequeueCommand = ReactiveCommand.Create(async () =>
{
await DoDequeueAsync(CoinsList.Coins.Where(c => c.IsSelected));
- });
+ }).DisposeWith(_disposables);
PrivacySomeCommand = ReactiveCommand.Create(() =>
{
TargetPrivacy = TargetPrivacy.Some;
- });
+ }).DisposeWith(_disposables);
PrivacyFineCommand = ReactiveCommand.Create(() =>
{
TargetPrivacy = TargetPrivacy.Fine;
- });
+ }).DisposeWith(_disposables);
PrivacyStrongCommand = ReactiveCommand.Create(() =>
{
TargetPrivacy = TargetPrivacy.Strong;
- });
+ }).DisposeWith(_disposables);
TargetButtonCommand = ReactiveCommand.Create(async () =>
{
switch (TargetPrivacy)
@@ -142,7 +142,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
}
Global.Config.MixUntilAnonymitySet = CoinJoinUntilAnonimitySet;
await Global.Config.ToFileAsync();
- });
+ }).DisposeWith(_disposables);
this.WhenAnyValue(x => x.Password).Subscribe(async x =>
{
@@ -155,7 +155,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
await DoEnqueueAsync(CoinsList.Coins.Where(c => c.IsSelected));
}
}
- });
+ }).DisposeWith(_disposables);
this.WhenAnyValue(x => x.IsEnqueueBusy).Subscribe(busy =>
{
@@ -167,7 +167,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
{
EnqueueButtonText = EnqueueButtonTextString;
}
- });
+ }).DisposeWith(_disposables);
this.WhenAnyValue(x => x.IsDequeueBusy).Subscribe(busy =>
{
@@ -179,12 +179,12 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
{
DequeueButtonText = DequeueButtonTextString;
}
- });
+ }).DisposeWith(_disposables);
this.WhenAnyValue(x => x.TargetPrivacy).Subscribe(target =>
{
CoinJoinUntilAnonimitySet = GetTargetLevel(target);
- });
+ }).DisposeWith(_disposables);
}
private async Task DoDequeueAsync(IEnumerable<CoinViewModel> selectedCoins)
@@ -510,5 +510,39 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
public ReactiveCommand PrivacyFineCommand { get; }
public ReactiveCommand PrivacyStrongCommand { get; }
public ReactiveCommand TargetButtonCommand { get; }
+
+ #region IDisposable Support
+
+ private bool _disposedValue = false; // To detect redundant calls
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_disposedValue)
+ {
+ if (disposing)
+ {
+ Global.ChaumianClient.CoinQueued -= ChaumianClient_CoinQueued;
+ Global.ChaumianClient.CoinDequeued -= ChaumianClient_CoinDequeued;
+ Global.ChaumianClient.StateUpdated -= ChaumianClient_StateUpdated;
+ _coinsList.DequeueCoinsPressed -= CoinsList_DequeueCoinsPressedAsync;
+ _disposables.Dispose();
+ CoinsList.Dispose();
+ }
+
+ CoinsList = null;
+ _disposedValue = true;
+ }
+ }
+
+ // This code added to correctly implement the disposable pattern.
+ public void Dispose()
+ {
+ // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
+ Dispose(true);
+ // TODO: uncomment the following line if the finalizer is overridden above.
+ // GC.SuppressFinalize(this);
+ }
+
+ #endregion IDisposable Support
}
}

MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/CoinListViewModel.cs
WalletWasabi.Gui/Controls/WalletExplorer/CoinListViewModel.cs
@@ -15,10 +15,11 @@ using Avalonia.Threading;
using DynamicData;
using DynamicData.Binding;
using System.Threading.Tasks;
+using System.Reactive.Disposables;
namespace WalletWasabi.Gui.Controls.WalletExplorer
{
- public class CoinListViewModel : ViewModelBase
+ public class CoinListViewModel : ViewModelBase, IDisposable
{
public ReadOnlyObservableCollection<CoinViewModel> Coins => _coinViewModels;
private readonly ReadOnlyObservableCollection<CoinViewModel> _coinViewModels;
@@ -43,6 +44,8 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
private bool? _selectNonPrivateCheckBoxState;
private GridLength _coinJoinStatusWidth;
private SortOrder _historySortDirection;
+ private List<CoinViewModel> _removedCoinViewModels = new List<CoinViewModel>();
+ private CompositeDisposable _disposables = new CompositeDisposable();
public ReactiveCommand EnqueueCoin { get; }
public ReactiveCommand DequeueCoin { get; }
@@ -194,19 +197,15 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
AmountSortDirection = SortOrder.Decreasing;
RefreshOrdering();
- var sortChanged = this.WhenValueChanged(@this => MyComparer)
- .Select(_ =>
- MyComparer);
+ var sortChanged = this.WhenValueChanged(@this => MyComparer).Select(_ => MyComparer);
_rootlist.Connect()
- .OnItemAdded(cvm =>
- cvm.PropertyChanged += Coin_PropertyChanged)
- //.OnItemRemoved(cvm => //TODO: possible memory leak. If I uncomment this line, that Unspent propchange not triggered in some cases => spent money stays in list
- // cvm.PropertyChanged -= Coin_PropertyChanged)
+ .OnItemAdded(cvm => cvm.PropertyChanged += Coin_PropertyChanged)
+ .OnItemRemoved(cvm => _removedCoinViewModels.Add(cvm)) //TODO: fix and test. If I directly unsubscribe from Coin_PropertyChanged then Unspent propchange not triggered in some cases => spent money stays in list
.Sort(MyComparer, comparerChanged: sortChanged, resetThreshold: 5)
.Bind(out _coinViewModels)
.ObserveOn(RxApp.MainThreadScheduler)
- .Subscribe();
+ .Subscribe().DisposeWith(_disposables);
foreach (var sc in Global.WalletService.Coins.Where(sc => sc.Unspent))
{
@@ -225,7 +224,8 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
}
if (x != SortOrder.None)
RefreshOrdering();
- });
+ }).DisposeWith(_disposables);
+
this.WhenAnyValue(x => x.HistorySortDirection).Subscribe(x =>
{
if (x != SortOrder.None)
@@ -236,7 +236,8 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
}
if (x != SortOrder.None)
RefreshOrdering();
- });
+ }).DisposeWith(_disposables);
+
this.WhenAnyValue(x => x.StatusSortDirection).Subscribe(x =>
{
if (x != SortOrder.None)
@@ -247,7 +248,8 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
}
if (x != SortOrder.None)
RefreshOrdering();
- });
+ }).DisposeWith(_disposables);
+
this.WhenAnyValue(x => x.PrivacySortDirection).Subscribe(x =>
{
if (x != SortOrder.None)
@@ -258,19 +260,19 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
}
if (x != SortOrder.None)
RefreshOrdering();
- });
+ }).DisposeWith(_disposables);
EnqueueCoin = ReactiveCommand.Create(() =>
{
if (SelectedCoin == null) return;
//await Global.ChaumianClient.QueueCoinsToMixAsync()
- });
+ }).DisposeWith(_disposables);
DequeueCoin = ReactiveCommand.Create(() =>
{
if (SelectedCoin == null) return;
DequeueCoinsPressed?.Invoke();
- }, this.WhenAnyValue(x => x.CanDeqeue));
+ }, this.WhenAnyValue(x => x.CanDeqeue)).DisposeWith(_disposables);
SelectAllCheckBoxCommand = ReactiveCommand.Create(() =>
{
@@ -290,7 +292,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
SelectAllCheckBoxState = false;
break;
}
- });
+ }).DisposeWith(_disposables);
SelectPrivateCheckBoxCommand = ReactiveCommand.Create(() =>
{
@@ -309,7 +311,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
SelectPrivateCheckBoxState = false;
break;
}
- });
+ }).DisposeWith(_disposables);
SelectNonPrivateCheckBoxCommand = ReactiveCommand.Create(() =>
{
@@ -328,7 +330,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
SelectNonPrivateCheckBoxState = false;
break;
}
- });
+ }).DisposeWith(_disposables);
SetSelections();
SetCoinJoinStatusWidth();
}
@@ -416,5 +418,48 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
}
});
}
+
+ #region IDisposable Support
+
+ private bool _disposedValue = false; // To detect redundant calls
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_disposedValue)
+ {
+ if (disposing)
+ {
+ Global.WalletService.Coins.CollectionChanged -= Coins_CollectionGlobalChanged;
+
+ foreach (var cvm in _rootlist.Items)
+ {
+ cvm.PropertyChanged -= Coin_PropertyChanged;
+ cvm.Dispose();
+ }
+ foreach (var cvm in _removedCoinViewModels)
+ {
+ cvm.PropertyChanged -= Coin_PropertyChanged;
+ cvm.Dispose();
+ }
+
+ _rootlist.Dispose();
+ _disposables.Dispose();
+ }
+ _rootlist = null;
+
+ _disposedValue = true;
+ }
+ }
+
+ // This code added to correctly implement the disposable pattern.
+ public void Dispose()
+ {
+ // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
+ Dispose(true);
+ // TODO: uncomment the following line if the finalizer is overridden above.
+ // GC.SuppressFinalize(this);
+ }
+
+ #endregion IDisposable Support
}
}

MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/CoinViewModel.cs
WalletWasabi.Gui/Controls/WalletExplorer/CoinViewModel.cs
@@ -11,13 +11,15 @@ using WalletWasabi.Gui.Models;
using WalletWasabi.Models.ChaumianCoinJoin;
using System.Globalization;
using System.Threading.Tasks;
+using System.Reactive.Disposables;
namespace WalletWasabi.Gui.Controls.WalletExplorer
{
- public class CoinViewModel : ViewModelBase
+ public class CoinViewModel : ViewModelBase, IDisposable
{
private bool _isSelected;
private SmartCoinStatus _smartCoinStatus;
+ private CompositeDisposable _disposables = new CompositeDisposable();
public CoinViewModel(SmartCoin model)
{
@@ -27,33 +29,33 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
{
RefreshSmartCoinStatus();
this.RaisePropertyChanged(nameof(Confirmed));
- });
+ }).DisposeWith(_disposables);
model.WhenAnyValue(x => x.SpentOrCoinJoinInProgress).ObserveOn(RxApp.MainThreadScheduler).Subscribe(_ =>
{
this.RaisePropertyChanged(nameof(SpentOrCoinJoinInProgress));
- });
+ }).DisposeWith(_disposables);
model.WhenAnyValue(x => x.CoinJoinInProgress).ObserveOn(RxApp.MainThreadScheduler).Subscribe(_ =>
{
RefreshSmartCoinStatus();
this.RaisePropertyChanged(nameof(CoinJoinInProgress));
- });
+ }).DisposeWith(_disposables);
model.WhenAnyValue(x => x.IsBanned).ObserveOn(RxApp.MainThreadScheduler).Subscribe(_ =>
{
RefreshSmartCoinStatus();
- });
+ }).DisposeWith(_disposables);
model.WhenAnyValue(x => x.SpentAccordingToBackend).ObserveOn(RxApp.MainThreadScheduler).Subscribe(_ =>
{
RefreshSmartCoinStatus();
- });
+ }).DisposeWith(_disposables);
model.WhenAnyValue(x => x.Unspent).ObserveOn(RxApp.MainThreadScheduler).Subscribe(_ =>
{
this.RaisePropertyChanged(nameof(Unspent));
- });
+ }).DisposeWith(_disposables);
model.WhenAnyValue(x => x.History).ObserveOn(RxApp.MainThreadScheduler).Subscribe(_ =>
{
@@ -63,13 +65,13 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
this.WhenAnyValue(x => x.Status).Subscribe(_ =>
{
this.RaisePropertyChanged(nameof(ToolTip));
- });
+ }).DisposeWith(_disposables);
Global.Synchronizer.WhenAnyValue(x => x.BestBlockchainHeight).ObserveOn(RxApp.MainThreadScheduler).Subscribe(_ =>
{
RefreshSmartCoinStatus();
this.RaisePropertyChanged(nameof(Confirmations));
- });
+ }).DisposeWith(_disposables);
Global.ChaumianClient.StateUpdated += ChaumianClient_StateUpdated;
}
@@ -224,5 +226,34 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
}
}
}
+
+ #region IDisposable Support
+
+ private bool _disposedValue = false; // To detect redundant calls
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_disposedValue)
+ {
+ if (disposing)
+ {
+ _disposables.Dispose();
+ Global.ChaumianClient.StateUpdated -= ChaumianClient_StateUpdated;
+ }
+
+ _disposedValue = true;
+ }
+ }
+
+ // This code added to correctly implement the disposable pattern.
+ public void Dispose()
+ {
+ // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
+ Dispose(true);
+ // TODO: uncomment the following line if the finalizer is overridden above.
+ // GC.SuppressFinalize(this);
+ }
+
+ #endregion IDisposable Support
}
}

MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/HistoryTabViewModel.cs
WalletWasabi.Gui/Controls/WalletExplorer/HistoryTabViewModel.cs
@@ -8,6 +8,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
+using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Text;
using System.Threading;
@@ -18,7 +19,7 @@ using WalletWasabi.Services;
namespace WalletWasabi.Gui.Controls.WalletExplorer
{
- public class HistoryTabViewModel : WalletActionViewModel
+ public class HistoryTabViewModel : WalletActionViewModel, IDisposable
{
private ObservableCollection<TransactionViewModel> _transactions;
private TransactionViewModel _selectedTransaction;
@@ -28,6 +29,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
private SortOrder _dateSortDirection;
private SortOrder _amountSortDirection;
private SortOrder _transactionSortDirection;
+ private CompositeDisposable _disposables = new CompositeDisposable();
public HistoryTabViewModel(WalletViewModel walletViewModel)
: base("History", walletViewModel)
@@ -43,11 +45,12 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
coinsChanged
.Merge(newBlockProcessed)
.Merge(coinSpent)
+ .Throttle(TimeSpan.FromSeconds(10))
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(o =>
{
RewriteTable();
- });
+ }).DisposeWith(_disposables);
this.WhenAnyValue(x => x.SelectedTransaction).Subscribe(async transaction =>
{
@@ -70,7 +73,8 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
{
Interlocked.Exchange(ref _disableClipboard, 0);
}
- });
+ }).DisposeWith(_disposables);
+
DateSortDirection = SortOrder.Decreasing;
}
@@ -145,7 +149,14 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
txRecordList = txRecordList.OrderByDescending(x => x.dateTime).ThenBy(x => x.amount).ToList();
var rememberSelectedTransactionId = SelectedTransaction?.TransactionId;
- Transactions?.Clear();
+ if (Transactions != null)
+ {
+ foreach (var tr in Transactions)
+ {
+ tr.Dispose();
+ }
+ Transactions.Clear();
+ }
foreach (var txr in txRecordList)
{
var txinfo = new TransactionInfo
@@ -174,7 +185,10 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
public ObservableCollection<TransactionViewModel> Transactions
{
get { return _transactions; }
- set { this.RaiseAndSetIfChanged(ref _transactions, value); }
+ set
+ {
+ this.RaiseAndSetIfChanged(ref _transactions, value);
+ }
}
public TransactionViewModel SelectedTransaction
@@ -242,16 +256,17 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
private void RefreshOrdering()
{
+ ObservableCollection<TransactionViewModel> newTrs = null;
if (TransactionSortDirection != SortOrder.None)
{
switch (TransactionSortDirection)
{
case SortOrder.Increasing:
- Transactions = new ObservableCollection<TransactionViewModel>(_transactions.OrderBy(t => t.TransactionId));
+ newTrs = new ObservableCollection<TransactionViewModel>(_transactions.OrderBy(t => t.TransactionId));
break;
case SortOrder.Decreasing:
- Transactions = new ObservableCollection<TransactionViewModel>(_transactions.OrderByDescending(t => t.TransactionId));
+ newTrs = new ObservableCollection<TransactionViewModel>(_transactions.OrderByDescending(t => t.TransactionId));
break;
}
}
@@ -260,11 +275,11 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
switch (AmountSortDirection)
{
case SortOrder.Increasing:
- Transactions = new ObservableCollection<TransactionViewModel>(_transactions.OrderBy(t => t.Amount));
+ newTrs = new ObservableCollection<TransactionViewModel>(_transactions.OrderBy(t => t.Amount));
break;
case SortOrder.Decreasing:
- Transactions = new ObservableCollection<TransactionViewModel>(_transactions.OrderByDescending(t => t.Amount));
+ newTrs = new ObservableCollection<TransactionViewModel>(_transactions.OrderByDescending(t => t.Amount));
break;
}
}
@@ -273,14 +288,58 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
switch (DateSortDirection)
{
case SortOrder.Increasing:
- Transactions = new ObservableCollection<TransactionViewModel>(_transactions.OrderBy(t => t.DateTime));
+ newTrs = new ObservableCollection<TransactionViewModel>(_transactions.OrderBy(t => t.DateTime));
break;
case SortOrder.Decreasing:
- Transactions = new ObservableCollection<TransactionViewModel>(_transactions.OrderByDescending(t => t.DateTime));
+ newTrs = new ObservableCollection<TransactionViewModel>(_transactions.OrderByDescending(t => t.DateTime));
break;
}
}
+ if (newTrs != null)
+ {
+ foreach (var tr in Transactions)
+ {
+ tr.Dispose();
+ }
+ Transactions = newTrs;
+ }
+ }
+
+ #region IDisposable Support
+
+ private bool _disposedValue = false; // To detect redundant calls
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_disposedValue)
+ {
+ if (disposing)
+ {
+ if (Transactions != null)
+ {
+ foreach (var tr in Transactions)
+ {
+ tr.Dispose();
+ }
}
+ _disposables.Dispose();
+ }
+
+ _transactions = null;
+ _disposedValue = true;
+ }
+ }
+
+ // This code added to correctly implement the disposable pattern.
+ public void Dispose()
+ {
+ // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
+ Dispose(true);
+ // TODO: uncomment the following line if the finalizer is overridden above.
+ // GC.SuppressFinalize(this);
+ }
+
+ #endregion IDisposable Support
}
}

MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/ReceiveTabViewModel.cs
WalletWasabi.Gui/Controls/WalletExplorer/ReceiveTabViewModel.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
+using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -14,7 +15,7 @@ using WalletWasabi.KeyManagement;
namespace WalletWasabi.Gui.Controls.WalletExplorer
{
- public class ReceiveTabViewModel : WalletActionViewModel
+ public class ReceiveTabViewModel : WalletActionViewModel, IDisposable
{
private ObservableCollection<AddressViewModel> _addresses;
private AddressViewModel _selectedAddress;
@@ -25,6 +26,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
private bool _clipboardNotificationVisible;
private int _caretIndex;
private ObservableCollection<SuggestionViewModel> _suggestions;
+ private CompositeDisposable _disposables = new CompositeDisposable();
public ReceiveTabViewModel(WalletViewModel walletViewModel)
: base("Receive", walletViewModel)
@@ -37,7 +39,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
.Subscribe(o =>
{
InitializeAddresses();
- });
+ }).DisposeWith(_disposables);
InitializeAddresses();
@@ -76,8 +78,10 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
Label = "";
});
- });
- this.WhenAnyValue(x => x.Label).Subscribe(x => UpdateSuggestions(x));
+ }).DisposeWith(_disposables);
+
+ this.WhenAnyValue(x => x.Label).Subscribe(x => UpdateSuggestions(x)).DisposeWith(_disposables);
+
this.WhenAnyValue(x => x.SelectedAddress).Subscribe(address =>
{
if (!(address is null))
@@ -92,7 +96,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
ClipboardNotificationOpacity = 0;
});
}
- });
+ }).DisposeWith(_disposables);
this.WhenAnyValue(x => x.CaretIndex).Subscribe(_ =>
{
@@ -101,7 +105,8 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
{
CaretIndex = Label.Length;
}
- });
+ }).DisposeWith(_disposables);
+
_suggestions = new ObservableCollection<SuggestionViewModel>();
}
@@ -222,5 +227,36 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
}
public ReactiveCommand GenerateCommand { get; }
+
+ #region IDisposable Support
+
+ private bool _disposedValue = false; // To detect redundant calls
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_disposedValue)
+ {
+ if (disposing)
+ {
+ _disposables.Dispose();
+ }
+
+ _addresses = null;
+ _suggestions = null;
+
+ _disposedValue = true;
+ }
+ }
+
+ // This code added to correctly implement the disposable pattern.
+ public void Dispose()
+ {
+ // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
+ Dispose(true);
+ // TODO: uncomment the following line if the finalizer is overridden above.
+ // GC.SuppressFinalize(this);
+ }
+
+ #endregion IDisposable Support
}
}

MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/SendTabViewModel.cs
WalletWasabi.Gui/Controls/WalletExplorer/SendTabViewModel.cs
@@ -19,10 +19,11 @@ using WalletWasabi.Gui.Tabs.WalletManager;
using WalletWasabi.Backend.Models.Responses;
using System.ComponentModel;
using WalletWasabi.Gui.Models;
+using System.Reactive.Disposables;
namespace WalletWasabi.Gui.Controls.WalletExplorer
{
- public class SendTabViewModel : WalletActionViewModel
+ public class SendTabViewModel : WalletActionViewModel, IDisposable
{
private CoinListViewModel _coinList;
private string _buildTransactionButtonText;
@@ -55,6 +56,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
private int _caretIndex;
private ObservableCollection<SuggestionViewModel> _suggestions;
private FeeDisplayFormat _feeDisplayFormat;
+ private CompositeDisposable _disposables = new CompositeDisposable();
private bool IgnoreAmountChanges { get; set; }
@@ -132,7 +134,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
}
SetFeesAndTexts();
- });
+ }).DisposeWith(_disposables);
BuildTransactionCommand = ReactiveCommand.Create(async () =>
{
@@ -218,12 +220,13 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
IsBusy = false;
}
},
- (this).WhenAny(x => x.IsMax, x => x.Amount, x => x.Address, x => x.IsBusy,
- (isMax, amount, address, busy) => ((isMax.Value || !string.IsNullOrWhiteSpace(amount.Value)) && !string.IsNullOrWhiteSpace(Address) && !IsBusy)));
+ this.WhenAny(x => x.IsMax, x => x.Amount, x => x.Address, x => x.IsBusy,
+ (isMax, amount, address, busy) => (isMax.Value || !string.IsNullOrWhiteSpace(amount.Value)) && !string.IsNullOrWhiteSpace(Address) && !IsBusy))
+ .DisposeWith(_disposables);
- MaxCommand = ReactiveCommand.Create(SetMax);
+ MaxCommand = ReactiveCommand.Create(SetMax).DisposeWith(_disposables);
- FeeRateCommand = ReactiveCommand.Create(ChangeFeeRateDisplay);
+ FeeRateCommand = ReactiveCommand.Create(ChangeFeeRateDisplay).DisposeWith(_disposables);
this.WhenAnyValue(x => x.IsBusy).Subscribe(busy =>
{
@@ -235,7 +238,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
{
BuildTransactionButtonText = BuildTransactionButtonTextString;
}
- });
+ }).DisposeWith(_disposables);
this.WhenAnyValue(x => x.Password).Subscribe(x =>
{
@@ -247,9 +250,10 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
Password = x.TrimEnd('\r', '\n');
}
}
- });
+ }).DisposeWith(_disposables);
+
+ this.WhenAnyValue(x => x.Label).Subscribe(x => UpdateSuggestions(x)).DisposeWith(_disposables);
- this.WhenAnyValue(x => x.Label).Subscribe(x => UpdateSuggestions(x));
this.WhenAnyValue(x => x.CaretIndex).Subscribe(_ =>
{
if (Label == null) return;
@@ -257,12 +261,12 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
{
CaretIndex = Label.Length;
}
- });
+ }).DisposeWith(_disposables);
this.WhenAnyValue(x => x.FeeTarget).Subscribe(_ =>
{
SetFeesAndTexts();
- });
+ }).DisposeWith(_disposables);
CoinList.SelectionChanged += CoinList_SelectionChanged;
@@ -874,5 +878,40 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
public ReactiveCommand MaxCommand { get; }
public ReactiveCommand FeeRateCommand { get; }
+
+ #region IDisposable Support
+
+ private bool _disposedValue = false; // To detect redundant calls
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_disposedValue)
+ {
+ if (disposing)
+ {
+ Global.Synchronizer.PropertyChanged -= Synchronizer_PropertyChanged;
+ CoinList.SelectionChanged -= CoinList_SelectionChanged;
+ _coinList.DequeueCoinsPressed -= CoinsList_DequeueCoinsPressedAsync;
+
+ CoinList.Dispose();
+ _disposables.Dispose();
+ }
+
+ CoinList = null;
+
+ _disposedValue = true;
+ }
+ }
+
+ // This code added to correctly implement the disposable pattern.
+ public void Dispose()
+ {
+ // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
+ Dispose(true);
+ // TODO: uncomment the following line if the finalizer is overridden above.
+ // GC.SuppressFinalize(this);
+ }
+
+ #endregion IDisposable Support
}
}

MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/TransactionViewModel.cs
WalletWasabi.Gui/Controls/WalletExplorer/TransactionViewModel.cs
using ReactiveUI;
using System;
using System.Globalization;
+using System.Reactive.Disposables;
using WalletWasabi.Gui.ViewModels;
namespace WalletWasabi.Gui.Controls.WalletExplorer
{
- public class TransactionViewModel : ViewModelBase
+ public class TransactionViewModel : ViewModelBase, IDisposable
{
private TransactionInfo _model;
+ private CompositeDisposable _disposables = new CompositeDisposable();
public TransactionViewModel(TransactionInfo model)
{
_model = model;
- _confirmed = model.WhenAnyValue(x => x.Confirmed).ToProperty(this, x => x.Confirmed, model.Confirmed);
+ _confirmed = model.WhenAnyValue(x => x.Confirmed).ToProperty(this, x => x.Confirmed, model.Confirmed).DisposeWith(_disposables);
}
private readonly ObservableAsPropertyHelper<bool> _confirmed;
@@ -48,5 +50,34 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
{
get => _model.TransactionId;
}
+
+ #region IDisposable Support
+
+ private bool _disposedValue = false; // To detect redundant calls
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_disposedValue)
+ {
+ if (disposing)
+ {
+ _disposables.Dispose();
+ }
+
+ _model = null;
+ _disposedValue = true;
+ }
+ }
+
+ // This code added to correctly implement the disposable pattern.
+ public void Dispose()
+ {
+ // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
+ Dispose(true);
+ // TODO: uncomment the following line if the finalizer is overridden above.
+ // GC.SuppressFinalize(this);
+ }
+
+ #endregion IDisposable Support
}
}

CoinViewModel dispose implemetation