I'm facing a problem with displaying graphs filtered by ComboBox
selection without having the UI lock up. The statistic filtering is quite heavy and needs to run async
. This works fine all up until I try to call FilterStatisticsAsync
and MonthSelectionChanged
from the Property setter. Does anyone have a good tip on how to solve or work around this?
The XAML looks like this:
<ComboBox x:Name="cmbMonth"
ItemsSource="{Binding Months}"
SelectedItem="{Binding SelectedMonth }"
IsEditable="True"
IsReadOnly="True"
And the ViewModel property setter like this:
public string SelectedMonth
{
get { return _selectedMonth; }
set { SetProperty(ref _selectedMonth, value); LoadStatisticsAsync(); MonthSelectionChanged(); }
}
SetProperty
derives from a base class which encapsulates INPC like this:
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected virtual void SetProperty<T>(ref T member, T value, [CallerMemberName] string propertyName = null)
{
if (Equals(member, value))
return;
member = value;
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
I would do it using this:
public class AsyncProperty<T> : INotifyPropertyChanged
{
public async Task UpdateAsync(Task<T> updateAction)
{
LastException = null;
IsUpdating = true;
try
{
Value = await updateAction.ConfigureAwait(false);
}
catch (Exception e)
{
LastException = e;
Value = default(T);
}
IsUpdating = false;
}
private T _value;
public T Value
{
get { return _value; }
set
{
if (Equals(value, _value)) return;
_value = value;
OnPropertyChanged();
}
}
private bool _isUpdating;
public bool IsUpdating
{
get { return _isUpdating; }
set
{
if (value == _isUpdating) return;
_isUpdating = value;
OnPropertyChanged();
}
}
private Exception _lastException;
public Exception LastException
{
get { return _lastException; }
set
{
if (Equals(value, _lastException)) return;
_lastException = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Definition of property
public AsyncProperty<string> SelectedMonth { get; } = new AsyncProperty<string>();
somewhere else in your code:
SelectedMonth.UpdateAsync(Task.Run(() => whateveryourbackground work is));
binding in xaml:
SelectedItem="{Binding SelectedMonth.Value }"
Note that properties should reflect a current state, instead of triggering processes which may take an indefinite amount of time. Hence the need to update the property in a different way from just assigning it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With