Click here to Skip to main content
15,073,273 members
Articles / Programming Languages / C#
Article
Posted 29 Jan 2015

Tagged as

Stats

111.8K views
9.8K downloads
117 bookmarked

Windows Form Design at Run Time

Rate me:
Please Sign up or sign in to vote.
4.73/5 (63 votes)
29 Jan 2015CPOL8 min read
Panel has been used as a Form where user can Add, Move controls, user can add text, items, colors, etc. using Property Window.Bind data to DatagridView, ListView, ComboBox from DataTable, XML File and from SQL Server database.

Image 1

Introduction

Windows Form Design at Run Time will allow users to Design Form at run time, save the form, open and reuse the existing Form. Users can add controls at Runtime from the Toolbar, design their Form, write their code for controls to perform some Action. Now, for example, user can add a DataGridView and a Button Control at Run time. In Code Part Text Box, users can add their code to Bind data from Database to the Selected Data Grid. Everything is done at Runtime.

This Windows Form Design at Runtime will be useful for:

  1. Project Manager/System Analyst where they can design there for new project and hand over to Developer Programmer and Software Eng.
  2. Project demo to Client. In some cases, Clients needs on spot Form design and sample output of form looks. In that case, this application will be very useful to design their form and show for demo within less time.
  3. Teaching and training new programmer or developers. Trainer or teacher can explain about basic Windows form controls, property of each control and how to write simple code to new programmers.

The Windows Form Design at Run Time software application was developed to design your own Form using panel Control to add Label, Button, TextBox, ListBox, DataGridView, ListView, CheckBox, etc. The user can Add/Change the background Color/Image of Form (here, we use the Panel). User can add items to ComboBox, TreeView, listView, Textbox using the Property Window. Users can enter their own C# code at textbox at the bottom to run their code at Runtime, for example, Add a Button and in the Textbox, add your code and click the Button to see the Runtime Button Click event. Here in this program, I have added a simple MessageBox display while clicking the Button. I have a plan to extend this function to bind data from XML, database Datagridview. Using this application, user can bind data from DataTable, XML File and from SQL data source. This simple C# application allows the user to add:

  1. Create New Form
  2. Save Form as XML file
  3. Open Form from XML file
  4. Cut, Copy, and Paste all controls
  5. Delete All Control
  6. Delete Selected Control
  7. Add/Change Back Color for Form (Here, Panel used as Form)
  8. Add/Change BackGround Image for Form
  9. Add Label Control and using Property window design your Label
  10. Add Button Control and using Property window design your Button
  11. Add CheckBox Control and using Property window design your CheckBox..
  12. Add Button Control and using Property window design your Button
  13. Add ComboBox Control and using Property window design your ComboBox
  14. Add DataGridView Control and using Property window design your DataGridView
  15. Add DataTimePicker Control and using Property window design your DataTimePicker
  16. Add ListBox Control and using Property window design your ListBox
  17. Add ListView Control and using Property window design your ListView
  18. Add NumericUpDown Control and using Property window design your NumericUpDown
  19. Add PictureBox Control and using Property window design your PictureBox
  20. Add RadioButton Control and using Property window design your RadioButton
  21. Add TextBox Control and using Property window design your TextBox
  22. Add TreeView Control and using Property window design your TreeView
  23. Bind Data from as DataTable return to DataGridView, ListView, ComboBox and ListBox
  24. Bind Data From XML File as DataTable return to DataGridView, ListView, ComboBox and ListBox
  25. Bind Data From SQL Server Data base as DataTable return to DataGridView, ListView, ComboBox and ListBox

Application Layout

In this Application, we can see Toolbar at the Left which is used to add controls to Form (here our Panel). Center has a Form(Panel) where users can add and design their controls. At the Right, it has Property window which is used to add all design to selected controls. At the Bottom, it has TextBox for the Code Part where user can write there own code for Run time generated Button Click events.

Toolbar

Here, we can see all the list of controls which can be added to form at Runtime. New Features have been added in Version 1.2. New features like Create New Form, Save Form, Open Form, Cut, Copy and Paste Controls.

Image 2

Form (as Panel)

Here, we can see the Form(Panel) at center where users can add their control and we can see the Message Box after Button Click.

Image 3

Property Window

