0
基本的なペイントプログラムで作業していて、現在のペイントウィンドウをpngファイルにエクスポートするセーブコードを書きました。私はそのコードを新しいスレッドで実行するように変更したので、私は考えました。しかし、保存ボタンのテキストを変更するコードを実行すると、画像が保存されるまで更新されません。WPFボタンのテキストが変更されないUIがスレッドコードからロックされる
私の最初のことは奇妙です。SaveBtnClickの行にブレークポイントを置くと、テキストが「保存」に変わり、ボタンには影響しません。私はSaveAsPngAsyncで実行されているコードがまだ私のUIをロックしていることを認識しています。私はそれが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
}
は、それはです。ボタンクリックハンドラであなたの仕事を待っているわけではありません。 – dymanoid
'SaveAsPngAsync'にスケジューラ、作成オプション、トークン、async/awaitという混乱する点がたくさんあります。あなたが必要とするものはありません。これらをすべて削除すると、意図したとおりに動作します。 – Sinatr
@Sinatr私は、保存操作をキャンセルするためのトークンが必要だと思っています。私はスレッドのSTAに関する例外が発生するため、スケジューラを与えました。 –