Sign in to follow this  
briantee

BeginInvoke within XNA

Recommended Posts

 public delegate void LineReceivedEvent(string sentence);

 //serial port receive in its own thread
 private void sp_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
 {
    if (sp.IsOpen)
    {
       string sentence = sp.ReadLine();
       this.BeginInvoke(new LineReceivedEvent(LineReceived), sentence);
    }
 }


private void LineReceived(string sentence)
{
   textBoxRcv.AppendText(sentence);
   textBoxRcv.AppendText("\n");
}

I have this SerialPort code that works perfectly in a Windows.Form but not in XNA. I am reading gps data from a serial port, a single sentence, then want to use that data in the XNA game to plot a 3D path.

 

The error is BeginInvoke does not exist in the current context. I can of course get around this by using a ReadExisting in the update part of XNA without events or threaded but would really prefer the serial reading done in its own thread.

 

I made sure all references and "using's" were the same as the Windows.Form application.

 

I am using a Forms.Panel in the XNA window for buttons and the textbox. Everything works fine as long as i don't use BeginInvoke and the Serial Data Event.

 

Any suggestions?

 

Here is the code in its entirety:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using ButtonState = Microsoft.Xna.Framework.Input.ButtonState;
using Point = System.Drawing.Point;
using System.IO.Ports;
//using System.Threading;

namespace gpsPlot
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class GPSMain : Microsoft.Xna.Framework.Game
    {        
        //Global Properties
        GraphicsDeviceManager graphics;
        Viewport vp;
        
        SpriteBatch spriteBatch;
        private Texture2D img;

        //panel objects and controls
        Panel pnl;   

        Button btnExit;
        Button btnOpenSerial;
        Button btnCloseSerial;

        ComboBox cboxPort;
        ComboBox cboxBaud;

        TextBox textBoxRcv;

        //serial related
        static string portName = "COM7";
        static int baudRate = 4800;

        SerialPort sp = new SerialPort(portName, baudRate, Parity.None, 8, StopBits.One);

        public delegate void LineReceivedEvent(string sentence);

        //serial port receive in its own thread
        private void sp_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            if (sp.IsOpen)
            {
                try
                {
                    string sentence = sp.ReadLine();
                    this.BeginInvoke(new LineReceivedEvent(LineReceived), sentence);
                }

                //this is bad programming, it just ignores errors until its hooked up again.
                catch (Exception) { }
            }

        }


        private void btnOpenSerial_Click(object sender, EventArgs e)
        {
            if (!sp.IsOpen)
            {
                sp.PortName = portName;

                sp.BaudRate = baudRate;

                sp.DataReceived += sp_DataReceived;
            }


            try { sp.Open(); }
            catch (Exception exc)
            {
                MessageBox.Show(exc.Message, "Not Connected to GPS");
            }

            if (sp.IsOpen)
            {
                //sp.ReadTimeout = 200;

                btnOpenSerial.Enabled = true;
                btnCloseSerial.Enabled = true;

                //update port status labels
                //this.labelPortStatus.Text = "Open";
                //this.labelPortName.Text = portName;
                //this.labelBaudRate.Text = Convert.ToString(baudRate);

                //discard any stuff in the buffers
                sp.DiscardOutBuffer();
                sp.DiscardInBuffer();

                if (!sp.IsOpen) return;
            }
        }


        private void btnCloseSerial_Click(object sender, EventArgs e)
        {
            try { sp.Close(); }
            catch (Exception exc)
            {
                MessageBox.Show(exc.Message, "Connection already terminated. Unplugged or shut off?");
            }

            btnOpenSerial.Enabled = true;
            btnCloseSerial.Enabled = false;
            //this.labelPortStatus.Text = "Closed";
            //this.labelPortName.Text = "-";
            //this.labelBaudRate.Text = "-";
            //Application.Exit();
        }

        private void LineReceived(string sentence)
        {
            //no matter what the line is, display it
            textBoxRcv.AppendText(sentence);
            textBoxRcv.AppendText("\n");
        }
        private void cboxPort_SelectedIndexChanged(object sender, EventArgs e)
        {

        }

        private void cboxBaud_SelectedIndexChanged(object sender, EventArgs e)
        {

        }


        public GPSMain()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.PreferredBackBufferWidth = 1400;
            graphics.PreferredBackBufferHeight = 768;

            Content.RootDirectory = "Content";

            // allow the mouse to remain visible and allow user resizing ofthe window
            this.IsMouseVisible = true;
            this.Window.AllowUserResizing = true;
            
            // create and setup the winform controls on the game window
            this.CreateControls();

            // hook into the client size change event so we can update the win form
            // controls and update the viewport on the graphics device
            this.Window.ClientSizeChanged += this.Window_ClientSizeChanged;

            //add available ports in port name combobox
            foreach (String s in System.IO.Ports.SerialPort.GetPortNames())
            {
                cboxPort.Items.Add(s);
            }
        }

 
        private void CreateControls()
        {
            // get game window as a win form Form class
            Form frm = Control.FromHandle(this.Window.Handle) as Form;
            
            // setup the panel control---------------------------------
            this.pnl = new Panel();
            this.pnl.Dock = DockStyle.Right;
            this.pnl.Width = 250;

            //Exit button-----------------------------------------------
            this.btnExit = new Button();
            this.btnExit.Location = new System.Drawing.Point(12, 12);
            this.btnExit.Name = "btnExit";
            this.btnExit.Size = new System.Drawing.Size(75, 23);
            this.btnExit.TabIndex = 0;
            this.btnExit.Text = "Exit";
            this.btnExit.UseVisualStyleBackColor = true;
            this.btnExit.Click += new System.EventHandler(this.btnExit_Click); 
            this.pnl.Controls.Add(btnExit);// add the button to the panel

            //Open Serial button----------------------------------------
            this.btnOpenSerial = new Button();
            this.btnOpenSerial.Location = new System.Drawing.Point(122, 146);
            this.btnOpenSerial.Name = "btnOpenSerial";
            this.btnOpenSerial.Size = new System.Drawing.Size(75, 23);
            this.btnOpenSerial.TabIndex = 0;
            this.btnOpenSerial.Text = "Open Port";
            this.btnOpenSerial.UseVisualStyleBackColor = true;
            this.btnOpenSerial.Click += new System.EventHandler(this.btnOpenSerial_Click);
            this.pnl.Controls.Add(btnOpenSerial);

            // 
            // btnCloseSerial--------------------------------------------------
            // 
            this.btnCloseSerial = new Button();
            this.btnCloseSerial.Location = new System.Drawing.Point(122, 185);
            this.btnCloseSerial.Name = "btnCloseSerial";
            this.btnCloseSerial.Size = new System.Drawing.Size(75, 23);
            this.btnCloseSerial.TabIndex = 1;
            this.btnCloseSerial.Text = "Close Serial";
            this.btnCloseSerial.UseVisualStyleBackColor = true;
            this.btnCloseSerial.Click += new System.EventHandler(this.btnCloseSerial_Click);
            this.pnl.Controls.Add(btnCloseSerial);

            // cboxPort---------------------------------------------------------
            // 
            this.cboxPort = new ComboBox();
            this.cboxPort.FormattingEnabled = true;
            this.cboxPort.Location = new System.Drawing.Point(16, 146);
            this.cboxPort.Name = "cboxPort";
            this.cboxPort.Size = new System.Drawing.Size(71, 21);
            this.cboxPort.TabIndex = 3;
            this.cboxPort.Text = "Port";
            this.cboxPort.SelectedIndexChanged += new System.EventHandler(this.cboxPort_SelectedIndexChanged);
            this.pnl.Controls.Add(cboxPort);
            // 
            // cboxBaud------------------------------------------------------------
            //
            this.cboxBaud = new ComboBox();
            this.cboxBaud.FormattingEnabled = true;
            this.cboxBaud.Items.AddRange(new object[] {
            "4800",
            "9600",
            "19200",
            "38400",
            "57600",
            "115200"});
            this.cboxBaud.Location = new System.Drawing.Point(17, 185);
            this.cboxBaud.Name = "cboxBaud";
            this.cboxBaud.Size = new System.Drawing.Size(70, 21);
            this.cboxBaud.TabIndex = 4;
            this.cboxBaud.Text = "Baud";
            this.pnl.Controls.Add(cboxBaud);
            this.cboxBaud.SelectedIndexChanged += new System.EventHandler(this.cboxBaud_SelectedIndexChanged);

            // textBoxRcv---------------------------------------------------------------
            // 
            this.textBoxRcv = new TextBox();
            this.textBoxRcv.BackColor = System.Drawing.SystemColors.ButtonHighlight;
            this.textBoxRcv.Location = new System.Drawing.Point(3, 43);
            this.textBoxRcv.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
            this.textBoxRcv.Multiline = true;
            this.textBoxRcv.Name = "textBoxRcv";
            this.textBoxRcv.ReadOnly = true;
            this.textBoxRcv.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
            this.textBoxRcv.Size = new System.Drawing.Size(230, 95);
            this.textBoxRcv.TabIndex = 17;
            this.pnl.Controls.Add(textBoxRcv);
 


            //add the panel to the game window form
            frm.Controls.Add(this.pnl);

        }

        //Event Handlers
        private void btnExit_Click(object sender, EventArgs e)
        {
            this.Exit();

        }

 
 
       void Window_ClientSizeChanged(object sender, EventArgs e)
        {
            // get the viewport from the graphics device
            vp = this.GraphicsDevice.Viewport;
            // change the viewport dimensions so that it is not drawn under any of our winform controls
            vp.Width = this.Window.ClientBounds.Width - pnl.Width;
            vp.Height = this.Window.ClientBounds.Height;
            // set the viewport back onto the graphics device
            this.GraphicsDevice.Viewport = vp;
            // update the label to display the rectangle info
            Rectangle rect = new Rectangle(vp.X, vp.Y, vp.Width, vp.Height);
            //lbl.Text = "Client: " + this.Window.ClientBounds.ToString() + "\r\n" +
            //           "Viewport: " + rect.ToString();

            this.Window.Title = "GPS Plotter";
        }


        protected override void Initialize()
        {
            this.Window_ClientSizeChanged(this, EventArgs.Empty);

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // load a sample image that will be drawn the size of the viewport
            this.img = this.Content.Load<Texture2D>("Map2");

        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        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);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // draw the image the same size of the viewport
            this.spriteBatch.Begin();
            //var vp = this.GraphicsDevice.Viewport;
            Rectangle rect = new Rectangle(vp.X, vp.Y, vp.Width, vp.Height);
            Rectangle srcrect = new Rectangle(0, 0, this.img.Width, this.img.Height);
            this.spriteBatch.Draw(this.img, rect, srcrect, Color.White);
            this.spriteBatch.End();


            base.Draw(gameTime);
        }
    }
}

Edited by briantee

Share this post


Link to post
Share on other sites

If I'm understanding right...you're talking about a compile error?

 

Microsoft.Xna.Framework.Game doesn't have a function called BeginInvoke. Presumably your sp_DataReceived was declared in a type that extended a Form or some Control before you ported the code over to an XNA game. This is where MSDN documentation is your friend, as BeginInvoke is a method declared in System.Windows.Forms.Control.

 

You'll get an error if you try and modify state on a WinForm control from a thread that is not the UI thread (the thread that the controls are being run on). BeginInvoke allows you to send a message to the UI thread and perform an action that will update the UI at a later point in time. So call BeginInvoke on one of your WinForm controls and it should compile.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this