Hosting XNA inside WPF - Weird "Phantom" Window issue - c#

I'm trying to host an XNA game inside a WPF window using the Windows Forms host control. I've got a weird problem that a "Phantom" window is created when I run the game. It is created exactly at the first call to game's Update method exits.
Here is the Update code, the default one from a new XNA project:
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
// TODO: Add your update logic here
base.Update(gameTime);
}
The window is created after I step from the last curly brace at the very bottom. (yes, weird to have it NOT in base.Update but the after the } after it.)
I have a Windows Forms host as I've said, with the code below:
The relevant XAML is here (I've obviously got the Forms namespace etc. set up so no need to paste here):
<WindowsFormsHost Margin="12" Name="windowsFormsHost1">
<forms:Panel x:Name="p"></forms:Panel>
</WindowsFormsHost>
and in codebehind:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Viewer v = new Viewer(p.Handle);
v.Run();
}
where the Viewer is my Game class (from XNA), with:
public Viewer(IntPtr handle)
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
panelHandle = handle;
graphics.PreparingDeviceSettings += new EventHandler<PreparingDeviceSettingsEventArgs>(graphics_PreparingDeviceSettings);
}
void graphics_PreparingDeviceSettings(object sender, PreparingDeviceSettingsEventArgs e)
{
e.GraphicsDeviceInformation.PresentationParameters.DeviceWindowHandle = panelHandle;
}
This technique is from a blog post I've found a few days ago (I can't find it again now, it is EXACTLY the same way -- with the exception that the post was old and doesn't have the issue I'm having).
The problem is that, I build and run, but along with my "normal" window, there is a phantom window created. In my main window, XNA renders correctly. But the phantom window (the Window title is my assembly's name), doesn't have anything in it, is not resizable, cursor doesn't render inside it, and it acts like the main window in the sense that when I minimize this window, my main window stops rendering the XNA content until I un-minimize that phantom window. My program doesn't quit until I close BOTH windows. (If I close the phantom only, my main window, as you guess, stops rendering my XNA content). I tried iterating over my application windows, with 'App.Current.Windows', and all I've got is my main window listed there, that's why I'm calling that semi-responsive window the "phantom" window. It is not visible in my object model in WPF.
When I wrote "XNA WPF phantom window", the first I've got was this: WPF: How to determine the origin of a phantom window?
So I went and tried the Snoop. But Snoop probably also relies on the Windows iteration, and it also doesn't see any extra window there. I use the crosshair-drag feature (you drag the cursor around the screen and Snoop tells which process and window it belongs to), and Snoop tells that the Phantom Window is actually my MainWindow. But my MainWindow is ALSO the same window, according to Snoop. So this phantom is somewhat closely related (or maybe a "child"?) of my MainWindow instance, but I need a way to close it (or at least, hide it).
Any ideas?

The two good ways of embedding XNA in WPF that I know of are in this post (one is described in the post itself, the other is described in the first link in the post):
http://blogs.msdn.com/b/nicgrave/archive/2011/03/25/wpf-hosting-for-xna-game-studio-4-0.aspx
Both have code samples and I've used the WriteableBitmap version before with good results. (I was creating a development tool that didn't need to show anything larger than 384x384 so FPS was acceptable; if you need high FPS with a large back buffer (e.g. 800x600, 720p, etc.) you'll definitely want to use the HwndHost method).
I'd recommend trying whichever one of those two best fits your needs. When you try to use the window XNA creates, XNA's WinForms window expects to be a top level window. I've tried messing around with solutions such as what you've posted in the past but have never gotten beyond the phantom window issue.

I wrote this and finished it just now, maybe this can help you. I wrote it for the express purpose of XNA <-> WPF Compatibility; that is, having XNA be able to render inside a specific Control on WPF. Give it a shot:
https://xnaml.codeplex.com/

Besides being just a workaround, I think we are stuck with this being the best until something new comes in..
After further investigation, I've found out that the phantom window is the "window handle" of the Game instance, and it is required for the game to keep running. It could be accessed as the game's window handle, and creating (actually getting reference to an existing) a form from it. The game also prevented the input mechanisms of the objects such as WPF TextBox, and I got a very inconvenient (but works for me now) workaround by deriving the TextBox class and overriding the OnKeyDown manually (it caught the even but weirdly wasn't putting in text input.) and raising a TextInput event manually. A bit hacky, but works. And with the form, I can just set the opacity to zero and move it somewhere not in the middle, and it will stay there.
But my solution is just a hacky workaround and Mike's solution is definitely better, so if you are reading this and just about to start off a new project like these, follow his answer.

Related

Grab screen pixel data "behind" the Windows 10 magnifier tool

I was wondering if it would be possible to capture the whole view of the screen while the windows magnifier tool is running in fullscreen mode. Essentially, what I am looking for is while I'm looking with my eyes at a certain point on the screen, and only that certain point of the screen is shown on my monitor, I want to have the ability to printscreen the entirety of the original screen without closing out of magnifier. No clue how to word it better and clearer than that. I know it's possible to get screen pixels info through the Graphics class like so:
this.graphics.Clear(Color.Black);
this.graphics.CopyFromScreen(pictureBoxPoint.X, pictureBoxPoint.Y, 0, 0, size);
But this does not exclude the magnifier's influence on the screen, and only grabs it as it's seen by the user. What I would ideally like to be able to do is grab the entirety of the screen as it's supposed to be seen if the magnifier tool wasn't zooming in only on one part, if that makes sense. The entire screenspace even if only part is visible.
I had a quick look at the Magnification API docs page, but I have no idea if that is actually where I should be looking. Is there something else I can do? I'd appreciate it if someone pointed me in the right direction because I seem to be going in circles.
Thanks a lot!
Have a look at the github project karna-magnification, this will allow you to make a windows form object into the view portal and magnify anything under the cursor. To capture the magnification, you just need to capture the contents of the form.
Once you reference the github project, in the constructor for the form you want to use as the magnifier view panel, Add
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Magnifier mg = new Magnifier(this);
}
}
}
from there add code to use the graphics context of the form to capture the image. That part I will leave to you.

