Confessions of a .NET Developer!

Allow numbers or letters and disable right-click in textbox

I have seen a number of people asking how to allow only numbers to be added in to a textbox, sometimes only letters etc. Also to disable right-click to disable pasting. One such question can be found here: http://www.codeproject.com/Answers/208641/Wish-to-use-text-box-to-only-allow-numeric-Data-en/?cmt=114369#answer4 to which my friend Chanakya(a pro-photographer with excellent technical skills) gave an excellent, sweet and simple answer, also do read the comments. I got inspiration from him to write this post.
So I will create a new TextBox which will handle all the above things. Its a “derived” control where I create a class which derives from TextBox class.

Here is the class:

    class NewTextBox : System.Windows.Forms.TextBox
    {
        private ContextMenu OrgContextMenu;
        private RestrictType _restrictOptions = RestrictType.None;
        public RestrictType RestrictOptions
        {
            get { return _restrictOptions; }
            set { _restrictOptions = value; }
        }

        private bool _allowPaste = true;
        private ContextMenu PrevContextMenu { get; set; }
        private bool _setOnceFlag = false;

        public bool AllowPaste
        {
            get { return _allowPaste; }
            set
            {
                _allowPaste = value;
                DisableRightClick(value);
            }
        }

        protected override void OnKeyDown(KeyEventArgs e)
        {
            //To only allow numbers 
            if (RestrictOptions == RestrictType.OnlyNumbers)
            {
                if (char.IsNumber((char)e.KeyValue) || (((char)e.KeyData) == '\b'))
                {
                    e.SuppressKeyPress = false;
                }
                else { e.SuppressKeyPress = true; }
            }

            // To only allow letters
            else if (RestrictOptions == RestrictType.OnlyLetters)
            {
                if (char.IsLetter((char)e.KeyValue) || (((char)e.KeyData) == '\b'))
                {
                    e.SuppressKeyPress = false;
                }
                else { e.SuppressKeyPress = true; }
            }

            //To only allow 0's and 1's
            else if (RestrictOptions == RestrictType.OnlyBinary)
            {
                if (((char)e.KeyValue).Equals('1') || ((char)e.KeyValue).Equals('0')
                    || (((char)e.KeyData) == '\b'))
                {
                    e.SuppressKeyPress = false;
                }
                else { e.SuppressKeyPress = true; }
            }
        }

        //To disable Cntl + V for pasting
        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            if (AllowPaste.Equals(false))
            {
                if (keyData == (Keys.Control | Keys.V))
                {
                    return true;
                }
                else { return false; }
            }
            else { return false; }
        }

        // Disable Right-click for pasting
        private void DisableRightClick(bool enable)
        {
            if (_setOnceFlag == false)
            {
                OrgContextMenu = this.ContextMenu;
                _setOnceFlag = true;
            }
            if (enable.Equals(false))
            {
                // This will create a new ContextMenu with no options, hence no dropdown will be visible.
                this.ContextMenu = new ContextMenu();
            }
            // Set the original context menu.
            else
            { this.ContextMenu = OrgContextMenu; }
        }
    }

And an enum called RestrictType:

    public enum RestrictType
    {
        OnlyNumbers,
        OnlyLetters,
        OnlyBinary,
        None
    }

The code is self-explanatory. To briefly explain, I have created three properties, one is RestrictOptions which is of type RestrictType to let the user choose he/she wants allow numbers or letters or binary. Then the second property is to whether to disable pasting or not. The ProcessCmdKey function will take care of Ctrl + V, disabling it whenever the combination is used, and then using DiableRightClick function to disable Right-click by showing an empty ContextMenu. These two will be set by the user and by default, AllowPaste is true and RestrictType is None. The other property which is private is OrgContextMenu which will store the initial and original ContextMenu of the Textbox which will be set only one time.

Time to use it:

        public Form1()
        {
            InitializeComponent();
            newTextBox1.RestrictOptions = RestrictType.OnlyNumbers;
            newTextBox1.AllowPaste = false;
        }

Please do give your comments and valuable suggestions for improvement. 🙂
Happy Coding!

June 26, 2011 Posted by | C Sharp, Winforms | 1 Comment

