Managed DirectX in C# Forms

Started by
6 comments, last by benwalker 18 years ago
Hi, I'm trying to build a 3D viewer application based around WinForms - so that I can have user controls (eg camera presets, toolbars etc) around the edge of the main 3D window. My first stab was to have a full size winforms app that was the whole of the rendering area, with popup forms around the outside depending on what was clicked in the application menu. Although this has some success, I also get a load of problems: I can't add a toolbar or status bar since it never gets painted. Also, there are is some very weird behaviour when one of the popup forms overlaps the main 3D area (will add a screenshot if I can...). Also, tooltips and context menus failed in the popup windows... My second stab, just to try it, was to create a custom user control that contains all of the DirectX code. I then create a form with all the controls I need (I used a tab control down the left hand side), drag my DX control onto the form and all will work - right? erm... nope. If I ignore the DX control, my form behaves exactly as expected. Adding the DX control causes the tab ctrl and toolbar to disappear (I have to click in the vague area of each control to force it to paint), and the only thing that works is the Direct3D rendering. My DX controls are all setup with the AllPaintingInWMPaint, UserPaint and Opaque flags in the constructor. I've also tried various conbinations of control invalidation during each paint request, but to no avail Has anyone else used this method or had the same problems? If so, how do I fix it? I'm not trying to create a game or anything, so full screen rendering is not the aim here - purely user controllable visualisation. any thoughts or comments? benw
Advertisement
You can do this a number of ways.
I will try and explain the simplest way of doing this.

You create a new Winform project.
Drop a Panel control on to the form.
Write the needed code to get the Device going and pass the handle of the panel explicitly to the device constructor.
i.e.
device = new Device(..., Panel.Handle, ...);
Use whatever render loop you wish and wallah. This should work.

I hope this helps.
Take care.
benwalker,

Can you post any code? I've had tremendous success creating D3D "user controls" to add to forms, which I've used for various tools and editors. Never had a single problem with them coexisting on the same form as other Windows.Forms controls.

The only thing that I've noticed is that various hiccups can occur if you try to initialize the device on a control that hasn't been shown yet, but that's easily gotten around.

-Dave
I'd try to keep GUI and engine seperate. For my level-editor I used 2 projects, one for the form and it's controls, the other one for all engine related stuff and just pass a reference of one control to the engine, which takes care of rendering. It will keep your code clean and it will also save you from headaches if you later want to include more swapchains to render into multiple controls simultaneously.

If you want to see some code look into the blog of Michael Schuld (Link). You can download the whole stuff and see how he did it.
----------------------#include "signature.h"
finally got round to reading the replies and posting...

anyway. I've tried to do a simple test. Starting with a basic windows form, dragging on a menu, tab control (with two tabs) and a panel. Nothing fancy.

Then I do my initialisation, pass in panel1.Handle to the device constructor, add some rendering code, invalidate the form to force redrawing, override OnPaint to call the rendering code...

and despite being the worst 3D application you're likely to see, I still get problems viewing the tab controls and issues with the menu system.

I've included some similar source code this time...


