Field List Control in C#

Introduction

I started to write a large program that will contain some good functions to be able to code fields for documents and retrieve them easily by searching fields and/or OCRed body of the documents. My first two steps of the application about done. I would like to share one of the components that I wrote called "Field List" control 

This control runs of a file with "lst" extension which carries a very simple structure in text format.

Please check out the Video Clip of this code here,


Points of Interest

This article targets beginners to learn about "creating controls during runtime" which also includes List<string> array, splitting string and other simple samples of C# programming. If enhanced, this also could be used for any programs that needs unlimited field entry.

Components

I will include one component in this article. Please download the code to see the working sample. 
  • FieldList control
"FieldList" control has a Vertical Scroll bar and a container that labels, textboxes and/or drop-down boxes created during runtime. So, this is one of the useful things people might learn. How to create controls in runtime and get information from them. Following image shows the text file this control uses. Creating the text file is up to you. I am using another form that I created. First line of the code has a number that tells the control how many other lines of texts are being used for header purposes. Headers are not used by the control, they are ignored. Also, if you have a drop down menu, you have to use "@" sign to split them. You still identify the field name with the first item of the line. Example,"States@Alaska@Arizona@Alabama" your field name is "States" and drop down items are Alaska, Arizona and Alabama.

1.gif
 
The Code

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace FieldList
{
    public partial class FieldList : UserControl
    {
        // Fields
        // private int intFieldCount; // Number of fields will be set
        private const int BoxHeight = 20; // Height of the Textbox
        public FieldList()
        {
            InitializeComponent();
        }
        // We get the path of the 'lst' file and the lines will be skipped at the top of that file
        // There might be some user notes on the top that program should skip at streamread
        public void LoadFields(string FieldFilePath)
        {
            // Check the path
            if (File.Exists(FieldFilePath) != true)
            {
                throw new Exception("'Field List' File Can not be found!");
            }
            string LineData; // Line datab from 'lst' file
            string LabelText; // Field label text
            string ControlBoxName; // Combobox and textbox names created with this
            string[] SplitText; // Splitter
            int ControlCount = 0; // This calculates the 'top' for the controls
            int ii = 0; // Loop to bypass top lines of the 'lst' file
            int SkipFirstLines;
            // panel1.Top = 0; // Set container's 'top' value
            panel1.Width = (this.Width - 10) - SCRBar.Width; // Set container's 'width' value
            // Open Stream to tread
            StreamReader CurrentFields = new StreamReader(FieldFilePath);
            // Find out how many lines wil be skipped
            SkipFirstLines = Convert.ToInt32(CurrentFields.ReadLine());
            // Set fields
            for (ii = 0; ii < SkipFirstLines; ii++) // First two lines are not fields
            {
                LineData = CurrentFields.ReadLine();
            }
            while (CurrentFields.EndOfStream != true)
            {
                LineData = CurrentFields.ReadLine();
                SplitText = LineData.Split('@');
                LabelText = SplitText.GetValue(0).ToString();
                ControlBoxName = LabelText.Replace(' ', '_'); // Replace spaces with
                // underscores for text and combo box names
                if (ControlCount > 0)
                {
                    ControlCount = ControlCount + (BoxHeight + 2); // Encrement 'top' value
                }
                else
                {
                    ControlCount = 1; // This is set to one only once.
                    // Code will not come here the second time
                }
                if (SplitText.Count() > 1)
                {
                    // Drop Down Control
                    ComboBox NewComboBox = new ComboBox();
                    NewComboBox.Width = ((panel1.Width / 3) * 2) - 5;
                    NewComboBox.Height = BoxHeight;
                    NewComboBox.Left = (panel1.Width / 3) + 5;
                    NewComboBox.Top = ControlCount;
                    for (ii = 0; ii < (SplitText.GetUpperBound(0)); ii++)
                    {
                        NewComboBox.Items.Add(SplitText.GetValue(ii + 1));
                    }
                    NewComboBox.Name = "cmb" + ControlBoxName;
                    panel1.Controls.Add(NewComboBox);
                    NewComboBox.SelectedIndex = 0;
                }
                else
                {
                    // Text Control
                    TextBox NewTextBox = new TextBox();
                    NewTextBox.Width = ((panel1.Width / 3) * 2) - 5; // Space little less than 2/3
                    NewTextBox.Height = BoxHeight;
                    NewTextBox.Left = (panel1.Width / 3) + 5; // Start next to label
                    NewTextBox.Top = ControlCount;
                    NewTextBox.Name = "txt" + ControlBoxName;
                    panel1.Controls.Add(NewTextBox);
                }
                CreateLabel(ControlCount, LabelText);
                panel1.Height = ControlCount + 32;
            }
            ScrollSet();
            CurrentFields.Dispose();
        }
        private void CreateLabel(int TopValue, string LabelName)
        {
            // Label Control
            Label NewLabel = new Label();
            NewLabel.Width = (panel1.Width / 3) + 5; // Space little less than 2/3
            NewLabel.Height = BoxHeight;
            NewLabel.Left = 0; // Start at the left edge of the panel1
            NewLabel.Top = TopValue;
            panel1.Controls.Add(NewLabel);
            NewLabel.TextAlign = ContentAlignment.MiddleRight;
            NewLabel.Text = LabelName;
        }
        private void SCRBar_Scroll(object sender, ScrollEventArgs e)
        {
            panel1.Top = -SCRBar.Value;
        }
        private void FieldList_SizeChanged(object sender, EventArgs e)
        {
            ScrollSet();
        }
        private void ScrollSet()
        {
            if (panel1.Height > this.Height)
            {
                SCRBar.Enabled = true;
                SCRBar.Maximum = panel1.Height - this.Height;
            }
            else
            {
                SCRBar.Enabled = false;
            }
            panel1.Top = 0; // Set container's 'top' value
        }
        public List<string> GetValues()
        {
            List<string> strValues = new List<string>();
            foreach (Control BoxControlText in panel1.Controls)
            {
                if (BoxControlText is TextBox || BoxControlText is ComboBox)
                {
                    strValues.Add(BoxControlText.Text);
                }
            }
            return strValues;
        }
        public void SetValues(List<string> BoxValues)
        {
            int ii = 0;
            foreach (Control BoxControlText in panel1.Controls)
            {
                if (BoxControlText is TextBox || BoxControlText is ComboBox)
                {
                    BoxControlText.Text = BoxValues[ii];
                    ii++;
                }
            }
        }
    }
}