At the right, we have the Property window which will be useful for users to design their controls.

Image 4

Code Part

At the bottom, we can see the code part where users can write their own code. It has two parts as first Panel contains "Select Control To Bind" ComboBox. Whenever user adds a control to the Form(Panel), the control names will be added to this Combobox. Here, we can see now "grid_20" which is the name of the DatagridView which is added to the Form.

In "Code Type", user can select the Button Click event Type as "MessageBox", "Dat Tablereturn", "XML Data table Return, "SQL Data Base to Data Table return" and next, we have a Textbox where the user can enter their code here. Below, you can see code to create a Datatable and add rows with data to the Datatable and finally return the DataTable.

Image 5

Here, we can the "Select Control To Bind" ComboBox. Here, we can see list of controls which have been added to our Form as Grid, Label, Button, ListView, etc. I have used Control names with Control type prefix 3 char and Random no.

Image 6

Message Box

Here, we can see a Sample of how to display Message Box at Runtime from Button Click.

Image 7

Bind Controls from DataTable and XML File

Here, we can see controls like Label, ListBox, ComboBox, DatagridView, ListView and Button have been added. We should be careful in selecting the controls which need to be bound and we can select our function type as "Dat Table return".

Note: Only in Button Click, we can bind the Controls. So select your Control to bind, for example, combobox or ListBox or as you wish then in Textbox, enter your code to return the datatable which can be bound to your selected control.

Image 8

Here is the list of sample code which will be used to bind the data to the selected control. Just copy this code and paste in the TextBox of Code Part.

C#
//--for  return Datatable to Combobox/ListBox
DataTable dt = new DataTable(); 
dt.Columns.Add("itemCode"); 
dt.Columns.Add("ItemName"); 
DataRow row = dt.NewRow();
 row["itemCode"] = "0001"; 
row["ItemName"] = "SHANU";
 dt.Rows.Add(row);  
row = dt.NewRow();
 row["itemCode"] = "0002";
 row["ItemName"] = "Afraz"; 
dt.Rows.Add(row);  
row = dt.NewRow(); 
row["itemCode"] = "0003";
 row["ItemName"] = "Afreen";
 dt.Rows.Add(row); 
return dt;
C#
//-----------------  For return Datatable to from XML FIle
//You can find the "ShanuDataSource.xml" XML file at my bin folder of Zip file.
//If you want to load your XML File Place it in the same Bin Folder.
  DataSet dataSet = new DataSet();
 dataSet.ReadXml("ShanuDataSource.xml"); 
 return dataSet.Tables[0];
C#
// -----------------  From SQL SERVER Data Base to Data Table.
 string constring = @"Data Source=YourServerName;
                    Initial Catalog=YourDBName;User id = sa;password=YourPWD";
            SqlConnection con = new SqlConnection(constring);
       SqlCommand cmd = new SqlCommand("select * from ItemMasters", con);
            cmd.CommandType = CommandType.Text;
               SqlDataAdapter sda = new SqlDataAdapter(cmd);
               DataTable dt = new DataTable();                   
                        sda.Fill(dt);
                         return dt;

Bind Controls from SQL Server Database

Here, we can see a Sample of how to bind SQL server Database table data as return of Data Table to a DataGridView at Runtime from Button Click.

Image 9

Using the Code

The main code part in this application is to add Controls at Runtime and create event like Mouse Enter, Mouse Leave, Resize Control, Mouse Move, Mouse Down and to create a Class at Runtime and to compile the code at Runtime. All code part has been well commented so the user can easily understand the code.

Form Background

Here, I have used the Panel control to work as a Form. I have used the ColorDialog and OpenFileDialog to change the BackColor and Background Image of Form(Panel).

C#
private void frmbackColor_Click(object sender, EventArgs e)
       {
           ColorDialog colorDlg = new ColorDialog();
           if (colorDlg.ShowDialog() == DialogResult.OK)
           {
               if (pnControls.BackgroundImage != null)
               {

                   pnControls.BackgroundImage.Dispose();
                   pnControls.BackgroundImage = null;
               }
               pnControls.BackColor = colorDlg.Color;
           }
       }
       //////////////////////////////////
       /// To Add/Change the panel Background Image.

       private void frmBackGround_Click(object sender, EventArgs e)
       {
           OpenFileDialog dialog = new OpenFileDialog();
           dialog.Filter = "JPEG Files (*.jpeg)|*.jpeg|PNG Files
                  (*.png)|*.png|JPG Files (*.jpg)|*.jpg|GIF Files (*.gif)|*.gif";
           dialog.Title = "Select Your Form background Image File";
           if (dialog.ShowDialog() == DialogResult.OK)
           {
               pnControls.BackgroundImage = Image.FromFile(dialog.FileName);

           }
       }
       /////////////////////////////

Delete Controls

To delete all the controls and selected controls, I have used the public variable SelectedControl which will be stored in the current selected control.

C#
 /// To delete a selected control from mainPanel(OurForm)
private void DeleteSTool_Click(object sender, EventArgs e)
       {
           if (SelectedControl != null)
           {
               pnControls.Controls.Remove(SelectedControl);
               propertyGrid1.SelectedObject = null;
               pnControls.Invalidate();
           }
       }
       //////////////////////////////////
       /// To delete all controls placed in mainPanel(OurForm)
       private void DeleteATool_Click(object sender, EventArgs e)
       {
           pnControls.Controls.Clear();
           propertyGrid1.SelectedObject = null;
           pnControls.Invalidate();
       }

Add Controls

From the Toolbar, user can add controls to the Form(Panel). Now, for example, to add a DataTimePicker to the form here, we can see the below code. I create DataTimePicker at runtime and add the controls to the Form(Panel)>I have added the Runtime controls events like MoverEnter, MouseLeave, MouseDown. Using these events, the Runtime controls can be moved, Resized inside Form(Panel) for design. Similar to this, I have added all other controls like Textbox, Button, Label, etc.

C#
///To add DateTimePicker control to Panel(Form)

     private void DateTimeTool_Click(object sender, EventArgs e)
     {
         Random rnd = new Random();
         int randNumber = rnd.Next(1, 1000);
         String DatetimeName = "dte_" + randNumber;

         DateTimePicker ctrl = new DateTimePicker();
         ctrl.Location = new Point(70, 30);
         ctrl.Name = DatetimeName;
         ctrl.Font = new System.Drawing.Font("NativePrinterFontA", 10F,
                     System.Drawing.FontStyle.Regular,
                     System.Drawing.GraphicsUnit.Point, ((byte)(0)));
         ctrl.Text = DateTime.Now.ToString();
         ctrl.MouseEnter += new EventHandler(control_MouseEnter);
         ctrl.MouseLeave += new EventHandler(control_MouseLeave);
         ctrl.MouseDown += new MouseEventHandler(control_MouseDown);
         ctrl.MouseMove += new MouseEventHandler(control_MouseMove);
         ctrl.MouseUp += new MouseEventHandler(control_MouseUp);
         // ctrl.Click += new EventHandler(control_Click);

         pnControls.Controls.Add(ctrl);
     }

Runtime Controls Move and Resize

After adding Controls to Form(Panel), it needs to be moved, resized and assigned Property to selected Control. All these functions will be added here.

C#
/// RUN time Control Mouse Down Event used for Control Move
    private void control_MouseDown(object sender, MouseEventArgs e)
       {
           if (e.Button == MouseButtons.Left)
           {
               pnControls.Invalidate();  //unselect other control
               SelectedControl = (Control)sender;
               Control control = (Control)sender;
               mouseX = -e.X;
               mouseY = -e.Y;
               control.Invalidate();
               DrawControlBorder(sender);
               propertyGrid1.SelectedObject = SelectedControl;
           }
       }
       //////////////////////////////////
       /// RUN time Control Mouse Move Event used for Control Move
       ///
       private void control_MouseMove(object sender, MouseEventArgs e)
       {
           if (e.Button == MouseButtons.Left)
           {
               Control control = (Control)sender;
               Point nextPosition = new Point();
               nextPosition = pnControls.PointToClient(MousePosition);
               nextPosition.Offset(mouseX, mouseY);
               control.Location = nextPosition;
               Invalidate();
           }
       }

Runtime Class Creation

We need to execute a code for Runtime Button Click Event. The below function will create a Class at Runtime and we pass our code part TextBox text to this function from Runtime Button Control Click event.