How to move objects in winForms to create animation [duplicate]

I need to knock out a quick animation in C#/Windows Forms for a Halloween display. Just some 2D shapes moving about on a solid background. Since this is just a quick one-off project I really don't want to install and learn an entire new set of tools for this. (DirectX dev kits, Silverlight, Flash, etc..) I also have to install this on multiple computers so anything beyond the basic .Net framework (2.0) would be a pain in the arse.
For tools I've got VS2k8, 25 years of development experience, a wheelbarrow, holocaust cloak, and about 2 days to knock this out. I haven't done animation since using assembler on my Atari 130XE (hooray for page flipping and player/missile graphics!)
Advice? Here's some of the things I'd like to know:
I can draw on any empty widget (like a panel) by fiddling with it's OnPaint handler, right? That's how I'd draw a custom widget. Is there a better technique than this?
Is there a page-flipping technique for this kind of thing in Windows Forms? I'm not looking for a high frame rate, just as little flicker/drawing as necessary.
Thanks.
Post Mortem Edit ... "a couple of coding days later"
Well, the project is done. The links below came in handy although a couple of them were 404. (I wish SO would allow more than one reply to be marked "correct"). The biggest problem I had to overcome was flickering, and a persistent bug when I tried to draw on the form directly.
Using the OnPaint event for the Form: bad idea. I never got that to work; lots of mysterious errors (stack overflows, or ArgumentNullExceptions). I wound up using a panel sized to fill the form and that worked fine.
Using the OnPaint method is slow anyway. Somewhere online I read that building the PaintEventArgs was slow, and they weren't kidding. Lots of flickering went away when I abandoned this. Skip the OnPaint/Invalidate() and just paint it yourself.
Setting all of the "double buffering" options on the form still left some flicker that had to be fixed. (And I found conflicting docs that said "set them on the control" and "set them on the form". Well controls don't have a .SetStyle() method.) I haven't tested without them, so they might be doing something (this is the form):
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
So the workhorse of the code wound up looking like (pf is the panel control):
void PaintPlayField()
{
Bitmap bufl = new Bitmap(pf.Width, pf.Height);
using (Graphics g = Graphics.FromImage(bufl))
{
g.FillRectangle(Brushes.Black, new Rectangle(0, 0, pf.Width, pf.Height));
DrawItems(g);
DrawMoreItems(g);
pf.CreateGraphics().DrawImageUnscaled(bufl, 0, 0);
}
}
And I just called PaintPlayField from the inside of my Timer loop. No flicker at all.
Set off a timer at your desired frame rate. At each timer firing twiddle the internal representation of the shapes on the screen (your model) per the animation motion you want to achieve, then call Invalidate(true). Inside the OnPaint just draw the model on the screen.
Oh yeah, and you probably want to turn Double Buffering on (this is like automatic page flipping).
2d Game Primer
Timer Based Animation
Both of these give good examples of animation. The code is fairly straightforward. i used these when I needed to do a quick animation for my son.

XNA with Windows Forms - Opening new window

Okay, so here we go. I'm attempting to make an application, using XNA as the base because of its renderer. One of the things that is necessary in this project is to open a new window (as a dialog), in which is embedded a separate XNA render panel. I'm using this as an interactive preview panel, so I absolutely need XNA to render in there. However, it seems XNA is not very well equipped to do this. I have tried various things myself, but to no avail (either producing errors and not rendering correctly, or rendering in the wrong aspect ratio, etc.). Normally, I would post code here, but since I have had such little luck, there isn't much to post.
My application currently consists of an XNA application embedded within a Form, and I have a button to open the preview panel, which in theory should pop up as a Form Dialog, containing the XNA renderer, to allow me to draw the preview. I have been trying this for several hours, and got nowhere, so I'm asking for a bit of help here.
Thanks, anyway.
EDIT: Okay, I've made a little progress, but I have 2 problems. Firstly, any textures drawn with a sprite batch appear the right dimensions, but filled with solid black. Also, when I open the dialog, and then close it, and close the application, I get an AccessViolationException. I strongly suspect the two errors are linked in some way.
Here is my code initialising the preview dialog. (a is a custom class that essentially consists of a LinkedList of Texture2D objects).
animPrev = new AnimationPreview(a);
animPrev.Show();
My AnimationPreview class is an extension of the Form class, and contains a PreviewControl object, which is an extension of the GraphicsDeviceControl found in the XNA Winforms sample. Note that my main form extends the XNA Game class, for various reasons.
The PreviewControl object is set up like this:
protected override void Initialize()
{
sb = new SpriteBatch(GraphicsDevice);
Application.Idle += delegate { Invalidate(); };
}
And the Draw method contains:
protected override void Draw()
{
GraphicsDevice.Clear(Microsoft.Xna.Framework.Graphics.Color.Violet);
if (frame != null)
{
sb.Begin();
sb.Draw(Image, Vector2.Zero, Color.White);
sb.End();
}
}
This clears the background of the form violet, as expected, and draws a black box of the same size as Image. This is not expected. Hopefully someone can help me out here.
NOTE: An acceptable alternative would be to convert XNA Texture2D objects to System.Drawing.Image objects. However, I am using XNA 3.1, so I can't just save the texture to a stream and reload it.
Actually, after having tried this, it's a bit dodgy, and very slow, so I'd rather not do it this way.
Did you take a look at the following official tutorials / samples?
XNA WinForms Series 1: Graphics Device
XNA WinForms Series 2: Content Loading
They should explain everything in my opinion. You even find downloadable source for the samples.

How to effectively draw on desktop in C#?

I want to draw directly on the desktop in C#. From searching a bit, I ended up using a Graphics object from the Desktop HDC (null). Then, I painted normally using this Graphics object.
The problem is that my shapes get lost when any part of the screen is redrawn. I tried a While loop, but it actually ends up drawing as fast as the application can, which is not the update rate of the desktop.
Normally, I would need to put my drawing code in a "OnPaint" event, but such thing does not exist for the desktop.
How would I do it?
Example code: https://stackoverflow.com/questions/1536141/how-to-draw-directly-on-the-windows-desktop-c
I posted two solutions for a similar requirement here
Basically you have two options.
1- Get a graphics object for the desktop and start drawing to it. The problem is if you need to start clearing what you have previously drawn etc.
Point pt = Cursor.Position; // Get the mouse cursor in screen coordinates
using (Graphics g = Graphics.FromHwnd(IntPtr.Zero))
{
g.DrawEllipse(Pens.Black, pt.X - 10, pt.Y - 10, 20, 20);
}
2- The second option that I provide in the link above is to create a transparent top-most window and do all your drawing in that window. This basically provides a transparent overlay for the desktop which you can draw on. One possible downside to this, as I mention in the original answer, is that other windows which are also top-most and are created after your app starts will obscure your top most window. This can be solved if it is a problem though.
For option 2, making the form transparent is as simple as using a transparency key, this allows mouse clicks etc. to fall through to the underlying desktop.
BackColor = Color.LightGreen;
TransparencyKey = Color.LightGreen;
When you draw to HDC(NULL) you draw to the screen, in an unmanaged way. As you've discovered, as soon as windows refreshes that part of the screen, your changes are overwritten.
There are a couple of options, depending upon what you want to achieve:
create a borderless, possibly
non-rectangular window. (Use
SetWindowRgn to make a window
non-rectangular.) You can make this a child of the desktop window.
subclass the desktop window. This is not straightforward, and involves
injecting a DLL into the
Explorer.exe process.
To get an OnPaint for the desktop you would need to subclass the desktop window and use your drawing logic when it receives a WM_PAINT/WM_ERASEBKGND message.
As the thread you linked to says, you can only intercept messages sent to a window of an external process using a hook (SetWindowsHookEx from a DLL).
As mentioned a transparent window is another way to do it, or (depending on the update frequency) copying, drawing and setting a temporary wallpaper (as bginfo does).
This is difficult to do correctly.
It will be far easier, and more reliable, to make your own borderless form instead.

Simple animation using C#/Windows Forms

I need to knock out a quick animation in C#/Windows Forms for a Halloween display. Just some 2D shapes moving about on a solid background. Since this is just a quick one-off project I really don't want to install and learn an entire new set of tools for this. (DirectX dev kits, Silverlight, Flash, etc..) I also have to install this on multiple computers so anything beyond the basic .Net framework (2.0) would be a pain in the arse.
For tools I've got VS2k8, 25 years of development experience, a wheelbarrow, holocaust cloak, and about 2 days to knock this out. I haven't done animation since using assembler on my Atari 130XE (hooray for page flipping and player/missile graphics!)
Advice? Here's some of the things I'd like to know:
I can draw on any empty widget (like a panel) by fiddling with it's OnPaint handler, right? That's how I'd draw a custom widget. Is there a better technique than this?
Is there a page-flipping technique for this kind of thing in Windows Forms? I'm not looking for a high frame rate, just as little flicker/drawing as necessary.
Thanks.
Post Mortem Edit ... "a couple of coding days later"
Well, the project is done. The links below came in handy although a couple of them were 404. (I wish SO would allow more than one reply to be marked "correct"). The biggest problem I had to overcome was flickering, and a persistent bug when I tried to draw on the form directly.
Using the OnPaint event for the Form: bad idea. I never got that to work; lots of mysterious errors (stack overflows, or ArgumentNullExceptions). I wound up using a panel sized to fill the form and that worked fine.
Using the OnPaint method is slow anyway. Somewhere online I read that building the PaintEventArgs was slow, and they weren't kidding. Lots of flickering went away when I abandoned this. Skip the OnPaint/Invalidate() and just paint it yourself.
Setting all of the "double buffering" options on the form still left some flicker that had to be fixed. (And I found conflicting docs that said "set them on the control" and "set them on the form". Well controls don't have a .SetStyle() method.) I haven't tested without them, so they might be doing something (this is the form):
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
So the workhorse of the code wound up looking like (pf is the panel control):
void PaintPlayField()
{
Bitmap bufl = new Bitmap(pf.Width, pf.Height);
using (Graphics g = Graphics.FromImage(bufl))
{
g.FillRectangle(Brushes.Black, new Rectangle(0, 0, pf.Width, pf.Height));
DrawItems(g);
DrawMoreItems(g);
pf.CreateGraphics().DrawImageUnscaled(bufl, 0, 0);
}
}
And I just called PaintPlayField from the inside of my Timer loop. No flicker at all.
Set off a timer at your desired frame rate. At each timer firing twiddle the internal representation of the shapes on the screen (your model) per the animation motion you want to achieve, then call Invalidate(true). Inside the OnPaint just draw the model on the screen.
Oh yeah, and you probably want to turn Double Buffering on (this is like automatic page flipping).
2d Game Primer
Timer Based Animation
Both of these give good examples of animation. The code is fairly straightforward. i used these when I needed to do a quick animation for my son.

Categories

Resources