好的,我想我也會在這裏提出自己的答案,因爲它從不同的角度展示了一些東西。由Justin天使答案是好的,但有幾個問題吧:
- 它不可能有調度的參考,當代碼在模型層深,在後臺線程上運行。
- 我需要從方法中返回縮略圖並稍後在相同的同步上下文中使用它。否則,我將不得不圍繞這種創建縮略圖的方法來改變很多代碼。
有了這個需求,這是我的解決方案:
private WriteableBitmap CreateThumbnail(Stream stream, int width, int height, SynchronizationContext uiThread)
{
// This hack comes from the problem that classes like BitmapImage, WritableBitmap, Image used here could
// only be created or accessed from the UI thread. And now this code called from the threadpool. To avoid
// cross-thread access exceptions, I dispatch the code back to the UI thread, waiting for it to complete
// using the Monitor and a lock object, and then return the value from the method. Quite hacky, but the only
// way to make this work currently. It's quite stupid that MS didn't provide any classes to do image
// processing on the non-UI threads.
WriteableBitmap result = null;
var waitHandle = new object();
lock (waitHandle)
{
uiThread.Post(_ =>
{
lock (waitHandle)
{
var bi = new BitmapImage();
bi.SetSource(stream);
int w, h;
double ws = (double)width/bi.PixelWidth;
double hs = (double)height/bi.PixelHeight;
double scale = (ws > hs) ? ws : hs;
w = (int)(bi.PixelWidth * scale);
h = (int)(bi.PixelHeight * scale);
var im = new Image();
im.Stretch = Stretch.UniformToFill;
im.Source = bi;
result = new WriteableBitmap(width, height);
var tr = new CompositeTransform();
tr.CenterX = (ws > hs) ? 0 : (width - w)/2;
tr.CenterY = (ws < hs) ? 0 : (height - h)/2;
tr.ScaleX = scale;
tr.ScaleY = scale;
result.Render(im, tr);
result.Invalidate();
Monitor.Pulse(waitHandle);
}
}, null);
Monitor.Wait(waitHandle);
}
return result;
}
我捕捉UI線程的SynchronizationContext,而我仍然在UI線程(在視圖模型),並進一步傳遞,然後我使用閉包來捕獲局部變量,以便它們可用於在UI線程上運行的回調。我還使用鎖定和監視器來同步這兩個線程,並等待圖像準備就緒。
我會根據選票接受我或賈斯汀安琪的答案,如果有的話。 :)
編輯:您可以通過System.Threading.SynchronizationContext.Current
得到Dispatcher的SynchronizationContext
的實例,而你在UI線程(在按鈕單擊處理程序,例如)。像這樣:
private async void CreateThumbnailButton_Clicked(object sender, EventArgs args)
{
SynchronizationContext uiThread = SynchronizationContext.Current;
var result = await Task.Factory.StartNew<WriteableBitmap>(() =>
{
Stream img = GetOriginalImage();// get the original image through a long synchronous operation
return CreateThumbnail(img, 163, 163, uiThread);
});
await SaveThumbnailAsync(result);
}
Downvoters,小心解釋爲什麼?這是工作和確定性的方法。 – Haspemulator 2013-04-10 15:58:29
好吧,既然我收到了另一個downvote,我再次問:這種方法究竟有什麼錯誤?爲什麼它不是一個好方法?如果你倒退了但不指出答案的缺點,這只是浪費你的時間(儘管我明白用鼠標點擊一下比寫一些有意義的事情容易)。 – Haspemulator 2013-04-19 11:04:41
我如何通過ViewModel調用這個方法。從哪裏我可以得到SynchronizationContext。你可以添加調用方法的方式嗎? – StezPet 2013-04-19 11:31:47