XNA with Windows Forms - Opening new window - c#

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.

Related

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.

Hosting XNA inside WPF - Weird "Phantom" Window issue

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.

How to draw on an unfilled box on a video stream using the mouse

I am using dshownet(first time) and C#. I have got a sample to take the web cam input and display it on a form. I now need to draw a rectangle on top of the video stream using the mouse. (the intent is to track what is inside the box from there onwards).
I heard that there is something called VMR. So I went to the dshownet samples and went through them. I didnt find any samples that use the mouse to overlay a shape on the video stream. Someone on here suggested to use colorkey. Another person said to use GDI+ and mouse handling. I tried to compile the DXLogo sample but got this error :
Error 1 Cannot create an instance of the abstract class or interface 'System.Drawing.Image' C:\Documents and Settings\TLNA\Desktop\Final Year Project\Libraries\DirectShow library 2\DirectShowSamples-2010-February\Samples\Capture\DxLogo\Capture.cs 128 32 DxLogo-2008
for the code section:
if (fileName.Length > 0)
{
m_Bitmap = new Image(fileName); // error happened here
Rectangle r = new Rectangle(0, 0, m_Bitmap.Width, m_Bitmap.Height);
m_bmdLogo = m_Bitmap.LockBits(r, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
}
I know that I must go through the Bitmap9 Interface. But I really dont know where to start. Should I read the Directshow API docs.
btw I also have the book Programming Microsoft Directshow for digital video and television. I started reading that book and stopped after a few pages since the code is mainly in C++. Should I continue to read this is book ? How can I accomplish the certain mentioned tasks in C# ?
Any suggestions as how to draw on the video. Some useful links(tutorials) would be helpful.
Many Thanks
Tlna
I'm not sure why the DirectShow sample doesn't compile, but you may be able to change the problem line:
m_Bitmap = new Image(fileName);
to this:
m_Bitmap = new Bitmap(fileName);
and get it to work.
You're actually facing a pretty difficult problem here. DirectShow renders a video by drawing a series of still frames to a device context (like a PictureBox or a Form, or even the screen) many times a second (depending on whatever the frame rate is). You (as a programmer) can also (easily) render graphics directly to this same device context.
However, in order to make your drawn box appear over top of a running video, your code needs to draw the rectangle immediately after DirectShow draws each frame of the video; otherwise, the next frame will obliterate your rectangle. DirectShow objects probably have some sort of frame rendering event that you can handle, and then inside the event handler you just re-draw your box (based on the initial and current mouse coordinates, which you can get from the MouseDown and MouseMove events of whatever control you're drawing on).
Update: I just took a look at my code from when I was playing around with DirectShow.NET, and it looks like there is an event (DsEvCode.Repaint) that you could hook into and use to draw your box.
I haven't looked at the code sample you're working with, but do a search and see if you can find an OnGraphNotify() method in your code, you should be able to add something like this:
if (code == DsEvCode.Repaint)
{
// draw the box here
}
Presumably, this event is fired after each frame of the video is rendered, so if you redraw your box here every time it will appear as if the box is persisting.

Winforms: SuspendLayout/ResumeLayout is not enough?

I have a library of a few "custom controls". Essentially we have our own buttons, rounder corner panels, and a few groupboxes with some custom paint. Despite the "math" in the OnPaint methods, the controls are pretty standard. Most of the time, all we do is draw the rounded corners and add gradient to the background. We use GDI+ for all that.
These controls are ok (and very nice looking according to our customers), however and despite the DoubleBuffer, you can see some redrawing, especially when there are 20++ buttons (for example) on the same form. On form load you see the buttons drawing… which is annoying.
I'm pretty sure that our buttons are not the fastest thing on earth but my question is: if double buffer is "on", shouldn't all that redraw happen in background and the Windows subsystem should show the results "instantly" ?
On the other hand, if there's "complex" foreach loop that will create labels, add them to a panel (double buffered) and change their properties, if we suspendlayout of the panel before the loop and resume layout of the panel when the loop is over, shouldn't all these controls (labels and buttons) appear "almost instantly"? This doesn't happen like that, you can see the panel being filled.
Any idea why this is not happening? I know it's hard to evaluate without sample code but that's hard to replicate too. I could make a video with a camera, but trust me on this one, it's not fast :)
We've seen this problem too.
One way we've seen to "fix" it is to completely suspend drawing of the control until we're ready to go. To accomplish this, we send the WM_SETREDRAW message to the control:
// Note that WM_SetRedraw = 0XB
// Suspend drawing.
UnsafeSharedNativeMethods.SendMessage(handle, WindowMessages.WM_SETREDRAW, IntPtr.Zero, IntPtr.Zero);
...
// Resume drawing.
UnsafeSharedNativeMethods.SendMessage(handle, WindowMessages.WM_SETREDRAW, new IntPtr(1), IntPtr.Zero);
One of the things you should look at is whether you have set BackColor=Transparent on any of the child controls of your panels. The BackColor=Transparent will significantly degrade rendering performance especially if parent panels are using gradients.
Windows Forms does not use real transparency, rather it is uses "fake" one. Each child control paint call generates paint call on parent so parent can paint its background over which the child control paints its content so it appears transparent.
So if you have 50 child controls that will generate additional 50 paint calls on parent control for background painting. And since gradients are generally slower you will see performance degradation.
Hope this helps.
I'll approach your problem from a performance angle.
foreach loop that will create labels,
add them to a panel (double buffered)
and change their properties
If that's the order things are done, there's room for improvement. First create all your labels, change their properties, and when they are all ready, add them to the panel: Panel.Controls.AddRange(Control[])
Most of the time, all we do is draw
the rounded corners and add gradient
to the background
Are you doing the same thing over and over again? How are your gradients generated? Writing an image can't be that slow. I once had to create a 1680x1050 gradient in-memory, and it was really fast, like, too fast for Stopwatch, so drawing a gradient can't be so hard.
My advice would be to try and cache some stuff. Open Paint, draw your corners and save to disk, or generate an image in-memory just once. Then load (and resize) as needed. Same for the gradient.
Even if different buttons have different colors, but the same motif, you can create a bitmap with Paint or whatever and at runtime load it and multiply the Color values by another Color.
EDIT:
if we suspendlayout of the panel before the
loop and resume layout of the panel when the loop is over
That's not what SuspendLayout and ResumeLayout are for. They suspend the layout logic, that is, the automatic positioning of the controls. Most relevant with FlowLayoutPanel and TableLayoutPanel.
As for doublebuffering, I'm not sure it applies to custom draw code (haven't tried). I guess you should implement your own.
Doublebuffering in a nutshell:
It's very simple, a couple lines of code. On the paint event, render to a bitmap instead of rendering to the Graphics object, and then draw that bitmap to the Graphics object.
In addition to the DoubleBuffered property, also try adding this to your control's constructor:
SetStyle(ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint, true);
And if that ends up not being enough (which I'm gonna go out on a limb and say it isn't), consider having a look at my answer to this question and suspend/resume the redraw of the panel or Form. This would let your layout operations complete, then do all of the drawing once that's done.
You may want to look at the answer to my question, How do I suspend painting for a control and its children? for a better Suspend/Resume.
It sounds like what you are looking for is a "composited" display, where the entire application is drawn all at once, almost like one big bitmap. This is what happens with WPF applications, except the "chrome" surrounding the application (things like the title bar, resize handles and scrollbars).
Note that normally, unless you've messed with some of the window styles, each Windows Form control is responsible for painting itself. That is, every control gets a crack at the WM_ PAINT, WM_ NCPAINT, WM_ERASEBKGND, etc painting related messages and handles these message independently. What this means for you is that double buffering only applies to the single control you are dealing with. To get somewhat close to a clean, composited effect, you need to concern yourself not just with your custom controls that you are drawing, but also the container controls on which they are placed. For example, if you have a Form that contains a GroupBox which in turn contains a number of custom drawn buttons, each of these controls should have there DoubleBuffered property set to True. Note that this property is protected, so this means you either end up inheriting for the various controls (just to set the double buffering property) or you use reflection to set the protected property. Also, not all Windows Form controls respect the DoubleBuffered property, as internally some of them are just wrappers around the native "common" controls.
There is a way to set a composited flag if you are targeting Windows XP (and presumably later). There is the WS_ EX_ COMPOSITED window style. I have used it before to mix results. It doesn't work well with WPF/WinForm hybrid applications and also does not play well with the DataGridView control. If you go this route, be sure you do lots of testing on different machines because I've seen strange results. In the end, I abandoned used of this approach.
Maybe first draw on a control-only 'visible' (private) buffer and then render it:
In your control
BufferedGraphicsContext gfxManager;
BufferedGraphics gfxBuffer;
Graphics gfx;
A function to install graphics
private void InstallGFX(bool forceInstall)
{
if (forceInstall || gfxManager == null)
{
gfxManager = BufferedGraphicsManager.Current;
gfxBuffer = gfxManager.Allocate(this.CreateGraphics(), new Rectangle(0, 0, Width, Height));
gfx = gfxBuffer.Graphics;
}
}
In its paint method
protected override void OnPaint(PaintEventArgs e)
{
InstallGFX(false);
// .. use GFX to draw
gfxBuffer.Render(e.Graphics);
}
In its resize method
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
InstallGFX(true); // To reallocate drawing space of new size
}
The code above has been somewhat tested.
I had the same problem with a tablelayoutpanel when switching usercontrols that I wanted displayed.
I completely got rid of the flicker by creating a class that inherited the table, then enabled doublebuffering.
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
namespace myNameSpace.Forms.UserControls
{
public class TableLayoutPanelNoFlicker : TableLayoutPanel
{
public TableLayoutPanelNoFlicker()
{
this.DoubleBuffered = true;
}
}
}
I've had a lot of similar issues in the past, and the way I resolved it was to use a third-party UI suite (that is, DevExpress) rather than the standard Microsoft controls.
I started out using the Microsoft standard controls, but I found that I was constantly debugging issues which were caused by their controls. The problem is made worse by the fact that Microsoft generally does not fix any of the issues which are identified and they do very little to provide suitable workarounds.
I switched to DevExpress, and I have nothing but good things to say. The product is solid, they provide great support and documentation and yes they actually listen to their customers. Any time I had a question or an issue, I got a friendly response within 24 hours. In a couple of cases, I did find a bug and in both instances, they implemented a fix for the next service release.
I have seen bad winforms flicker on forms where the controls referred to a missing font.
This is probably not common, but it's worth looking into if you've tried everything else.

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