How to use Owner Drawn Controls

There are few controls which support Owner-drawing such as ListBox, ListView,
TreeView, Combobox to name a few. With owner-drawing, you can manipulate the individual items in the above mentioned controls. Each item can be painted using the Graphics object.

In our example, we will use a Listbox having a list of all names of Brushes. Then we will supply the BackColor of each item based on the item’s text(name of Brush).
First step is to set the DrawMode property which takes the DrawMode enum as value. It has three options:
1)Normal – Drawn by operating system, not in our control.
2)OwnerDrawFixed – To be drawn by using our logic with the condition that all the items will be having the same height and width.
3)OwnerDrawVariable – Same as OwnerDrawFixed except that we have to supply the logic for height and width.

Normal won’t be of use in this example as we are going to apply our own logic for drawing of items.
Lets consider using OwnerDrawFixed.
Now to draw the items, we would have to use DrawItem event of ListBox.

Before that, first let us load all the names of the Brushes in the ListBox.

        void Form1_Load(object sender, EventArgs e)
        {
            foreach (PropertyInfo info in typeof(Brushes).GetProperties())
            {
                listBox1.Items.Add(info.Name);
            }
        }

We are using Reflection here to get all the properties and adding each property’s name in to the ListBox.

This is how the window will look like:

SimpleView

SimpleView

Our next task is to give a Background Color for each item in the ListBox depending on the item’s text. For that we will set the DrawMode of ListBox1.

listBox1.DrawMode = DrawMode.OwnerDrawFixed;

Next use the DrawItem event.

listBox1.DrawItem += new DrawItemEventHandler(listBox1_DrawItem);

And the method definition:

void listBox1_DrawItem(object sender, DrawItemEventArgs e)
{
    Brush brush;
    // Take the text from the current listbox item
    string text = listBox1.Items[e.Index].ToString();
    brush = new SolidBrush(Color.FromName(text));
    // Fill the background
    e.Graphics.FillRectangle(brush, e.Bounds);
    // Display the text using the default font and with black foreground
    e.Graphics.DrawString(text, e.Font, Brushes.Black, e.Bounds.X, e.Bounds.Y);
}

And our window will look like this:

OwnerDrawFixed

OwnerDrawFixed

But one small issue is the spacing, the items are closely spaced. Lets provide more spacing by increasing the height of each item. For that we have to set the DrawMode property to OwnerDrawVariable.

listBox1.DrawMode = DrawMode.OwnerDrawVariable;

Our DrawItem event will remain the same but we have to use an additional event “MeasureItem” to alter the height.

listBox1.MeasureItem += new MeasureItemEventHandler(listBox1_MeasureItem);

And the method definition:

void listBox1_MeasureItem(object sender, MeasureItemEventArgs e)
{
   e.ItemHeight = 20;            
}

So now our final window will look like this:

OwnerDrawVariable

OwnerDrawVariable

This is the final source code:

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            this.Load += new EventHandler(Form1_Load);
            //listBox1.DrawMode = DrawMode.OwnerDrawFixed;
            listBox1.DrawMode = DrawMode.OwnerDrawVariable;
            listBox1.DrawItem += new DrawItemEventHandler(listBox1_DrawItem);
            listBox1.MeasureItem += new MeasureItemEventHandler(listBox1_MeasureItem);
        }

        void listBox1_MeasureItem(object sender, MeasureItemEventArgs e)
        {
            e.ItemHeight = 20;            
        }

        void listBox1_DrawItem(object sender, DrawItemEventArgs e)
        {
            Brush brush;
            string text = listBox1.Items[e.Index].ToString();
            brush = new SolidBrush(Color.FromName(text));
            e.Graphics.FillRectangle(brush, e.Bounds);

            e.Graphics.DrawString(text, e.Font, Brushes.Black, e.Bounds.X, e.Bounds.Y);
        }

        void Form1_Load(object sender, EventArgs e)
        {
            foreach (PropertyInfo info in typeof(Brushes).GetProperties())
            {
                listBox1.Items.Add(info.Name);
            }
        }
    }

June 26, 2011 Posted by | Winforms | Leave a comment