Skip to content

Instantly share code, notes, and snippets.

@cwensley
Created October 31, 2016 18:01
Show Gist options
  • Save cwensley/e9eda58ca2e44858f62bfdc2f2a8e809 to your computer and use it in GitHub Desktop.
Save cwensley/e9eda58ca2e44858f62bfdc2f2a8e809 to your computer and use it in GitHub Desktop.
Shows how to generate an image in your view model based on user input in a background thread using async/await.
using System;
using Eto.Forms;
using Eto.Drawing;
using System.ComponentModel;
using System.Windows.Input;
using System.Threading.Tasks;
using System.Threading;
namespace TestAsyncAwait
{
public class MyViewModel : INotifyPropertyChanged
{
CancellationTokenSource cancelSource;
int count;
/// <summary>
/// Call this from your view model to update the image asynchronously
/// </summary>
async void ExecuteUpdateImage()
{
try
{
// cancel previous update if any
cancelSource?.Cancel();
// tell user we're updating
Updating = true;
// create a new cancellation token
cancelSource = new CancellationTokenSource();
var ct = cancelSource.Token;
// 1 second delay before doing anything
await Task.Delay(1000, ct);
// generate new image in background thread
MyImage = await Task.Run(() => BackgroundUpdate(ct), ct);
// No longer updating. Does not execute for cancelled updates
Updating = false;
cancelSource = null;
}
catch (OperationCanceledException)
{
// task was cancelled!
}
}
/// <summary>
/// This is run in a background thread and won't block the UI
/// </summary>
Bitmap BackgroundUpdate(CancellationToken ct)
{
Thread.Sleep(1000); // simulate delay in generating bitmap
ct.ThrowIfCancellationRequested();
var bmp = new Bitmap(100, 100, PixelFormat.Format32bppRgba);
ct.ThrowIfCancellationRequested();
count++; // increment counter to ensure we're only generating limited number of bitmaps
using (var g = new Graphics(bmp))
{
g.DrawLine(Colors.Blue, 0, 0, 100, 100);
g.DrawLine(Colors.Blue, 100, 0, 0, 100);
g.DrawText(new Font(SystemFont.Default, 20), Colors.Black, 0, 0, count.ToString());
}
ct.ThrowIfCancellationRequested();
return bmp;
}
Command updateCommand;
public Command UpdateCommand => updateCommand ?? (updateCommand = new Command((sender, e) => ExecuteUpdateImage()));
Image myImage;
public Image MyImage
{
get { return myImage; }
private set
{
if (myImage != value)
{
myImage = value;
OnPropertyChanged(nameof(MyImage));
}
}
}
bool updating;
public bool Updating
{
get { return updating; }
private set
{
if (updating != value)
{
updating = value;
OnPropertyChanged(nameof(Updating));
}
}
}
double numericValue;
public double NumericValue
{
get { return numericValue; }
set
{
if (numericValue != value)
{
numericValue = value;
OnPropertyChanged(nameof(NumericValue));
ExecuteUpdateImage();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public partial class MainForm : Form
{
void InitializeComponent()
{
Title = "My Eto Form";
var updateImageButton = new Button { Text = "Update Image" };
updateImageButton.BindDataContext(c => c.Command, (MyViewModel m) => m.UpdateCommand);
var imageView = new ImageView { Width = 200, Height = 200 };
imageView.BindDataContext(c => c.Image, Binding.Property((MyViewModel m) => m.MyImage));
var updatingLabel = new Label { Text = "Updating image..." };
updatingLabel.BindDataContext(c => c.Visible, (MyViewModel m) => m.Updating);
var numericUpDown = new NumericUpDown();
numericUpDown.ValueBinding.BindDataContext((MyViewModel m) => m.NumericValue);
Content = new StackLayout
{
Padding = 10,
Spacing = 10,
Items =
{
TableLayout.Horizontal(5, updateImageButton, numericUpDown),
updatingLabel,
new StackLayoutItem(TableLayout.AutoSized(imageView, centered: true), HorizontalAlignment.Stretch, true)
// Add more controls here
}
};
DataContext = new MyViewModel();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment