我的第一件奇怪的事是,我可以在SaveBtnClick中的行上放置一个断点,将文本更改为“Saving”,这不会影响按钮。我意识到SaveAsPngAsync中执行的代码仍在锁定我的用户界面。我认为这是因为
scheduler
我传递给它,但如果没有那块代码,我会在谈论线程STA时出错。
代码:
private void SaveBtn_Click(object sender, RoutedEventArgs e)
{
ChangeSaveBtnText("Saving");
if (pictureSaveTask == null)
{
pictureSaveTask = SaveAsPngAsync();
pictureSaveTask.ContinueWith((t) =>
{
pictureSaveTask = null;
ChangeSaveBtnText("Save");
});
}
else
{
cts.Cancel();
ChangeSaveBtnText("Save");
}
}
private void ChangeSaveBtnText(string text)
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => {
SaveBtn.Content = text;
}));
}
/// <summary>
/// Saves the current state of the DrawingCanvas as Xaml so that it can re parsed on a different thread then exported as a png.
/// </summary>
/// <returns>Task of executing code</returns>
private async Task SaveAsPngAsync()
{
//Stores current DrawingCanvas at time of save as a string to be parsed later.
var canvasAtSaveState = XamlWriter.Save(DrawingCanvas);
var ActualWidth = DrawingCanvas.ActualWidth;
var ActualHeight = DrawingCanvas.ActualHeight;
//Not really sure why this is required but without, throws Error: Thread must be STA.
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
//This is the short hand way of creating a task and running it instantly
await Task.Factory.StartNew(() => {
try
{
// 'as' object syntax is a short hand conversion
InkCanvas cc = XamlReader.Parse(canvasAtSaveState) as InkCanvas;
//Pretty clear that this gets the users path to their desktop
var desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
//File stream is the easiest way to export a stream of bytes into a file
//Important usage of a C# syntax "using" statements are very important to prevent memory leaks especially with streams
using (var fs = new FileStream(System.IO.Path.Combine(desktopPath, "picture.png"), FileMode.Create, FileAccess.ReadWrite))
{
//Must use Actual(Value) because Simply using Width or Height doesn't change the window size changes or scales
RenderTargetBitmap rtb = new RenderTargetBitmap((int)ActualWidth, (int)ActualHeight, 96d, 96d, PixelFormats.Default);
rtb.Render(cc);
//PngBitmapEncoder because my InkCanvas has a transparent background
BitmapEncoder pngEncoder = new PngBitmapEncoder();
//Adds a single frame to the image because this isn't a Gif
pngEncoder.Frames.Add(BitmapFrame.Create(rtb));
//Begins saving the pngBytes into a stream that will then store the bytes into a file
pngEncoder.Save(fs);
}
}
catch (Exception ex)
{
//Catches anything just in case something explodes
}
}, cts.Token, TaskCreationOptions.PreferFairness, scheduler); //PreferFairness tells the thread pool that this new worker thread isn't super important
}