Capture the controls' OnPaint event for offline rendering - c#

I'm having a little bit trouble bringing together the old windows forms world and the WPF world. The original problem was that we always have to have a WindowsFormsHost for hosting our windows forms controls, especially the Chart control for displaying graphs.
Since we also have the need for client/server support, my idea was to create a windows form and add the Chart as a child. For this reason I created a OffscreenForm (BackEnd) which I shift to a location where the window is not visible (e.g. X=-10000, Y=-10000).
Then I attach my FrontEnd WPF control to a userdefined OnPaint method that updates everytime the BackEnd has changed. The problem in OnPaint is that is only renders the visible part. Therefore I took OnPrint, but DrawToImage has a memory bug and doesn't clean up correctly. I also transferred mouse events from the FrontEnd to the BackEnd.
In short words: Has someone ever tried to create something similar. Most of my current solution works fine, but there are minor things I don't really understand between OnPaint/OnPrint.
Here's my code. It looks currently a little bit ugly. Some of the things are only to see something when something happens.
It would be great if someone has a solution to get the image of the control when a refresh/repaint occurs.
Thanks
Martin
BackEnd:
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);
}
}
FrontEnd:
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;
}
}
}

Related

How to make a window non-capturable by screenshots? [duplicate]

I'm trying to write a magnifier application using a winform or wpf windows. The idea is to drag the window over a spot on the screen and magnify it. I know it exists commercially but need to build a customized version. The challenge I'm having is to capture the screen image behind the active application.
I have found code to capture a screen image below. But it includes the active window
{
Rectangle bounds = new Rectangle(this.Location, this.Size);
Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height);
Graphics g = Graphics.FromImage(bitmap);
g.CopyFromScreen(this.Location, Point.Empty, bounds.Size);
Bitmap bp2 = new Bitmap(bitmap); // local copy of image...
pictureBox1.Image = bp2;
}
Adding statements to hide the active application correct the image capture, but introduce a screen flicker which I'd like to avoid. (modified code below)
{
this.StartPosition = FormStartPosition.Manual; // get current window location
Point cur = this.Location;
this.Location = new Point(-500, -500); // hide the active app off screen.
Rectangle bounds = new Rectangle(cur, this.Size);
Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height);
Graphics g = Graphics.FromImage(bitmap);
g.CopyFromScreen(cur, Point.Empty, bounds.Size);
Bitmap bp2 = new Bitmap(bitmap); // local copy of image...
pictureBox1.Image = bp2;
this.Location = cur; // restore application location
}
Can someone suggest an alternative to capture a screen region, behind an active windows?
Thx.
Wrapping the Magnification API is pretty useful. I created a Winforms control that does this. Add a new class to your project and paste the code shown below. Compile. Drop the new control from the top of the toolbox onto a form.
The TrackMouse property controls which part of the screen is shown magnified. Set to False, it magnifies the area covered by the control, making it act like a magnifying glass. Set to True, it will operate like Windows' Magnifier, following the mouse.
The Magnification property controls the amount of magnification. You can already adjust it by using the mouse wheel.
The form you drop it should have its TopMost property set to True. You might want to tinker with its Region property to make it resemble a spyglass.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
class Magnifier : Control {
public Magnifier() {
if (!MagInitialize()) throw new NotSupportedException();
timer = new Timer { Interval = 45 };
timer.Tick += (o, ea) => { if (trackMouse) setSource(false); else Invalidate(); };
}
[DefaultValue(false)]
public bool TrackMouse {
get { return trackMouse; }
set { trackMouse = value; setSource(false); }
}
[DefaultValue(2.0f)]
public float Magnification {
get { return magnification; }
set { magnification = Math.Max(1, value); setSource(true); }
}
protected override CreateParams CreateParams {
get {
var cp = base.CreateParams;
if (!this.DesignMode) {
cp.ClassName = "Magnifier";
//cp.Style |= MS_SHOWMAGNIFIEDCURSOR;
this.SetStyle(ControlStyles.UserPaint, true);
}
return cp;
}
}
protected override void OnHandleCreated(EventArgs e) {
base.OnHandleCreated(e);
if (!this.DesignMode) {
setSource(true);
this.FindForm().LocationChanged += ParentLocationChanged;
timer.Start();
}
}
protected override void Dispose(bool disposing) {
if (disposing) {
var frm = this.FindForm();
if (frm != null) frm.LocationChanged -= ParentLocationChanged;
timer.Dispose();
MagUninitialize();
}
base.Dispose(disposing);
}
private void ParentLocationChanged(object sender, EventArgs e) {
if (!trackMouse) setSource(false);
}
protected override void OnSizeChanged(EventArgs e) {
setSource(false);
base.OnSizeChanged(e);
}
protected override void OnMouseWheel(MouseEventArgs e) {
this.Magnification += e.Delta / 100f;
((HandledMouseEventArgs)e).Handled = true;
}
private void setSource(bool newmag) {
if (!this.IsHandleCreated || this.DesignMode) return;
if (newmag) {
var xform = new MAGTRANSFORM();
xform.v11 = xform.v22 = magnification;
xform.v33 = 1.0f;
MagSetWindowTransform(this.Handle, ref xform);
}
Point center;
if (trackMouse) center = Cursor.Position;
else {
var rc = this.RectangleToScreen(this.Bounds);
center = new Point(rc.Left + rc.Width / 2, rc.Top + rc.Height / 2);
}
var scr = Screen.FromPoint(center);
var rect = new RECT();
rect.left = Math.Max(scr.Bounds.Left, center.X - (int)(this.Width / magnification / 2));
rect.top = Math.Max(scr.Bounds.Top, center.Y - (int)(this.Height / magnification / 2));
rect.right = rect.left + (int)(this.Width / magnification);
if (rect.right > scr.Bounds.Right) {
rect.right = scr.Bounds.Right;
rect.left = rect.right - (int)(this.Width / magnification);
}
rect.bottom = center.Y + (int)(this.Height / magnification);
if (rect.bottom > scr.Bounds.Bottom) {
rect.bottom = scr.Bounds.Bottom;
rect.top = rect.bottom - (int)(this.Height / magnification);
}
MagSetWindowSource(this.Handle, ref rect);
this.Invalidate();
}
private Timer timer;
private bool trackMouse;
private float magnification = 2.0f;
private struct RECT {
public int left, top, right, bottom;
}
private struct MAGTRANSFORM {
public float v11, v12, v13;
public float v21, v22, v23;
public float v31, v32, v33;
}
[DllImport("magnification.dll")]
private static extern bool MagInitialize();
[DllImport("magnification.dll")]
private static extern bool MagUninitialize();
[DllImport("magnification.dll")]
private static extern bool MagSetWindowSource(IntPtr hWnd, ref RECT rc);
[DllImport("magnification.dll")]
private static extern bool MagSetWindowTransform(IntPtr hWnd, ref MAGTRANSFORM xform);
private const int MS_SHOWMAGNIFIEDCURSOR = 1;
private const int MS_CLIPAROUNDCURSOR = 2;
private const int MS_INVERTCOLORS = 4;
}

TabControl Paint and Boundries in Winforms C#

I used a custom control which is created by other posts. I tried to modify it to fit my requirements but I'm stuck in those areas which we cannot edit it.
This is my Modified Control Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace CControls
{
public partial class TapControl : TabControl
{
#region "Properties"
private int _hotTabIndex = -1;
private int HotTabIndex
{
get { return _hotTabIndex; }
set
{
if (_hotTabIndex != value)
{
_hotTabIndex = value;
this.Invalidate();
}
}
}
private int CloseButtonHeight
{
get { return FontHeight; }
}
#endregion
public TapControl()
{
InitializeComponent();
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.DrawMode = TabDrawMode.OwnerDrawFixed;
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawLine(Pens.White, 0, 0, ClientRectangle.Right,ClientRectangle.Top);
e.Graphics.DrawLine(Pens.Red, 0, ItemSize.Height+2, ClientRectangle.Right-1, ItemSize.Height+2);
base.OnPaint(e);
for (int id = 0; id < this.TabCount; id++)
DrawTabContent(e.Graphics, id);
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
base.OnPaintBackground(pevent);
for (int id = 0; id < this.TabCount; id++)
DrawTabBackground(pevent.Graphics, id);
}
protected override void OnCreateControl()
{
base.OnCreateControl();
this.OnFontChanged(EventArgs.Empty);
}
protected override void OnFontChanged(EventArgs e)
{
base.OnFontChanged(e);
IntPtr hFont = this.Font.ToHfont();
SendMessage(this.Handle, WM_SETFONT, hFont, new IntPtr(-1));
SendMessage(this.Handle, WM_FONTCHANGE, IntPtr.Zero, IntPtr.Zero);
this.UpdateStyles();
}
private void DrawTabContent(Graphics graphics, int id)
{
bool selectedOrHot = id == this.SelectedIndex || id == this.HotTabIndex;
bool vertical = this.Alignment >= TabAlignment.Left;
Image tabImage = null;
if (this.ImageList != null)
{
TabPage page = this.TabPages[id];
if (page.ImageIndex > -1 && page.ImageIndex < this.ImageList.Images.Count)
tabImage = this.ImageList.Images[page.ImageIndex];
if (page.ImageKey.Length > 0 && this.ImageList.Images.ContainsKey(page.ImageKey))
tabImage = this.ImageList.Images[page.ImageKey];
}
Rectangle tabRect = GetTabRect(id);
Rectangle contentRect = vertical ? new Rectangle(0, 0, tabRect.Height, tabRect.Width) : new Rectangle(Point.Empty, tabRect.Size);
contentRect = new Rectangle(ClientRectangle.X, ClientRectangle.Y, ItemSize.Width, ItemSize.Height);
Rectangle textrect = contentRect;
textrect.Width -= FontHeight;
if (tabImage != null)
{
textrect.Width -= tabImage.Width;
textrect.X += tabImage.Width;
}
Color frColor = id == SelectedIndex ? Color.Black : this.ForeColor;
Color bkColor = id == SelectedIndex ? Color.White : FindForm().BackColor;
using (Bitmap bm = new Bitmap(contentRect.Width, contentRect.Height))
{
using (Graphics bmGraphics = Graphics.FromImage(bm))
{
TextRenderer.DrawText(bmGraphics, this.TabPages[id].Text, this.Font, textrect, frColor, bkColor);
//if (selectedOrHot)
//{
Rectangle closeRect = new Rectangle(contentRect.Right - CloseButtonHeight, 0, CloseButtonHeight, CloseButtonHeight);
closeRect.Offset(-2, (contentRect.Height - closeRect.Height) / 2);
DrawCloseButton(bmGraphics, closeRect);
//}
if (tabImage != null)
{
Rectangle imageRect = new Rectangle(Padding.X, 0, tabImage.Width, tabImage.Height);
imageRect.Offset(0, (contentRect.Height - imageRect.Height) / 2);
bmGraphics.DrawImage(tabImage, imageRect);
}
}
if (vertical)
{
if (this.Alignment == TabAlignment.Left)
bm.RotateFlip(RotateFlipType.Rotate270FlipNone);
else
bm.RotateFlip(RotateFlipType.Rotate90FlipNone);
}
graphics.DrawImage(bm, tabRect);
}
}
private void DrawCloseButton(Graphics graphics, Rectangle bounds)
{
using (Font closeFont = new Font("Tahoma", Font.Size, FontStyle.Bold))
TextRenderer.DrawText(graphics, "x", closeFont, bounds, Color.Gray, Color.Transparent, TextFormatFlags.HorizontalCenter | TextFormatFlags.NoPadding | TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter);
}
private void DrawTabBackground(Graphics graphics, int id)
{
if (id == SelectedIndex)
graphics.FillRectangle(Brushes.White, GetTabRect(id));
else if (id == HotTabIndex)
{
Rectangle rc = GetTabRect(id);
rc.Width--;
rc.Height--;
graphics.DrawRectangle(Pens.DarkGray, rc);
}
}
private const int TCM_ADJUSTRECT = 0x1328;//(TCM_FIRST + 40);
protected override void WndProc(ref Message m)
{
if (m.Msg == TCM_SETPADDING)
{
m.LParam = MAKELPARAM(this.Padding.X + CloseButtonHeight / 2, this.Padding.Y);
}
if (m.Msg == WM_MOUSEDOWN && !this.DesignMode)
{
Point pt = this.PointToClient(Cursor.Position);
Rectangle closeRect = GetCloseButtonRect(HotTabIndex);
if (closeRect.Contains(pt) && HotTabIndex != 0)
{
TabPages.RemoveAt(HotTabIndex);
m.Msg = WM_NULL;
}
}
if (m.Msg == TCM_ADJUSTRECT && !this.DesignMode)
{
RECT rc = new RECT();
rc.Left -= 3;
rc.Right += 1;
rc.Top -= 1;
rc.Bottom += 1;
Marshal.StructureToPtr(rc, m.LParam, true);
}
base.WndProc(ref m);
}
private struct RECT{
public int Left, Top, Right, Bottom;
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
TCHITTESTINFO HTI = new TCHITTESTINFO(e.X, e.Y);
HotTabIndex = SendMessage(this.Handle, TCM_HITTEST, IntPtr.Zero, ref HTI);
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
HotTabIndex = -1;
}
private IntPtr MAKELPARAM(int lo, int hi)
{
return new IntPtr((hi << 16) | (lo & 0xFFFF));
}
private Rectangle GetCloseButtonRect(int id)
{
Rectangle tabRect = GetTabRect(id);
Rectangle closeRect = new Rectangle(tabRect.Left, tabRect.Top, CloseButtonHeight, CloseButtonHeight);
switch (Alignment)
{
case TabAlignment.Left:
closeRect.Offset((tabRect.Width - closeRect.Width) / 2, 0);
break;
case TabAlignment.Right:
closeRect.Offset((tabRect.Width - closeRect.Width) / 2, tabRect.Height - closeRect.Height);
break;
default:
closeRect.Offset(tabRect.Width - closeRect.Width, (tabRect.Height - closeRect.Height) / 2);
break;
}
return closeRect;
}
#region Interop
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hwnd, int msg, IntPtr wParam, ref TCHITTESTINFO lParam);
[StructLayout(LayoutKind.Sequential)]
private struct TCHITTESTINFO
{
public Point pt;
public TCHITTESTFLAGS flags;
public TCHITTESTINFO(int x, int y)
{
pt = new Point(x, y);
flags = TCHITTESTFLAGS.TCHT_NOWHERE;
}
}
[Flags()]
private enum TCHITTESTFLAGS
{
TCHT_NOWHERE = 1,
TCHT_ONITEMICON = 2,
TCHT_ONITEMLABEL = 4,
TCHT_ONITEM = TCHT_ONITEMICON | TCHT_ONITEMLABEL
}
private const int WM_NULL = 0x0;
private const int WM_SETFONT = 0x30;
private const int WM_FONTCHANGE = 0x1D;
private const int WM_MOUSEDOWN = 0x201;
private const int TCM_FIRST = 0x1300;
private const int TCM_HITTEST = TCM_FIRST + 13;
private const int TCM_SETPADDING = TCM_FIRST + 43;
#endregion
}
}
and in the Below Picture, the result and required modifications