C#
        /// When Runtime Control clicks this event trigger here, 
        /// we can write our code for runtime control click.
        /// Here in this click event, I have called RunTimeCodeGenerate method. 
        /// This method will create class at runtime and run your code.
        /// 
        private void control_Click(object sender, EventArgs e)
        {
           if (rdoMessage.Checked == true)
            {
                RunTimeCodeGenerate(txtCode.Text.Trim());
            }
            else if (rdoDataTable.Checked == true)
            {
                RunTimeCodeGenerate_ReturnTypeDataTable(txtCode.Text.Trim());
            }
            else if (rdoXML.Checked == true)
            {
                RunTimeCodeGenerate_ReturnTypeDataTable(txtCode.Text.Trim());
            }
            else if (rdoDatabase.Checked == true)
            {
                RunTimeCodeGenerate_ReturnTypeDataTable(txtCode.Text.Trim());
            }
         }

 ///This function will create a Runtime Class and add all our runtime code.
        
        public  void RunTimeCodeGenerate(String yourCodeHere)
        {
            try
            {
                string code = @"
                
                     using System;
                     using System.Xml;
                     using System.Data;
               namespace SHANUFormDesing
                {
                    public class Program
                    {
                        public static void Main()
                        {
                        YourCodeHere
                        }
                    }
                }
            ";
                string finalCode = code.Replace("YourCodeHere", yourCodeHere);

                CSharpCodeProvider provider = new CSharpCodeProvider();
                CompilerParameters parameters = new CompilerParameters();
                // Reference to System.Drawing library
                parameters.ReferencedAssemblies.Add("System.Windows.Forms.dll");
                parameters.ReferencedAssemblies.Add("System.Drawing.dll");
                parameters.ReferencedAssemblies.Add("System.Data.dll");
                parameters.ReferencedAssemblies.Add("System.Xml.dll");
                parameters.ReferencedAssemblies.Add("System.dll");
                // parameters.ReferencedAssemblies.Add("System.Data.SqlClient.dll");
                parameters.GenerateInMemory = true;
                // True - exe file generation, false - dll file generation
                parameters.GenerateExecutable = true;

                CompilerResults results = 
                        provider.CompileAssemblyFromSource(parameters, finalCode);

                if (results.Errors.HasErrors)
                {
                    StringBuilder sb = new StringBuilder();

                    foreach (CompilerError error in results.Errors)
                    {
                        sb.AppendLine(String.Format("Error ({0}): {1}", 
                                      error.ErrorNumber, error.ErrorText));
                    }

                    throw new InvalidOperationException(sb.ToString());
                }

                Assembly assembly = results.CompiledAssembly;
                Type program = assembly.GetType("SHANUFormDesing.Program");
                MethodInfo main = program.GetMethod("Main");

                main.Invoke(null, null);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

///This function will create at Runtime  and this class has a method with return 
// type as DataTable. Users can write their own code in textbox to return the data
// Table and bind to the selected Controls.      

        public void RunTimeCodeGenerate_ReturnTypeDataTable(String yourCodeHere)
        {
            try
            {
                string code = @"
                
                   using System;
                    using System.Collections.Generic;
                    using System.ComponentModel;
                    using System.Data;
                    using System.Drawing;
                    using System.Text;
                    using System.Windows.Forms;
                    using Microsoft.CSharp;
                    using System.CodeDom.Compiler;
                    using System.Reflection;
                    using System.Data.SqlClient;
                    using System.IO;
               namespace SHANUFormDesing
                {
                    public class Program
                    {
                        public static void Main()
                        {
                        }
                        public static DataTable returnDTS()
                        {
                        YourCodeHere;
                        }
                    }
                }
            ";
                string finalCode = code.Replace("YourCodeHere", yourCodeHere);

                CSharpCodeProvider provider = new CSharpCodeProvider();
                CompilerParameters parameters = new CompilerParameters();
                // Reference to System.Drawing library
                parameters.ReferencedAssemblies.Add("System.Windows.Forms.dll");
                parameters.ReferencedAssemblies.Add("System.Drawing.dll");
                parameters.ReferencedAssemblies.Add("System.Data.dll");
                parameters.ReferencedAssemblies.Add("System.Xml.dll");
                parameters.ReferencedAssemblies.Add("System.dll");
                // parameters.ReferencedAssemblies.Add("System.Data.SqlClient.dll");
                parameters.GenerateInMemory = true;
                // True - exe file generation, false - dll file generation
                parameters.GenerateExecutable = true;

                CompilerResults results = 
                        provider.CompileAssemblyFromSource(parameters, finalCode);

                if (results.Errors.HasErrors)
                {
                    StringBuilder sb = new StringBuilder();

                    foreach (CompilerError error in results.Errors)
                    {
                        sb.AppendLine(String.Format("Error ({0}): {1}", 
                                      error.ErrorNumber, error.ErrorText));
                    }

                    throw new InvalidOperationException(sb.ToString());
                }

                Assembly assembly = results.CompiledAssembly;
                Type program = assembly.GetType("SHANUFormDesing.Program");
                MethodInfo main = program.GetMethod("returnDTS");
              
          object ds   = main.Invoke(null, null);
          DataTable dt = (DataTable)ds;
          foreach (Control pnlCntl in pnControls.Controls)
          {
              // DataGridView Data Bind from Data Table and from XML Data Source.
              if (pnlCntl is DataGridView)
              {
                  if (pnlCntl.Name == ComboControlNames.SelectedItem.ToString())
                  {
                      DataGridView grid = (DataGridView)pnlCntl;
                      grid.DataSource = dt;
                  }
              }
              else if (pnlCntl is ListView)  // ListView Data Bind from Data Table 
                                             // and from XML Data Source.
              {
                  if (pnlCntl.Name == ComboControlNames.SelectedItem.ToString())
                  {
                      ListView lstView = (ListView)pnlCntl;
                      lstView.View = View.Details;

                      foreach (DataColumn column in dt.Columns)
                      {
                          lstView.Columns.Add(column.ColumnName);
                      }
                      foreach (DataRow row in dt.Rows)
                      {
                          ListViewItem item = new ListViewItem(row[0].ToString());
                          for (int i = 1; i < dt.Columns.Count; i++)
                          {
                              item.SubItems.Add(row[i].ToString());
                          }
                          lstView.Items.Add(item);
                      }
                  }
              }
              else if (pnlCntl is ComboBox)  // ComboBox Data Bind from Data Table 
                                             // and from XML Data Source.
              {
                  if (pnlCntl.Name == ComboControlNames.SelectedItem.ToString())
                  {
                      ComboBox cmbo = (ComboBox)pnlCntl;
                      cmbo.DataSource =dt;

                      cmbo.DisplayMember = "ItemName";
                      cmbo.ValueMember = "itemCode";
                  }
              }
              else if (pnlCntl is ListBox)  // ListBox Data Bind from Data Table 
                                            // and from XML Data Source.
              {
                  if (pnlCntl.Name == ComboControlNames.SelectedItem.ToString())
                  {
                      ListBox lstBox = (ListBox)pnlCntl;
                      lstBox.DataSource = dt;

                      lstBox.DisplayMember = "ItemName";
                      lstBox.ValueMember = "itemCode";
                  }
              }
          }
        }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

Points of Interest

I have a Future plan to extend this Application ad add functions like,New,Save and Open Form,Bind Controls using Code part from XMl and Database.

New features have been added. Now user can bind data from Datatable, XML File and from SQL Server DataBase to DataGridView, ListView, ComboBox, ListBox at runtime using the same code. We will see in detail about how to bind data from XML and from SQL database in this article. Download the latest source code version "SHANUFormDesing_V1.3zip".

Note: In my zip file bin folder, you can see a "FormDesign.txt" In this text file, I have added a samples code which can be used in code part to load data. I have attached "ShanuDataSource.xml" XML file for loading data from XML.

I have added all the features which I mentioned previously as New, Save, Open, Cut, Copy and paste controls, Bind data from XML and SQL server database. You can download the latest zip file version V1.3 which is available for download now at the top of this article. I have many more plans to add more features to this Shanu Form Design Application. Comments are welcome from the readers of this article.

The base idea of this application was taken from my previous article, Drawing in Windows Forms.

Reference

This below CodeProject article helped me to create a class and compile at runtime:

History

  • 26th September, 2014: Initial release
  • 29th September, 2014: New features have been added as Bind Data from DataTable, XML File and from SQL Server database to DataGridView, ListView, ComboBox and ListBox at runtime from runtime generated Buttons by user entered code
  • 29th September, 2014: New features have been added to Create New Form, Save Form and Open Form as XML format. Cut, Copy and Paste Controls
  • 30th September, 2014: Source code updated

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

syed shanu
Team Leader
India India
Microsoft MVP | Code Project MVP | CSharp Corner MVP | Author | Blogger and always happy to Share what he knows to others. MyBlog

My Interview on Microsoft TechNet Wiki Ninja Link

Comments and Discussions

 
SuggestionAppreciation Pin
Member 1159157727-Apr-18 1:57
MemberMember 1159157727-Apr-18 1:57 
QuestionAdding the control to Panel at mouse coordinates Pin
yenkay2-Feb-18 0:11
Memberyenkay2-Feb-18 0:11 
AnswerRe: Adding the control to Panel at mouse coordinates Pin
syed shanu4-Feb-18 13:11
mvasyed shanu4-Feb-18 13:11 
GeneralRe: Adding the control to Panel at mouse coordinates Pin
yenkay4-Feb-18 21:19
Memberyenkay4-Feb-18 21:19 
GeneralRe: Adding the control to Panel at mouse coordinates Pin
syed shanu4-Feb-18 21:40
mvasyed shanu4-Feb-18 21:40 
GeneralRating your article Pin
Member 1314089215-Aug-17 2:42
MemberMember 1314089215-Aug-17 2:42 
QuestionHi Shanu Pin
chunk6-Sep-16 21:48
Memberchunk6-Sep-16 21:48 
QuestionWhy Windows Forms? Pin
webmaster44217-Apr-16 9:29
Memberwebmaster44217-Apr-16 9:29 
QuestionI have some question? Pin
Thời Gian Hương Vị4-Apr-16 7:45
MemberThời Gian Hương Vị4-Apr-16 7:45 
QuestionNo, no, no. I do even not vote here. Pin
User 1106097924-Jan-16 4:20
MemberUser 1106097924-Jan-16 4:20 
GeneralMy vote of 5 Pin
Renju Vinod15-Oct-14 19:48
professionalRenju Vinod15-Oct-14 19:48 
QuestionInteresting and Informative article. Keep it up. Pin
Janak Desai3-Oct-14 3:57
MemberJanak Desai3-Oct-14 3:57 
AnswerRe: Interesting and Informative article. Keep it up. Pin
syed shanu3-Oct-14 5:48
mvasyed shanu3-Oct-14 5:48 
QuestionInteresting Article Pin
Anthony Fountaine1-Oct-14 12:21
MemberAnthony Fountaine1-Oct-14 12:21 
AnswerRe: Interesting Article Pin
syed shanu1-Oct-14 13:06
mvasyed shanu1-Oct-14 13:06 
GeneralMy vote of 3 Pin
BillWoodruff30-Sep-14 2:08
mveBillWoodruff30-Sep-14 2:08 
GeneralRe: My vote of 3 Pin
syed shanu30-Sep-14 2:13
mvasyed shanu30-Sep-14 2:13 
AnswerAwesome Pin
Daisy Onta30-Sep-14 1:22
MemberDaisy Onta30-Sep-14 1:22 
GeneralRe: Awesome Pin
syed shanu30-Sep-14 1:42
mvasyed shanu30-Sep-14 1:42 
QuestionNice! Pin
Volynsky Alex29-Sep-14 12:13
professionalVolynsky Alex29-Sep-14 12:13 
AnswerRe: Nice! Pin
syed shanu29-Sep-14 14:04
mvasyed shanu29-Sep-14 14:04 
QuestionRe: Nice! Pin
Volynsky Alex30-Sep-14 11:38
professionalVolynsky Alex30-Sep-14 11:38 
QuestionExcellent Potential! Pin
C Grant Anderson26-Sep-14 6:06
professionalC Grant Anderson26-Sep-14 6:06 
AnswerRe: Excellent Potential! Pin
johannesnestler26-Sep-14 7:57
Memberjohannesnestler26-Sep-14 7:57 
AnswerRe: Excellent Potential! Pin
syed shanu26-Sep-14 13:16
mvasyed shanu26-Sep-14 13:16 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.