Last active
November 30, 2016 18:00
-
-
Save robibobi/b3e65b2f9c70ce4d1acfe8598bdac257 to your computer and use it in GitHub Desktop.
Blog "Asynchrone Command Pattern im MVVM-Stil" auf http://thecolorofcode.com/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class AsyncCommand<TParam> : AsyncCommandBase | |
{ | |
readonly Func<CancellationToken, TParam, Task> _command; | |
readonly Func<TParam, bool> _canExecuteFunc; | |
readonly CancelAsyncCommand _cancelCommand; | |
public AsyncCommand(Func<CancellationToken, TParam, Task> command) | |
{ | |
_command = command; | |
_canExecuteFunc = _ => true; | |
_cancelCommand = new CancelAsyncCommand(); | |
} | |
public AsyncCommand(Func<CancellationToken, TParam, Task> command, Func<TParam, bool> canExecute) | |
{ | |
_command = command; | |
_canExecuteFunc = canExecute; | |
_cancelCommand = new CancelAsyncCommand(); | |
} | |
public override bool CanExecute(object parameter) | |
{ | |
return _canExecuteFunc(SaveCast(parameter)) && (Execution == null || Execution.IsCompleted); | |
} | |
public override async Task ExecuteAsync(object parameter) | |
{ | |
TParam param = SaveCast(parameter); | |
_cancelCommand.NotifyCommandStarting(); | |
Execution = new NotifyTaskCompletion(_command(_cancelCommand.Token, param)); | |
OnPropertyChanged(nameof(Execution)); | |
RaiseCanExecuteChanged(); | |
await Execution.TaskCompletion; | |
_cancelCommand.NotifyCommandFinished(); | |
RaiseCanExecuteChanged(); | |
} | |
public ICommand CancelCommand => _cancelCommand; | |
public NotifyTaskCompletion Execution { get; private set; } | |
private TParam SaveCast(object param) | |
{ | |
if(param == null) | |
return default(TParam); | |
return (TParam)param; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class MainViewModel : ViewModelBase | |
{ | |
public AsyncCommand<string> CalculatePrimesCmd { get; } | |
public List<int> Primes { get; set; } | |
public MainViewModel() | |
{ | |
CalculatePrimesCmd = new AsyncCommand<string>(OnCalculatePrimes, CanExecuteCalculation); | |
} | |
private bool CanExecuteCalculation(string countString) | |
{ | |
return !String.IsNullOrEmpty(countString) && Regex.IsMatch(countString, @"^\d+$"); | |
} | |
private Task OnCalculatePrimes(CancellationToken t, string countString) | |
{ | |
return Task.Factory.StartNew(() => | |
{ | |
int count = Convert.ToInt32(countString); | |
Primes = GeneratePrimesNaive(t, count) | |
.Take(1000) | |
.ToList(); | |
RaisePropertyChanged(nameof(Primes)); | |
}); | |
} | |
private static List<int> GeneratePrimesNaive(CancellationToken t, int n) | |
{ | |
List<int> primes = new List<int>(); | |
primes.Add(2); | |
int nextPrime = 3; | |
while (primes.Count < n && !t.IsCancellationRequested) | |
{ | |
int sqrt = (int)Math.Sqrt(nextPrime); | |
bool isPrime = true; | |
for (int i = 0; (int)primes[i] <= sqrt; i++) | |
{ | |
if (nextPrime % primes[i] == 0) | |
{ | |
isPrime = false; | |
break; | |
} | |
} | |
if (isPrime) | |
{ | |
primes.Add(nextPrime); | |
} | |
nextPrime += 2; | |
} | |
return primes; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Window x:Class="Tcoc.Blog.Async.MainWindow" | |
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | |
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | |
xmlns:local="clr-namespace:Tcoc.Blog.Async" | |
Title="Primes Calculator" | |
Width="525" | |
Height="350"> | |
<Window.DataContext> | |
<local:MainViewModel /> | |
</Window.DataContext> | |
<Grid Margin="50"> | |
<Grid.ColumnDefinitions> | |
<ColumnDefinition Width="*" /> | |
<ColumnDefinition Width="*" /> | |
</Grid.ColumnDefinitions> | |
<Grid.RowDefinitions> | |
<RowDefinition Height="30" /> | |
<RowDefinition Height="16" /> | |
<RowDefinition Height="30" /> | |
<RowDefinition Height="16" /> | |
<RowDefinition Height="*" /> | |
</Grid.RowDefinitions> | |
<TextBlock VerticalAlignment="Center" Text="Enter prime count" /> | |
<!-- Eingabefeld Anzahl --> | |
<TextBox x:Name="PrimesCountTB" Grid.Column="1" /> | |
<!-- Primzahlen berechnen --> | |
<Button Grid.Row="2" | |
Grid.Column="0" | |
Command="{Binding CalculatePrimesCmd, Mode=OneWay}" | |
CommandParameter="{Binding ElementName=PrimesCountTB, Path=Text, UpdateSourceTrigger=PropertyChanged}" | |
Content="Calculate Primes" /> | |
<!-- Berechnung abbrechen --> | |
<Button Grid.Row="2" | |
Grid.Column="1" | |
Command="{Binding CalculatePrimesCmd.CancelCommand}" | |
Content="Cancel" /> | |
<!-- Ergebnis Anzeige --> | |
<ListView Grid.Row="4" | |
Grid.ColumnSpan="2" | |
ItemsSource="{Binding Primes, IsAsync=True}"> | |
<ListView.ItemsPanel> | |
<ItemsPanelTemplate> | |
<WrapPanel Width="400" ItemWidth="120" /> | |
</ItemsPanelTemplate> | |
</ListView.ItemsPanel> | |
</ListView> | |
</Grid> | |
</Window> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class NotifyTaskCompletion : INotifyPropertyChanged | |
{ | |
public NotifyTaskCompletion(Task task) | |
{ | |
Task = task; | |
TaskCompletion = WatchTaskAsync(task); | |
} | |
private async Task WatchTaskAsync(Task task) | |
{ | |
try | |
{ | |
await task; | |
} | |
catch | |
{ | |
} | |
var propertyChanged = PropertyChanged; | |
if (propertyChanged == null) | |
return; | |
propertyChanged(this, new PropertyChangedEventArgs("Status")); | |
propertyChanged(this, new PropertyChangedEventArgs("IsCompleted")); | |
propertyChanged(this, new PropertyChangedEventArgs("IsNotCompleted")); | |
if (task.IsCanceled) | |
{ | |
propertyChanged(this, new PropertyChangedEventArgs("IsCanceled")); | |
} | |
else if (task.IsFaulted) | |
{ | |
propertyChanged(this, new PropertyChangedEventArgs("IsFaulted")); | |
propertyChanged(this, new PropertyChangedEventArgs("Exception")); | |
propertyChanged(this, new PropertyChangedEventArgs("InnerException")); | |
propertyChanged(this, new PropertyChangedEventArgs("ErrorMessage")); | |
} | |
else | |
{ | |
propertyChanged(this, new PropertyChangedEventArgs("IsSuccessfullyCompleted")); | |
propertyChanged(this, new PropertyChangedEventArgs("Result")); | |
} | |
} | |
public Task Task { get; } | |
public Task TaskCompletion { get; } | |
public TaskStatus Status => Task.Status; | |
public bool IsCompleted => Task.IsCompleted; | |
public bool IsNotCompleted => !Task.IsCompleted; | |
public bool IsSuccessfullyCompleted => Task.Status == TaskStatus.RanToCompletion; | |
public bool IsCanceled => Task.IsCanceled; | |
public bool IsFaulted => Task.IsFaulted; | |
public AggregateException Exception => Task.Exception; | |
public Exception InnerException => (Exception == null) ? null : Exception.InnerException; | |
public string ErrorMessage => InnerException == null ? null : InnerException.Message; | |
public event PropertyChangedEventHandler PropertyChanged; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment