windows form how to add an icon to the right or left of a text box

27,543

Solution 1

You can use a Panel, a TextBox and a PictureBox.

enter image description here

The TextBox must be placed in a Panel so you can't write over your search picture.

Solution 2

You can create a new UserControl which will do the required job. You have to extend the TextBox class for that. Look at the code below:

    public class IconTextBox : System.Windows.Forms.TextBox
    {
        public IconTextBox() : base() { SetStyle(System.Windows.Forms.ControlStyles.UserPaint, true); this.Multiline = true; }

        public System.Drawing.Bitmap BitmapImage
        {
            set;
            get;
        }

        protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
        {
            base.OnPaint(e);
            System.Drawing.Image img = BitmapImage as System.Drawing.Image;
            e.Graphics.DrawImage(img, new System.Drawing.Point(this.Width - (img.Width), 0));

        }

    }

And in the OnPaint method you can specify the image. Also you can extend this to have a custom property which can be the image path. Your choice.

Solution 3

Along the lines of Atanas's answer I found the following to work well. The SearchImage and CancelSearchImage properties can be set to control the images used.

public class SearchTextBox : TextBox
{
    private const int EM_SETMARGINS = 0xd3;

    [System.Runtime.InteropServices.DllImport("user32.dll")]
    private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);

    private PictureBox searchPictureBox;

    private Button cancelSearchButton;

    public SearchTextBox()
    {
        cancelSearchButton = new Button();
        cancelSearchButton.Anchor = AnchorStyles.Top | AnchorStyles.Right;
        cancelSearchButton.Size = new Size(16, 16);
        cancelSearchButton.TabIndex = 0;
        cancelSearchButton.TabStop = false;
        cancelSearchButton.FlatStyle = FlatStyle.Flat;
        cancelSearchButton.FlatAppearance.BorderSize = 0;
        cancelSearchButton.Text = "";
        cancelSearchButton.Cursor = Cursors.Arrow;

        Controls.Add(cancelSearchButton);

        cancelSearchButton.Click += delegate
        {
            Text = "";
            Focus();
        };

        searchPictureBox = new PictureBox();
        searchPictureBox.Anchor = AnchorStyles.Top | AnchorStyles.Right;
        searchPictureBox.Size = new Size(16, 16);
        searchPictureBox.TabIndex = 0;
        searchPictureBox.TabStop = false;
        Controls.Add(searchPictureBox);

        // Send EM_SETMARGINS to prevent text from disappearing underneath the button
        SendMessage(Handle, EM_SETMARGINS, (IntPtr)2, (IntPtr)(16 << 16));

        UpdateControlsVisibility();
    }

    protected override void OnTextChanged(EventArgs e)
    {
        base.OnTextChanged(e);
        UpdateControlsVisibility();
    }

    private void UpdateControlsVisibility()
    {
        if (string.IsNullOrEmpty(Text))
        {
            cancelSearchButton.Visible = false;
            searchPictureBox.Visible = true;
        }
        else
        {
            cancelSearchButton.Visible = true;
            searchPictureBox.Visible = false;
        }
    }

    [Browsable(true)]
    public Image SearchImage
    {
        set
        {
            searchPictureBox.Image = value;
            searchPictureBox.Left = Width - searchPictureBox.Size.Width - 4;
            searchPictureBox.Top = Height - searchPictureBox.Size.Height - 4;
        }

        get { return searchPictureBox.Image; }
    }

    [Browsable(true)]
    public Image CancelSearchImage
    {
        set
        {
            cancelSearchButton.Image = value;
            cancelSearchButton.Left = Width - searchPictureBox.Size.Width - 4;
            cancelSearchButton.Top = Height - searchPictureBox.Size.Height - 4;
        }

        get { return cancelSearchButton.Image; }
    }
}

Solution 4

Using a user control, or adding code every time you want to do this can get very cumbersome. The way I handle this is to add an initializer class which can be called from my form at runtime. The behavior of this code is when the user starts typing, the image disappears. If the textbox has no content, then the image shows up. I handle the click event for the picture box to set focus to the textbox to preserve the illusion that it is part of the control, and offset the left in order to allow the | to be displayed showing that the textbox has focus and is ready to receive input.

By writing a controller instead of a user control, I avoid having to propagate all of the events and properties from the textbox through my user control. This class is dependent on System.Windows.Forms, and can either be included directly in your Windows Forms application, or added to a control library which can be called from multiple applications.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;

namespace WindowsFormsApplication1
{
    class TextBoxIcon
    {
        public static TextBoxIcon AddIcon(TextBox textbox, Image icon)
        {
            if (icon != null) {
                return new TextBoxIcon(textbox, icon);
            } else {
                return null;
            }
        }

        private TextBox _TextBox;
        private PictureBox _PictureBox;

        private TextBoxIcon(TextBox textbox, Image icon) {
            this._TextBox = textbox;
            this._PictureBox = new PictureBox();
            this._PictureBox.BackColor = textbox.BackColor;
            this._PictureBox.Image = ScaleImage(icon);
            this._TextBox.Parent.Controls.Add(_PictureBox);
            this._PictureBox.Location = new Point(textbox.Left + 5, textbox.Top + 2);
            this._PictureBox.Size = new Size(textbox.Width - 10, textbox.Height - 4);
            this._PictureBox.Anchor = textbox.Anchor;
            this._PictureBox.Visible = _TextBox.Visible;
            this._PictureBox.BringToFront();
            textbox.Resize += TextBox_Resize;
            textbox.TextChanged += TextBox_TextChanged;
            textbox.Leave += TextBox_Leave;
            _PictureBox.Click +=  PictureBox_Click;
            textbox.VisibleChanged += TextBox_VisibleChanged;
        }

