我在将旧的Windows窗体世界和WPF世界结合在一起时遇到了一些麻烦。最初的问题是我们总是需要一个WindowsFormsHost来托管我们的窗体控件,尤其是用于显示图形的Chart控件。
由于我们还需要客户端/服务器支持,我的想法是创建一个Windows窗体并将图表添加为子窗口。出于这个原因,我创建了一个OffscreenForm(BackEnd),我转移到窗口不可见的位置(例如X = -10000,Y = -10000)。
然后我将FrontEnd WPF控件附加到用户定义的OnPaint方法,该方法每次BackEnd更改时都会更新。 OnPaint中的问题是只渲染可见部分。因此我使用了OnPrint,但DrawToImage有一个内存错误,并没有正确清理。我还将鼠标事件从FrontEnd传输到BackEnd。
简而言之:有人试图创造类似的东西。我当前的大部分解决方案都运行正常,但在OnPaint / OnPrint之间我不太了解一些小事。
这是我的代码。它看起来有点难看。有些事情只是在事情发生时看到的东西。
如果有人有解决方案在刷新/重绘时获取控件的图像,那就太棒了。
由于 马丁
后端:
using siemens.Win32;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace formshost
{
public class OffscreenWindowsFormsRenderer : Control
{
public event EventHandler<MemoryStream> MyPaint;
System.Windows.Forms.Form form = new System.Windows.Forms.Form();
List<Point> pts = new List<Point>();
int x = 10;
public OffscreenWindowsFormsRenderer(Control ctrl)
: base()
{
Application.EnableVisualStyles();
form = new System.Windows.Forms.Form();
form.CreateControl();
form.SetBounds(-10000, 300, 0, 0, System.Windows.Forms.BoundsSpecified.Location);
form.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
form.Show();
form.Controls.Add(this);
CreateControl();
BackColor = Color.Lime;
Controls.Add(ctrl);
//FireImage();
Timer T = new Timer();
T.Tick += T_Tick;
T.Start();
}
void T_Tick(object sender, EventArgs e)
{
Refresh();
x += 5;
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.FillRectangle(Brushes.Yellow, x, x, 50, 50);
lock (pts)
{
if (pts.Count > 1)
e.Graphics.DrawLines(Pens.Red, pts.ToArray());
}
FireImage();
}
protected override void OnPrint(PaintEventArgs e)
{
base.OnPrint(e);
//FireImage();
}
Bitmap bmp;
void FireImage()
{
lock (this)
{
int W = Width;
int H = Height;
if (bmp != null)
{
if (bmp.Width != W || bmp.Height != H)
{
bmp.Dispose();
bmp = null;
}
}
if (bmp == null)
{
if (W > 0 && H > 0)
bmp = new Bitmap(W, H, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
}
if (bmp != null)
{
DrawToBitmapExtended(bmp, new Rectangle(0, 0, W, H));
MemoryStream mem = new MemoryStream();
bmp.Save(mem, ImageFormat.Bmp);
MyPaint(this, mem);
}
// using(Graphics g
}
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
FireImage();
}
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
Refresh();
}
public void MouseMove(IntPtr wparam, IntPtr lparam)
{
//Message msg = Message.Create(this.Handle, User.WM_MOUSEMOVE, wparam, lparam);
SendMessage(this.Handle, User.WM_MOUSEMOVE, wparam, lparam);
FireImage();
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
lock (pts)
{
pts.Add(e.Location);
}
}
Bitmap tempimage;
public void DrawToBitmapExtended(Bitmap bitmap, Rectangle targetBounds)
{
if (bitmap == null)
{
throw new ArgumentNullException("bitmap");
}
if (targetBounds.Width <= 0 || targetBounds.Height <= 0
|| targetBounds.X < 0 || targetBounds.Y < 0)
{
throw new ArgumentException("targetBounds");
}
if (!IsHandleCreated)
{
CreateHandle();
}
int width = Math.Min(this.Width, targetBounds.Width);
int height = Math.Min(this.Height, targetBounds.Height);
if (tempimage != null)
{
if (tempimage.Width != width || tempimage.Height != height)
{
tempimage.Dispose();
tempimage = null;
}
}
// if (tempimage == null)
tempimage = new Bitmap(width, height, bitmap.PixelFormat);
using (Graphics g = Graphics.FromImage(tempimage))
{
IntPtr hDc = g.GetHdc();
//send the actual wm_print message
SendMessage(this.Handle, WM_PRINT, (IntPtr)hDc,
(IntPtr)(PRF_CHILDREN | PRF_CLIENT | PRF_NONCLIENT ));
//now BLT the result to the destination bitmap.
using (Graphics destGraphics = Graphics.FromImage(bitmap))
{
IntPtr desthDC = destGraphics.GetHdc();
BitBlt(desthDC, targetBounds.X, targetBounds.Y, width, height,
hDc, 0, 0, 0x00CC0020);
destGraphics.ReleaseHdc(desthDC);
}
g.ReleaseHdc(hDc);
}
}
const int PRF_NONCLIENT = 0x00000002,
PRF_CLIENT = 0x00000004,
PRF_ERASEBKGND = 0x00000008,
PRF_CHILDREN = 0x00000010,
WM_PRINT = 0x0317,
WM_PRINTCLIENT = 0x0318,
PRF_OWNED = 0x0020;
[DllImport("gdi32.dll")]
static extern int SetROP2(IntPtr hdc, int fnDrawMode);
[DllImport("User32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
[DllImport("Gdi32.dll", SetLastError = true, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
public static extern int BitBlt(IntPtr hDC, int x, int y, int nWidth, int nHeight,
IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);
}
}
前端:
using formshost;
using siemens.Win32;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication6
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
OffscreenWindowsFormsRenderer off;
System.Windows.Forms.PictureBox box = new System.Windows.Forms.PictureBox();
public MainWindow()
{
InitializeComponent();
}
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
PanelFast fast = new PanelFast();
myGrid.Children.Add(fast);
Grid.SetRow(fast, 1);
box.Width = 100;
box.Height = 100;
box.Image = new Bitmap("D:\\image.jpg");
//box.Dock = System.Windows.Forms.DockStyle.Fill;
box.Show();
off = new OffscreenWindowsFormsRenderer(box);
off.Visible = true;
fast.SetRenderer(off);
}
}
public class PanelFast : Panel
{
public OffscreenWindowsFormsRenderer Renderer
{
get;
private set;
}
BitmapImage myBitmap = null;
public void SetRenderer(OffscreenWindowsFormsRenderer r)
{
Renderer = r;
Renderer.MyPaint += off_MyPaint;
}
void off_MyPaint(object sender, MemoryStream e)
{
myBitmap = new BitmapImage();
myBitmap.BeginInit();
myBitmap.StreamSource = e;
myBitmap.EndInit();
myBitmap.Freeze();
SnapsToDevicePixels = true;
InvalidateVisual();
}
public PanelFast()
{
SizeChanged += PanelFast_SizeChanged;
}
void PanelFast_SizeChanged(object sender, SizeChangedEventArgs e)
{
Renderer.SetBounds(0, 0, (int)ActualWidth, (int)ActualHeight, System.Windows.Forms.BoundsSpecified.Size);
}
protected override void OnRender(DrawingContext dc)
{
base.OnRender(dc);
dc.DrawImage(myBitmap, new Rect(0, 0, ActualWidth, ActualHeight));
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
Renderer.MouseMove((IntPtr)0, (IntPtr)GetXY(e.GetPosition(this)));
}
int GetXY(System.Windows.Point pt)
{
int x = (int)(pt.X);
int y = (int)(pt.Y);
return y << 16 | x;
}
}
}