using System;using System.Drawing;using System.Collections;using System.ComponentModel;using System.Windows.Forms;using System.Data;using Microsoft.DirectX;using Microsoft.DirectX.Direct3D;namespace FormsDx{	/// <summary>	/// Summary description for Form1.	/// </summary>	public class Form1 : System.Windows.Forms.Form	{		// GUI Items		private System.Windows.Forms.MainMenu mainMenu1;		private System.Windows.Forms.MenuItem menuItem1;		private System.Windows.Forms.MenuItem menuItem2;		private System.Windows.Forms.MenuItem menuItem3;		private System.Windows.Forms.TabControl tabControl1;		private System.Windows.Forms.TabPage tabPage1;		private System.Windows.Forms.TabPage tabPage2;		private System.Windows.Forms.Panel panel1;		// Standard MDX stuff		private Device dev = null;		private Mesh mesh = null;		private Matrix world, view, proj;		private float angle = 0f;		private bool drawing = false;		/// <summary>		/// Required designer variable.		/// </summary>		private System.ComponentModel.Container components = null;		public Form1()		{			InitializeComponent();//			this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);		}				/// <summary>		/// Graphics init code		/// </summary>		private void InitialiseGraphics()		{			PresentParameters p = new PresentParameters();			p.Windowed = true;			p.SwapEffect = SwapEffect.Discard;			p.EnableAutoDepthStencil = true;			p.AutoDepthStencilFormat = DepthFormat.D16;			dev = new Device(0, DeviceType.Hardware, panel1.Handle, 				CreateFlags.SoftwareVertexProcessing, p);			mesh = Mesh.Teapot(dev);			proj = Matrix.PerspectiveFovLH((float)Math.PI / 4, 				this.Width / this.Height, 1.0f, 100.0f);			view = Matrix.LookAtLH(new Vector3(2.0f,2.0f,2.0f), new Vector3(), 				new Vector3(0,1,0));		}		private void SetupCamera()		{			world = Matrix.Translation(0f,0f,0f) * Matrix.RotationY(angle);			dev.Transform.World = world;			dev.Transform.View = view;			dev.Transform.Projection = proj;						dev.RenderState.Lighting = false;			angle+= 0.05f;		}		private void Render()		{			if(!drawing) return;			SetupCamera();			dev.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Black, 1.0f, 0);			dev.BeginScene();			mesh.DrawSubset(0);			dev.EndScene(); dev.Present();			this.Invalidate();//			panel1.Invalidate();		}			/// <summary>		/// Clean up any resources being used.		/// </summary>		protected override void Dispose( bool disposing )		{			if( disposing )			{				if (components != null) 				{					components.Dispose();				}			}			base.Dispose( disposing );		}		#region Windows Form Designer generated code		/// <summary>		/// Required method for Designer support - do not modify		/// the contents of this method with the code editor.		/// </summary>		private void InitializeComponent()		{			this.mainMenu1 = new System.Windows.Forms.MainMenu();			this.menuItem1 = new System.Windows.Forms.MenuItem();			this.menuItem2 = new System.Windows.Forms.MenuItem();			this.menuItem3 = new System.Windows.Forms.MenuItem();			this.tabControl1 = new System.Windows.Forms.TabControl();			this.tabPage1 = new System.Windows.Forms.TabPage();			this.tabPage2 = new System.Windows.Forms.TabPage();			this.panel1 = new System.Windows.Forms.Panel();			this.tabControl1.SuspendLayout();			this.SuspendLayout();			// 			// mainMenu1			// 			this.mainMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {																											 this.menuItem1});			// 			// menuItem1			// 			this.menuItem1.Index = 0;			this.menuItem1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {																											 this.menuItem2,																											 this.menuItem3});			this.menuItem1.Text = "File";			// 			// menuItem2			// 			this.menuItem2.Index = 0;			this.menuItem2.Text = "Open";			// 			// menuItem3			// 			this.menuItem3.Index = 1;			this.menuItem3.Text = "Close";			// 			// tabControl1			// 			this.tabControl1.Controls.Add(this.tabPage1);			this.tabControl1.Controls.Add(this.tabPage2);			this.tabControl1.Location = new System.Drawing.Point(0, 0);			this.tabControl1.Name = "tabControl1";			this.tabControl1.SelectedIndex = 0;			this.tabControl1.Size = new System.Drawing.Size(152, 272);			this.tabControl1.TabIndex = 0;			// 			// tabPage1			// 			this.tabPage1.Location = new System.Drawing.Point(4, 22);			this.tabPage1.Name = "tabPage1";			this.tabPage1.Size = new System.Drawing.Size(144, 246);			this.tabPage1.TabIndex = 0;			this.tabPage1.Text = "Tab 1";			// 			// tabPage2			// 			this.tabPage2.Location = new System.Drawing.Point(4, 22);			this.tabPage2.Name = "tabPage2";			this.tabPage2.Size = new System.Drawing.Size(144, 246);			this.tabPage2.TabIndex = 1;			this.tabPage2.Text = "Tab 2";			// 			// panel1			// 			this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;			this.panel1.Location = new System.Drawing.Point(160, 24);			this.panel1.Name = "panel1";			this.panel1.Size = new System.Drawing.Size(224, 248);			this.panel1.TabIndex = 1;//			this.panel1.Paint += new System.Windows.Forms.PaintEventHandler(this.panel1_Paint);			// 			// Form1			// 			this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);			this.ClientSize = new System.Drawing.Size(384, 273);			this.Controls.Add(this.panel1);			this.Controls.Add(this.tabControl1);			this.Menu = this.mainMenu1;			this.Name = "Form1";			this.Text = "Form1";			this.tabControl1.ResumeLayout(false);			this.ResumeLayout(false);		}		#endregion		/// <summary>		/// The main entry point for the application.		/// </summary>		[STAThread]		static void Main() 		{			using(Form1 frm = new Form1())			{				frm.Show();				frm.InitialiseGraphics();				frm.drawing = true;				while(frm.Created)				{					Application.DoEvents();					//					ProcessEvents					//				Application.Run(frm);				}			}		}		protected override void OnPaint(PaintEventArgs e)		{			Render();		}//		private void panel1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)//		{//			//		}	}}


This is driving me nuts at the moment since it should be something so simple to do.... I don't really want to go down the MDX GUI based route either, so this is kind of bugging me...

any comments much appreciated.

thanks


benw
If you override OnPaint, the actual drawing it's supposed to be doing does happen. This means the application never draws anything unless you tell it to, and you're only drawing the DX stuff.

Try calling base.OnPaint() so the normal form rendering gets done as well.

Hope this helps.
Sirob Yes.» - status: Work-O-Rama.
I did try that and the same thing happens. Also tried various combinations of AlPaintingInWmPaint, Invalidate(), base.OnPaint(e) before and after the rendering call and all gives the same effect.

I also tride using the control's OnPaint event instead and ended up with everything drawn once but the 3d view never being updated.


benw
It seems to be a change in the behaviour from two different SDKs.

I put together two projects in an attemp to separate things out - one a UserControl that creates and acts as a DirectX device, the second a Forms application that uses the custom control dll and has some Tab controls on the side. This was the basis for an app that I wrote a while ago to try and see if it could form - consequently written using the Summer 2003 SDK.

I then tried porting it to August 2005 to mimic what I'm trying to do now - this is where the problems occur.

Summer 03 - All controls drawn correctly
Summer 2003 - controls are drawn correctly

August 05 - dead...
August 2005 - where did they go???

so do I go back to using Summer 03 and change my code, or stick with August 05 and try to find a workaround... Same code, same form, same settings....

help!


benw

This topic is closed to new replies.

Advertisement