        public static Image ScaleImage(Image img) {
            if (img.Height == 16) {
                return img;
            } else {
                return new Bitmap(img, new Size((int)((img.Height / 16.0) * img.Width), 16));
            }
        }

        private void TextBox_Resize(Object sender, EventArgs e) {
            _PictureBox.Size = new Size(_TextBox.Width - 10, _TextBox.Height - 4);
        }

        private void TextBox_VisibleChanged(Object sender, EventArgs e) {
            _PictureBox.Visible = _TextBox.Visible;
        }

        private void ShowPictureBox() {
            _PictureBox.Visible = String.IsNullOrEmpty(_TextBox.Text);
        }

        private void TextBox_TextChanged(Object sender, EventArgs e) {
            ShowPictureBox();
        }

        private void TextBox_Leave(Object sender, EventArgs e) {
            ShowPictureBox();
        }

        public void PictureBox_Click(object sender, EventArgs e) { 
            _TextBox.Focus();
        }
    }
}

Here is how the class would be used from the form:

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

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            TextBoxIcon.AddIcon(txtSearch, Properties.Resources.search);
        }
    }
}

As long as the text box exists, and has been added to the form, the call can happen at any time.

Share:
27,543
Agnieszka Polec
Author by

Agnieszka Polec

Updated on December 21, 2020

Comments

  • Agnieszka Polec
    Agnieszka Polec over 3 years

    I am building a Windows form application and I have a text box for searching purposes.

    I would like to put a search icon inside the text box, at the right or left

    like this:

    enter image description here

    I would prefer at the right

    Update 1

    I am asking about Windows forms not ASP.net or MVC

    • Mo Patel
      Mo Patel almost 10 years
      Odd concept. From a user's point of view I would prefer to see icons like these to the right of the control and not to the left like in your sample.
    • Agnieszka Polec
      Agnieszka Polec almost 10 years
      @MPatel that is the search pannel in this website :)
    • Cody Gray
      Cody Gray almost 10 years
      See here: Button inside a winforms textbox This isn't an exact duplicate, since you're asking about an icon instead of a button. But the answer is almost identical. Only a minor tweak is required to substitute a button for a PictureBox (or something else that can display an icon). I strongly recommend Hans's answer over the alternatives.
  • Agnieszka Polec
    Agnieszka Polec almost 10 years
    i am trying ur solution
  • Agnieszka Polec
    Agnieszka Polec almost 10 years
    where can I set the path of the image?
  • Cody Gray
    Cody Gray almost 10 years
    What keeps the text from appearing over the image?
  • Agnieszka Polec
    Agnieszka Polec almost 10 years
    @CodyGray where is the image path please in the above example?
  • Cody Gray
    Cody Gray almost 10 years
    The point of the Panel is to simulate the "well" of the TextBox. You'll also need to set its properties appropriate to give a border that matches the TextBox. And set the TextBox to be borderless.
  • Cody Gray
    Cody Gray almost 10 years
    There is no image path. You have to add that yourself. Atanas's code has a BitmapImage property that takes a Bitmap object. The Bitmap class has a method that loads a bitmap from a file: Bitmap.FromFile(...)
  • TaW
    TaW almost 10 years
    What a terrible idea - the question is about a search textbox_
  • Lamloumi Afif
    Lamloumi Afif almost 10 years
    @TaW What is the terrible in the idea?? do you mean that changing textbox to richtextbox is terrible or what exactly , please explain
  • TaW
    TaW almost 10 years
    Even simpler: Use a Label! It can display the image, can be clicked and saves you one expensive control. You set its parent in the load event: textBox1.Parent = label1; textBox1.Location = Point.Empty; to display the image to the left choose an approriate value for textbox.left!
  • TaW
    TaW almost 10 years
    A RichTextbox is a very expensive control. None of its features are called for here. Even using a PictureBox is overkill. The simplest way is to use a Textbox inside a Label.
  • Lamloumi Afif
    Lamloumi Afif almost 10 years
    @TaW If I uderstood you think that putting the "search" word as a default text in the textbox is better and easier than changing textbox to richtextbox. But, I think the OP mentionned that she needs to add an icon not a simple "search" text. To be honest, I don't know a method to add an image as a background in a textbox , for this reason I suggest to change it to richtextbox which have this property.
  • TaW
    TaW almost 10 years
    No, you did not understand me. The Text should be deleted from the Label and an image should be added instead! Note: The good old Label is probably the moste underrated of all Controls. Do have a look at it!! It can show an aligned image, just as was requested.
  • Cody Gray
    Cody Gray almost 10 years
    Meh. Panels and labels aren't more expensive than the other. It is more obvious that you're using a container control if you use a Panel rather than a Label.
  • Alexey Khoroshikh
    Alexey Khoroshikh over 7 years
    You pointed to the MSDN page what says "This property is not relevant to this class." And it seems that you haven't tested this (wrong) solution before offering it.