17 May 2020
Category: Mini
Hi everyone,
I was playing some more with tasks and async-await because I was curious how to make looping task to be looping
just as long as the other long task is running. Functionality like loaders on websites and games and such. I felt BackgrounbdWorker
is not a good tool for this so I chose Tasks for this. After some digging and posting questions on Stack Overflow, I found the solution.
THANKS to Stack Overflow community - a place where I always find help or answers.
Important notes:
I hope this could help somebody on their path to conquer WPF. And as usual, there is a link to the repo with the project.
Petr
Main Window:
<Window x:Class="AsynIndicationStartStop.View.AsyncIndicationStartStopWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
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:commands="clr-namespace:AsynIndicationStartStop.ViewModel.Commands"
xmlns:converters="clr-namespace:AsynIndicationStartStop.ViewModel.Converters"
xmlns:local="clr-namespace:AsynIndicationStartStop.View"
mc:Ignorable="d"
Title="Async Indication Start Stop" Height="273" Width="440">
<Window.Resources>
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
</Window.Resources>
<Grid x:Name="container" VerticalAlignment="Center" HorizontalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Border BorderBrush="Coral" BorderThickness="1" CornerRadius="10"
Grid.Row="0" Grid.ColumnSpan="2"
>
<Label VerticalAlignment="Stretch" HorizontalAlignment="Center"
Content="{Binding Title}"
FontSize="25"
/>
</Border>
<Button Grid.Column="0" Grid.Row="1" Margin="10, 20"
Content="Start Process"
HorizontalAlignment="Center"
FontSize="20"
Command="{Binding UpdateProgressCommand}"/>
<TextBlock Grid.Column="1" Grid.Row="1"
FontSize="20"
VerticalAlignment="Center"
Text="{Binding ProgressText}"/>
<Border BorderBrush="Coral" BorderThickness="1" CornerRadius="10" Margin="0,0,0,20"
Grid.Row="2" Grid.ColumnSpan="2"
Visibility="{Binding IsRunning, Converter={StaticResource BoolToVisibilityConverter}}"
>
<Label VerticalAlignment="Stretch" HorizontalAlignment="Center"
Content="Long working task is running."
FontSize="25"
/>
</Border>
</Grid>
</Window>
MainWindow cs:
using AsynIndicationStartStop.ViewModel;
using System.Windows;
namespace AsynIndicationStartStop.View
{
/// <summary>
/// Interaction logic for AsyncIndicationStartStopWindow.xaml
/// </summary>
public partial class AsyncIndicationStartStopWindow : Window
{
public MainVM MainVM;
public AsyncIndicationStartStopWindow()
{
InitializeComponent();
MainVM = new MainVM();
container.DataContext = MainVM;
}
}
}
MainVM:
using AsynIndicationStartStop.ViewModel.Commands;
using JetBrains.Annotations;
using System;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
namespace AsynIndicationStartStop.ViewModel
{
public class MainVM : INotifyPropertyChanged
{
private const string PROGRESS = "Progress";
private const int PROGRESS_DELAY = 200;
private string progressText;
public string ProgressText
{
get { return progressText; }
set { progressText = value; OnPropertyChanged(); }
}
private bool isRunning;
public bool IsRunning
{
get { return isRunning; }
set { isRunning = value; OnPropertyChanged(); }
}
private string title;
public string Title
{
get { return title; }
set { title = value; OnPropertyChanged(nameof(Title)); }
}
public UpdateProgressCommand UpdateProgressCommand { get; set; }
public MainVM()
{
ProgressText = PROGRESS;
Title = "Async process looping till long working task finishes.";
UpdateProgressCommand = new UpdateProgressCommand(this);
}
public async void RunProgressTextUpdate()
{
var cts = new CancellationTokenSource();
if (!IsRunning)
{
IsRunning = true;
UpdateProgressTextTask(cts.Token);
string longTaskText = await Task.Run(() => LongTask(cts));
await Task.Delay(PROGRESS_DELAY); // Additional delay to prevent alternating finished text by looping task
ProgressText = longTaskText;
IsRunning = false;
}
}
private void UpdateProgressTextTask(CancellationToken token)
{
Task.Run(async () =>
{
ProgressText = PROGRESS;
while (!token.IsCancellationRequested)
{
await Task.Delay(PROGRESS_DELAY);
var dotsCount = ProgressText.Count<char>(ch => ch == '.');
ProgressText = dotsCount < 6 ? ProgressText + "." : ProgressText.Replace(".", "");
}
});
}
private string LongTask(CancellationTokenSource cts)
{
var result = Task.Run(async () =>
{
await Task.Delay(5000);
cts.Cancel();
return "Long task finished.";
});
return result.Result;
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
BoolToVisibilityConverter
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace AsynIndicationStartStop.ViewModel.Converters
{
class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? Visibility.Visible : Visibility.Hidden;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
Petr