좋아, 나는 약간의 다른 관점에서 물건을 보여주기 때문에 나는 여기에 내 자신의 대답도 넣을 것이라고 생각한다. 저스틴 천사 대답은 괜찮지 만, 그것으로 문제가 몇 가지있다 : 코드가 모델 층의 깊이와 배경 스레드에서 실행할 때
- 그것은, 디스패처에 대한 참조를 가질 수는 없습니다은.
- 메서드에서 축소판 이미지를 반환하고 나중에 동일한 동기화 컨텍스트에서 사용해야합니다. 그렇지 않으면 미리보기 이미지를 만드는 방법을 중심으로 많은 코드를 변경해야합니다.
염두에 요구 사항을
가 여기 내 솔루션입니다 :
내가 UI 스레드에 여전히 해요 동안 (보기 모델에서) UI 스레드의 SynchronizationContext에 캡처, 더욱 그것을 통과하고있어
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 스레드에서 실행되는 콜백에 사용할 수 있도록 클로저를 사용하여 로컬 변수를 캡처합니다. 또한 잠금 및 모니터를 사용하여이 두 스레드를 동기화하고 이미지가 준비 될 때까지 대기합니다.
내 투표 나 저스틴 천사의 답변을 투표에 따라 허용합니다 (있는 경우). :)
편집 : 당신이 (예를 들어, 버튼 클릭 핸들러) UI 스레드에있는 동안 당신은 System.Threading.SynchronizationContext.Current
을 통해 Dispatcher의 SynchronizationContext
의 인스턴스를 얻을 수 있습니다. 이와 같이 :
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
좋아, 내가 또 다른 downvote을 받았기 때문에, 나는 다시 한 번 묻고있다 : 정확히이 접근법에 잘못된 무엇입니까? 왜 의지하는 것이 좋은 방법이 아닌가요?downvote하지만 대답의 단점을 지적하지 않으면, 그것은 단지 시간 낭비 일뿐입니다 (마우스로 한 번 클릭하는 것은 의미있는 것을 작성하는 것보다 쉽습니다). – Haspemulator
ViewModel을 통해이 메서드를 호출하는 방법. 어디에서 SynchronizationContext를 얻을 수 있습니다. 메소드를 호출하는 방법을 추가 할 수 있습니까? – StezPet