我看到WinRT RenderTargetBitmap能夠通過「RenderAsync(visual)」異步渲染視覺效果;方法。 不幸的是.net RendertargetBitmap沒有RenderAsync方法。 有.net RenderTargetBitmap任何解決方法或擴展,以允許WPF視覺效果的異步呈現? 感謝您的幫助!異步呈現WPF視覺到位圖
0
A
回答
3
我寫了一個方法來通過BitBlt函數渲染視覺到位圖。在像我這樣的沉重用戶界面中,這種方法比RenderTargetBitmap要快20倍。
你在這裏。
public static class VisualRender
{
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
static readonly IntPtr _HwndBottom = new IntPtr(1);
const UInt32 _SWP_NOSIZE = 0x0001;
const UInt32 _SWP_NOMOVE = 0x0002;
const UInt32 _SWP_NOACTIVATE = 0x0010;
public static async Task<BitmapSource> RenderAsync(this UIElement uiElement)
{
var brush = new VisualBrush(uiElement);
var topLeft = uiElement.PointToScreen(new System.Windows.Point(0, 0));
var bottomRight = uiElement.PointToScreen(new System.Windows.Point(uiElement.RenderSize.Width, uiElement.RenderSize.Height));
var helperWindowLocation = Rect.Empty;
helperWindowLocation.Union(topLeft);
helperWindowLocation.Union(bottomRight);
var width = helperWindowLocation.Width;
var height = helperWindowLocation.Height;
//
var windowsScaleTransform = uiElement.GetWindowsScaleTransform();
helperWindowLocation.X /= windowsScaleTransform;
helperWindowLocation.Y /= windowsScaleTransform;
helperWindowLocation.Width /= windowsScaleTransform;
helperWindowLocation.Height /= windowsScaleTransform;
BitmapSource bitmapSourceT = await RenderAsync(brush, helperWindowLocation, new System.Windows.Size(width, height));
bitmapSourceT.Freeze();
return bitmapSourceT;
}
private static async Task<BitmapSource> RenderAsync(System.Windows.Media.Brush brush, Rect helperWindowLocation, System.Windows.Size snapshotSize)
{
// Create a tmp window with its own hwnd to render it later
var window = new Window { WindowStyle = WindowStyle.None, ResizeMode = ResizeMode.NoResize, ShowInTaskbar = false, Background = System.Windows.Media.Brushes.White };
window.Left = helperWindowLocation.X;
window.Top = helperWindowLocation.Y;
window.Width = helperWindowLocation.Width;
window.Height = helperWindowLocation.Height;
window.ShowActivated = false;
var content = new Grid() { Background = brush };
RenderOptions.SetBitmapScalingMode(content, BitmapScalingMode.HighQuality);
window.Content = content;
var handle = new WindowInteropHelper(window).EnsureHandle();
window.Show();
// Make sure the tmp window is under our mainwindow to hide it
SetWindowPos(handle, _HwndBottom, 0, 0, 0, 0, _SWP_NOMOVE | _SWP_NOSIZE | _SWP_NOACTIVATE);
//Wait for the element to render
//await popupChild.WaitForLoaded();
await window.WaitForFullyRendered();
////Why we have to wait here fore the visualbrush to finish its lazy rendering Process?
//// Todo: It seems like very complex uielements does not finish its rendering process within one renderloop
//// Check https://stackoverflow.com/questions/2851236/rendertargetbitmap-resourced-visualbrush-incomplete-image
// Async BitBlt the tmp Window
var bitmapSourceT = await Task.Run(() =>
{
Bitmap bitmap = VisualToBitmapConverter.GetBitmap(handle,
(int)snapshotSize.Width, (int)snapshotSize.Height);
var bitmapSource = new SharedBitmapSource(bitmap);
bitmapSource.Freeze();
return bitmapSource;
});
// Close the Window
window.Close();
return bitmapSourceT;
}
public static class VisualToBitmapConverter
{
private enum TernaryRasterOperations : uint
{
SRCCOPY = 0x00CC0020,
SRCPAINT = 0x00EE0086,
SRCAND = 0x008800C6,
SRCINVERT = 0x00660046,
SRCERASE = 0x00440328,
NOTSRCCOPY = 0x00330008,
NOTSRCERASE = 0x001100A6,
MERGECOPY = 0x00C000CA,
MERGEPAINT = 0x00BB0226,
PATCOPY = 0x00F00021,
PATPAINT = 0x00FB0A09,
PATINVERT = 0x005A0049,
DSTINVERT = 0x00550009,
BLACKNESS = 0x00000042,
WHITENESS = 0x00FF0062,
CAPTUREBLT = 0x40000000
}
[DllImport("gdi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, TernaryRasterOperations dwRop);
public static Bitmap GetBitmap(IntPtr hwnd, int width, int height)
{
var bitmap = new Bitmap(width, height);
using (Graphics graphicsFromVisual = Graphics.FromHwnd(hwnd))
{
using (Graphics graphicsFromImage = Graphics.FromImage(bitmap))
{
IntPtr source = graphicsFromVisual.GetHdc();
IntPtr destination = graphicsFromImage.GetHdc();
BitBlt(destination, 0, 0, bitmap.Width, bitmap.Height, source, 0, 0, TernaryRasterOperations.SRCCOPY);
graphicsFromVisual.ReleaseHdc(source);
graphicsFromImage.ReleaseHdc(destination);
}
}
return bitmap;
}
}
}
static class Extensions
{
public static Task WaitForLoaded(this FrameworkElement element)
{
var tcs = new TaskCompletionSource<object>();
RoutedEventHandler handler = null;
handler = (s, e) =>
{
element.Loaded -= handler;
tcs.SetResult(null);
};
element.Loaded += handler;
return tcs.Task;
}
public static Task WaitForFullyRendered(this Window window)
{
var tcs = new TaskCompletionSource<object>();
EventHandler handler = null;
handler = (s, e) =>
{
window.ContentRendered -= handler;
tcs.SetResult(null);
};
window.ContentRendered += handler;
return tcs.Task;
}
public static double GetWindowsScaleTransform(this Visual visual)
{
Matrix m = Matrix.Identity;
var presentationSource = PresentationSource.FromVisual(visual);
if (presentationSource != null)
{
if (presentationSource.CompositionTarget != null) m = presentationSource.CompositionTarget.TransformToDevice;
}
double totalTransform = m.M11;
return totalTransform;
}
}
您可以在這裏找到SharedBitmapSource。 https://stackoverflow.com/a/32841840/690656
+0
我需要異步渲染巨大的位圖(6000x6000像素)。這個解決方案是否工作,還是僅限於窗口大小? – Arek
0
我不認爲你可以。
你可以嘗試向上執行使用類似await Task.Run(() => bitmap.Render(Button));
後臺線程渲染,但它不會工作:後臺線程將不會被允許訪問Button
。
+0
我看到的唯一方法是使用反射來從調度程序中斷開元素 – Andreas
相關問題
- 1. 將WPF窗口呈現爲位圖
- 2. 如何呈現UIImage異步?
- 3. 僅在異步處理循環完成後呈現視圖
- 4. 在後臺異步呈現視圖控制器
- 5. 異步子視圖異步
- 6. 呈現,解聘視圖導致異常
- 7. 視覺在WPF
- 8. 在Backbone中呈現多個視圖會發生同步還是異步?
- 9. 如何呈現從有位圖效果WPF元素的位圖?
- 10. WPF從鼠標位置獲取視覺
- 11. 如何在WPF中將位圖呈現到畫布中?
- 12. 從視圖模型打印WPF視覺
- 13. WPF圖片/呈現事件
- 14. WPF呈現無圖形卡
- 15. 從Mysql到WPF異步加載圖像
- 16. 即時呈現,呈現視圖
- 17. WPF視覺繼承
- 18. WPF將視覺刷的視覺綁定到不同的窗口
- 19. 骨幹視圖重新呈現,如果任何其他視圖得到呈現
- 20. UI-Router嵌套狀態視圖不呈現(呈現父視圖)
- 21. 從當前呈現的模態視圖呈現模態視圖
- 22. 異步管道無法呈現Observable
- 23. 燒瓶 - 異步呈現模板
- 24. 異步控制器呈現數據
- 25. WPF更高效渲染()視覺/控制位圖
- 26. 將「不可見」的WPF控件呈現爲位圖圖像
- 27. WPF - Windows 7 - 64位 - 無內容呈現
- 28. 打印WPF視覺包含圖像
- 29. 無法使用視圖路由器解析異步組件呈現
- 30. backbone.js視圖不會顯示結果,因爲異步獲取,不會呈現
這是純粹的猜測,你有沒有想過使用'Dispatcher.BeginInvoke()'? [docs](https://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.begininvoke(v = vs.110).aspx) – XAMlMAX
不幸的是,這也會在UI上下文/線程中運行... – Andreas