Jonathan.Peppers

I'm working on a Prettier version of the messagebox, that has portions of it transparent over top of my main application. However, if I set AllowsTransparency to true for both windows, the CPU usage goes up quite a bit and I can't have that happen.

So my solution was to grab a screenshot and set it to the background image of the MessageBox, but I have had problems with that as well. First I tried using GDI+:

public static Bitmap GetScreenShot(int x, int y, int width, int height)

{

Bitmap screen = new Bitmap(width, height);

Graphics g = Graphics.FromImage(screen);

g.CopyFromScreen(x, y, 0, 0, new System.Drawing.Size(width, height));

g.Dispose();

return screen;

}

And then called this to convert to an image source:

public static BitmapSource ConvertImage(Bitmap image)

{

if (image == null)

{

return null;

}

return Imaging.CreateBitmapSourceFromHBitmap(image.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());

}

This worked almost perfect, however sometimes (only on one computer I can recreate) the image of the whole Messagebox would have approx. 10-20 pixel lines that were switched around going across the screen. The entire image was "chopped up."

So then I tried this in WPF:

public static BitmapSource GetScreenShot(Visual target)

{

Rect bounds = VisualTreeHelper.GetDescendantBounds(target);

RenderTargetBitmap bitmap = new RenderTargetBitmap(800, 600, 96, 96, PixelFormats.Pbgra32);

DrawingVisual drawingvisual = new DrawingVisual();

using (DrawingContext context = drawingvisual.RenderOpen())

{

context.DrawRectangle(new VisualBrush(target), null, new Rect(new Point(), bounds.Size));

context.Close();

}

try

{

bitmap.Render(drawingvisual);

}

catch (Exception exc)

{

MessageBox.Show(exc.ToString());

}

return bitmap;

}

I however would get an exception every other time (explains the try catch around bitmap.Render). I believe it was System.AccessViolationException "Attempt to read protected memory. something is corrupt" or similar.

So would anyone have any suggestions I'll take any path that will work. The WPF way seemed slow, because my whole parent window had to render again. GDI was faster just because it went straight for the screen. AllowsTransparency would be optimal, but would it work to set and unset this variable when a box shows up In either case, my app is full screen at 800x600 resolution (and will be the only app running), so I have a lot of options.



Re: Windows Presentation Foundation (WPF) Grabbing a screenshot with WPF

Jonathan.Peppers

Some more info on my options:

AllowsTransparency=True on both the parent window and the MessageBox makes my CPU usage go from 15% to 70-80% (which is unacceptable).

AllowsTransparency=True only on the MessageBox works except when the parent window has an animation playing (then I get some crazy flickering which is also unacceptable). This even happens if I stop the animation and then show the MessageBox for some reason.

*AllowsTransparency also can't be set after a window is shown, so I can't change back and forth

GDI screenshot works, but occasionally mispaints the whole window, I set SnapsToDevicePixels since it was the only thing I could think of that would cause this. I'd get I screenshot if I could but it happens randomly and is hard to explain what it looks like.

WPF screenshot is slow and blows up every other time. There is also a weird white line at the top, but I can probably do something about that.

So I need another option, or a way to make one of these work.

Thanks!





Re: Windows Presentation Foundation (WPF) Grabbing a screenshot with WPF

Jonathan.Peppers

Well, it seems about every time I post a problem I figure it out b/f someone posts an answer.

I figured out a way to do this without a new window at all! Here's what I came up with, which is also extremely fast:

I created a MessageBox class (derives from Canvas) with a static show method. This method creates a new MessageBox, adds it to my original window, and then starts a new message loop while the box is visible. An OK button click ends the loop, and hides the MessageBox. Since the MessageBox was initially a canvas, I put whichever buttons, transparent rectangles, labels, etc. I needed onto it.

So here's the magic:

DispatcherFrame frame = new DispatcherFrame();

Dispatcher.PushFrame(frame);

and the click event would call:

frame.Continue = false;