How do I detect the color of a certain pixel?

I want to make a system that can detect the color of a certain pixel that is selected.
Color[] pixelData;
pixelData= new Color[graphics.GraphicsDevice.Viewport.Width * (graphics.GraphicsDevice.Viewport.Height)];
Terrain.GetData(pixelData);
Now I want to do something like this
if(pixelData[graphics.GraphicsDevice.Viewport.Width * playervec.Y + playervec.X].R == 255)
{
rest of the code
}
But I don't know how I should do the part where it picks a certain pixel
This may help you
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Diagnostics;
namespace FormTest
{
public partial class Form1 : Form
{
[DllImport("user32.dll")]
static extern bool GetCursorPos(ref Point lpPoint);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern int BitBlt(IntPtr hDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);
public Form1()
{
InitializeComponent();
}
private void MouseMoveTimer_Tick(object sender, EventArgs e)
{
Point cursor = new Point();
GetCursorPos(ref cursor);
var c = GetColorAt(cursor);
this.BackColor = c;
if (c.R == c.G && c.G < 64 && c.B > 128)
{
MessageBox.Show("Blue");
}
}
Bitmap screenPixel = new Bitmap(1, 1, PixelFormat.Format32bppArgb);
public Color GetColorAt(Point location)
{
using (Graphics gdest = Graphics.FromImage(screenPixel))
{
using (Graphics gsrc = Graphics.FromHwnd(IntPtr.Zero))
{
IntPtr hSrcDC = gsrc.GetHdc();
IntPtr hDC = gdest.GetHdc();
int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, location.X, location.Y, (int)CopyPixelOperation.SourceCopy);
gdest.ReleaseHdc();
gsrc.ReleaseHdc();
}
}
return screenPixel.GetPixel(0, 0);
}
}
}
EDIT:
Given the above GetColorAt function you can poll a certain pixel on the screen in a safe, performance friendly way like this:
private void PollPixel(Point location, Color color)
{
while(true)
{
var c = GetColorAt(location);
if (c.R == color.R && c.G == color.G && c.B == color.B)
{
DoAction();
return;
}
// By calling Thread.Sleep() without a parameter, we are signaling to the
// operating system that we only want to sleep long enough for other
// applications. As soon as the other apps yield their CPU time, we will
// regain control.
Thread.Sleep()
}
}
Source : How to read the Color of a Screen Pixel