Above code is the entire code for the control. I am pretty sure this can be done some easier ways, but the code is doing exactly what I need. If you do come up with some more shortcuts for it, please let me know. 

'File.Exists(FieldFilePath)' One of my best friends in coding when it comes to check a file or a folder.

panel1.Width = (this.Width - 10) - SCRBar.Width; Sometimes I set the width of some of the controls during runtime. It's good practice to make things more organize on your application. for (ii = 0; ii < SkipFirstLines; ii++) Here is the code when we skip the first lines (optional). The reason I decided to have header lines in Field List file to put some reminders or warnings. Sometimes people open text files and screw around with them, it's good thing to let them now not to touch to some of them.

ControlCount = ControlCount + (BoxHeight + 2); This line is the main line which the height of the container calculated. Also it sets where the next control (Label, textbox and/or combo box) will be located from top (of the container).

ControlBoxName = LabelText.Replace(' ','_'); When I name the controls, I made sure there are no spaces between words. I also added "cbo" and/or "txt" start of the names to identify them.

panel1.Controls.Add(NewTextBox); Each control has "add" method, so all we have to do is to add the controls into wherever we want... in this case into container control. Just in case you ask "Why into the container. I use the container group everything and actually move it up and down with the scrollbar if items get more than fits in the control itself (Check private void ScrollSet() method).

I used List<string> a lot in this code. I also used "@" as my separator for the Drop Down menu items, and a "hidden listbox" to keep the "header text" during settings. Remember, those things can be done with many ways. You don't have to use separators, you can use other methods such as lines, or use a database to keep y our drop down information and other field information. instead of a "hidden listbox", you can write your own array class etc. I went shortcut, the tools available for me instead of writing more lines of code. A habit from VB world. Pros will roll their eyes for this code... which I want to know how would they do :)