Capturing webpage as image in c#, ensuring javascript rendered elements are visible

I am trying to capture the following page using standard c# .net code. I've searched around for people's various methods, most of which involve instantiating a browser object and using a draw to bitmap method. However, none of these pick up the contents of the chart on this page:
http://www.highcharts.com/demo/combo-dual-axes
Perhaps the javascript doesn't have time to run, but adding Thread.Sleep(x) hasn't assisted.
This commercial component captures it correctly, but I'd rather avoid requiring an additional dependency in my project and paying $150 when the other solutions are sooo close!.
Anyone find their solution renders this correctly?
You have possibly tried IECapt. I think it is the right way to go. I created a modified version of it and use a timer instead of Thread.Sleep it captures your site as expected.
------EDIT------
Here is the ugly source. Just Add a reference to Microsoft HTML Object Library.
And this is the usage:
HtmlCapture capture = new HtmlCapture(#"c:\temp\myimg.png");
capture.HtmlImageCapture += new HtmlCapture.HtmlCaptureEvent(capture_HtmlImageCapture);
capture.Create("http://www.highcharts.com/demo/combo-dual-axes");
void capture_HtmlImageCapture(object sender, Uri url)
{
this.Close();
}
File1
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
namespace MyIECapt
{
public class HtmlCapture
{
private WebBrowser web;
private Timer tready;
private Rectangle screen;
private Size? imgsize = null;
//an event that triggers when the html document is captured
public delegate void HtmlCaptureEvent(object sender, Uri url);
public event HtmlCaptureEvent HtmlImageCapture;
string fileName = "";
//class constructor
public HtmlCapture(string fileName)
{
this.fileName = fileName;
//initialise the webbrowser and the timer
web = new WebBrowser();
tready = new Timer();
tready.Interval = 2000;
screen = Screen.PrimaryScreen.Bounds;
//set the webbrowser width and hight
web.Width = 1024; //screen.Width;
web.Height = 768; // screen.Height;
//suppress script errors and hide scroll bars
web.ScriptErrorsSuppressed = true;
web.ScrollBarsEnabled = false;
//attached events
web.Navigating +=
new WebBrowserNavigatingEventHandler(web_Navigating);
web.DocumentCompleted += new
WebBrowserDocumentCompletedEventHandler(web_DocumentCompleted);
tready.Tick += new EventHandler(tready_Tick);
}
public void Create(string url)
{
imgsize = null;
web.Navigate(url);
}
public void Create(string url, Size imgsz)
{
this.imgsize = imgsz;
web.Navigate(url);
}
void web_DocumentCompleted(object sender,
WebBrowserDocumentCompletedEventArgs e)
{
//start the timer
tready.Start();
}
void web_Navigating(object sender, WebBrowserNavigatingEventArgs e)
{
//stop the timer
tready.Stop();
}
void tready_Tick(object sender, EventArgs e)
{
try
{
//stop the timer
tready.Stop();
mshtml.IHTMLDocument2 docs2 = (mshtml.IHTMLDocument2)web.Document.DomDocument;
mshtml.IHTMLDocument3 docs3 = (mshtml.IHTMLDocument3)web.Document.DomDocument;
mshtml.IHTMLElement2 body2 = (mshtml.IHTMLElement2)docs2.body;
mshtml.IHTMLElement2 root2 = (mshtml.IHTMLElement2)docs3.documentElement;
// Determine dimensions for the image; we could add minWidth here
// to ensure that we get closer to the minimal width (the width
// computed might be a few pixels less than what we want).
int width = Math.Max(body2.scrollWidth, root2.scrollWidth);
int height = Math.Max(root2.scrollHeight, body2.scrollHeight);
//get the size of the document's body
Rectangle docRectangle = new Rectangle(0, 0, width, height);
web.Width = docRectangle.Width;
web.Height = docRectangle.Height;
//if the imgsize is null, the size of the image will
//be the same as the size of webbrowser object
//otherwise set the image size to imgsize
Rectangle imgRectangle;
if (imgsize == null) imgRectangle = docRectangle;
else imgRectangle = new Rectangle() { Location = new Point(0, 0), Size = imgsize.Value };
//create a bitmap object
Bitmap bitmap = new Bitmap(imgRectangle.Width, imgRectangle.Height);
//get the viewobject of the WebBrowser
IViewObject ivo = web.Document.DomDocument as IViewObject;
using (Graphics g = Graphics.FromImage(bitmap))
{
//get the handle to the device context and draw
IntPtr hdc = g.GetHdc();
ivo.Draw(1, -1, IntPtr.Zero, IntPtr.Zero,
IntPtr.Zero, hdc, ref imgRectangle,
ref docRectangle, IntPtr.Zero, 0);
g.ReleaseHdc(hdc);
}
//invoke the HtmlImageCapture event
bitmap.Save(fileName);
bitmap.Dispose();
}
catch
{
//System.Diagnostics.Process.GetCurrentProcess().Kill();
}
if(HtmlImageCapture!=null) HtmlImageCapture(this, web.Url);
}
}
}
and File2
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Runtime.InteropServices;
namespace MyIECapt
{
[ComVisible(true), ComImport()]
[GuidAttribute("0000010d-0000-0000-C000-000000000046")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IViewObject
{
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int Draw(
[MarshalAs(UnmanagedType.U4)] UInt32 dwDrawAspect,
int lindex,
IntPtr pvAspect,
[In] IntPtr ptd,
IntPtr hdcTargetDev,
IntPtr hdcDraw,
[MarshalAs(UnmanagedType.Struct)] ref Rectangle lprcBounds,
[MarshalAs(UnmanagedType.Struct)] ref Rectangle lprcWBounds,
IntPtr pfnContinue,
[MarshalAs(UnmanagedType.U4)] UInt32 dwContinue);
[PreserveSig]
int GetColorSet([In, MarshalAs(UnmanagedType.U4)] int dwDrawAspect,
int lindex, IntPtr pvAspect, [In] IntPtr ptd,
IntPtr hicTargetDev, [Out] IntPtr ppColorSet);
[PreserveSig]
int Freeze([In, MarshalAs(UnmanagedType.U4)] int dwDrawAspect,
int lindex, IntPtr pvAspect, [Out] IntPtr pdwFreeze);
[PreserveSig]
int Unfreeze([In, MarshalAs(UnmanagedType.U4)] int dwFreeze);
}
}
Thread.Sleep will simply suspend the thread your web browser is running on - how do you expect it to render anything when it is suspended? :)
Instead, you need to allow the thread to process work. You can achieve this with a combination of Thread.Sleep(0) and Application.DoEvents(), with something like the following:
DateTime finish = DateTime.Now.AddSeconds(3);
while (DateTime.Now < finish) {
Application.DoEvents();
Thread.Sleep(0);
}
#L.B , thank you for the help!
Just an FYI for anyone wanting to run it in a class library,
WebBrowser needs to Single Threaded Apartment, so do something like this:
var t = new Thread(InitAndDo); //InitAndDo would have your code creating the webbrowser object etc...
t.SetApartmentState(ApartmentState.STA);
t.Start();
Then the Gotcha, after the navigate call is done, add this line of code so that you get the completed navigation event:
web.Navigate(Url);
Application.Run();
I created a nuget package for this purpose
https://github.com/dcumin39/RenderHighCharts/wiki

Geting a inverted result from using FromArgb R = B? where does this fail?

the R and B gets error when using this logic, i cant seem to find what im doing wrong, my workaround in the end were i flip the r and b is not good at all and im trying to find where the logic breakes.
the label1.Text = colorX; shows R=255, G=0, B=0 when it shold be R=0, G=0, B=255, where does this fail?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
namespace Color_tool
{
public partial class Form1 : Form
{
Regex rgbInputR;
Regex rgbInputG;
Regex rgbInputB;
int r;
int g;
int b;
string colorX;
[DllImport("gdi32")]
private static extern int GetPixel(IntPtr hdc, int x, int y);
[DllImport("User32")]
private static extern IntPtr GetWindowDC(IntPtr hwnd);
private static readonly IntPtr DesktopDC = GetWindowDC(IntPtr.Zero);
public static System.Drawing.Color GetPixelAtCursor()
{
System.Drawing.Point p = Cursor.Position;
return System.Drawing.Color.FromArgb(GetPixel(DesktopDC, p.X, p.Y));
}
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
button1.BackColor = Color.Black;
}
private void timer1_Tick(object sender, EventArgs e)
{
colorX = GetPixelAtCursor().ToString();
Color backX = GetPixelAtCursor();
this.BackColor = Color.FromArgb(r,g,b);
label1.Text = colorX;
RGB_value();
}
private void button1_Click(object sender, EventArgs e)
{
if (timer1.Enabled == false)
timer1.Enabled = true;
else
timer1.Enabled = false;
}
private void RGB_value()
{
rgbInputR = new Regex(#"(?<=R=)\d{0,3}");
rgbInputG = new Regex(#"(?<=G=)\d{0,3}");
rgbInputB = new Regex(#"(?<=B=)\d{0,3}");
Match R, G, B;
R = rgbInputR.Match(colorX);
G = rgbInputG.Match(colorX);
B = rgbInputB.Match(colorX);
//had to flip the R and B ???
b = int.Parse(R.Groups[0].Value);
g = int.Parse(G.Groups[0].Value);
r = int.Parse(B.Groups[0].Value);
}
}
}
I think you're going about this in an odd way for one; Color has R, G, and B properties you can use instead of string matching:
R = colorX.R;
G = colorX.G;
B = colorX.B;
Second, to address your question, GetPixel is probably getting BGR formatted pixels instead of RGB. BGR is commonly used in bitmaps and the HDC of the window you're talking to is most likely returning in this format.
Edit: from the MSDN docs:
When specifying an explicit RGB color,
the COLORREF value has the following
hexadecimal form. 0x00bbggrr
The Color.FromArgb() method expects 0xAARRGGBB. Your workaround is just fine, although a proper fix would be to flip the R and B components before you call the .FromArgb() method:
int color = GetPixel(...);
return Color.FromArgb(color & 0xFF, color >> 8 & 0xFF, color >> 16);

Categories

Resources