2

When writing in CSS I can add a class of "badge" and get what I want. A small number near a button or tab with some styling to it, to show that this control has pending info that needs review.

There is even a post here of somebody trying to do what I want on iOS

I want to do this on WinForms on either a button or a tab. If an easier solution is available in WPF, then I might consider using that instead.

Here is an image showing an example of what I want to achieve:

Buttons and controls with badges

Community
  • 1
  • 1
Oxymoron
  • 1,380
  • 2
  • 19
  • 47
  • 3
    `Ian easier solution is available in WPF I might consider using that instead.` - of course, winforms is completely useless and doesn't support anything. What have you tried? – Federico Berasategui Apr 20 '15 at 18:53
  • 3
    @HighCore Dont you think you are exadurating with the "uselessness" of winforms? – Eminem Apr 20 '15 at 19:02
  • 1
    @Eminem no. Not at all. Whatever you can achieve in winforms, I can achieve the same in WPF with 30% the amount of effort and code and with a much greater extensibility, customizability, and proper separation. winforms is useless. – Federico Berasategui Apr 20 '15 at 19:05
  • Could you not create a custom composite control that is a combo of a button and label? – Scott Wylie Apr 20 '15 at 19:05
  • 1
    I've got to second @HighCore here... of course it's possible to do in winforms, but it's *way* easier in WPF, as is almost everything. If you're open to considering it (i.e. not supporting a legacy winforms app) I would absolutely urge you to go that route. – DrewJordan Apr 20 '15 at 19:36
  • Unless you are developing a new application that has to run on Windows 98... you should be using WPF over WinForms. There is absolutely zero reason to use WinForms other than the unwillingness to learn a great framework (WPF). This badge view would be about 10 lines of XAML to achieve. – Erik Apr 20 '15 at 20:14
  • 2
    OP asked about WinForms. – David Tansey Apr 20 '15 at 22:51
  • For more on the grand promise and limited success of WPF see [here](http://www.i-programmer.info/news/89-net/7973-wpf-lives.html). – TaW Apr 20 '15 at 22:54
  • Have you resolved your problem? – TaW Apr 28 '15 at 20:49
  • thank you for the reminder. I've selected an answer. All answers basically were for the same thing. Add a label inside a control with public properties for controlling them. – Oxymoron Apr 29 '15 at 20:26
  • @FedericoBerasategui you are exaggerating, Winforms is not "completely useless" at all. Yes, Wpf is more complete and you can do more things than with WF, but it is more complex too. – Luis Dec 09 '18 at 21:50
  • @Luis yes, I'm pretty sure using declarative XAML and MVVM (like any modern UI framework does, even on the Web) is somehow "more complex" than using stupid procedural code in `form1.cs` handling click events, doing `txtLastName.Text = person.LastName` all the time and of course tight coupling your logic to the UI, resulting in completely unmaintainable, rigid code. – Federico Berasategui Dec 09 '18 at 23:52
  • @FedericoBerasategui you can use MVVM or any design pattern with Winforms, and you can use "procedural code" with WPF. It is clear that you don't like Winforms, but saying that it is "completely useless" is overexaggerating. Do you know that you can use XAML to create a Winforms app? – Luis Oct 23 '19 at 18:54

3 Answers3

7

And here is a way with a static Adorner class, very quick and rather dirty..

It can add a Label to many controls and it includes a click action, dynamic text and has code to remove the Label.

an adorned button

Adding a Badge to a Button takes one line:

    public Form1()
    {
        InitializeComponent();
        // adorn one Button with a Badge Label:
        Adorner.AddBadgeTo(button1, "123");
        // if you want to you can add a click action:
        Adorner.SetClickAction(button1, dobidoo);
    }

    // a test action
    void dobidoo(Control ctl)
    {
        Console.WriteLine("You have clicked on :" + ctl.Text);
    }

Here is the Adorner class:

static class Adorner
{
    private static List<Control> controls = new List<Control>();

    static public bool AddBadgeTo(Control ctl, string Text)
    {
        if (controls.Contains(ctl)) return false;

        Badge badge = new Badge();
        badge.AutoSize = true;
        badge.Text = Text;
        badge.BackColor = Color.Transparent;
        controls.Add(ctl);
        ctl.Controls.Add(badge);
        SetPosition(badge, ctl);

        return true;
    }

    static public bool RemoveBadgeFrom(Control ctl)
    {
        Badge badge = GetBadge(ctl);
        if (badge != null)
        {
            ctl.Controls.Remove(badge);
            controls.Remove(ctl);
            return true;
        }
        else return false;
    }

    static public void SetBadgeText(Control ctl, string newText)
    {
        Badge badge = GetBadge(ctl);
        if (badge != null) 
        {
                badge.Text = newText;
                SetPosition(badge, ctl);
        }
    }

    static public string GetBadgeText(Control ctl)
    {
        Badge badge = GetBadge(ctl);
        if (badge != null) return badge.Text;
        return "";
    }

    static private void SetPosition(Badge badge, Control ctl)
    {
       badge.Location = new Point(ctl.Width  - badge.Width - 5, 
                                  ctl.Height - badge.Height - 5);
    }

    static public void SetClickAction(Control ctl, Action<Control> action)
    {
        Badge badge = GetBadge(ctl);
        if (badge != null)  badge.ClickEvent = action;
    }

    static Badge GetBadge(Control ctl)
    {
        for (int c = 0; c < ctl.Controls.Count; c++)
            if (ctl.Controls[c] is Badge) return ctl.Controls[c] as Badge;
        return null;
    }


    class Badge : Label
    {
        Color BackColor = Color.SkyBlue;
        Color ForeColor = Color.White;
        Font font = new Font("Sans Serif", 8f);

        public Action<Control> ClickEvent;

        public Badge()   {}

        protected override void OnPaint(PaintEventArgs e)
        {
            e.Graphics.FillEllipse(new SolidBrush(BackColor), this.ClientRectangle);
            e.Graphics.DrawString(Text, font, new SolidBrush(ForeColor), 3, 1);
        }

        protected override void OnClick(EventArgs e)
        {
            ClickEvent(this);
        }

    }
}

Note that while you can add it to most controls, not all work as well as a Button. A TabControl is rather hard to adorn as its Tabs really are not Controls but just painted areas, so just like adding a 'close X' to it you would have to user draw the badges of all TabPages..

TaW
  • 53,122
  • 8
  • 69
  • 111
4

Really the easiest and best way to achieve this is to create a new customized UserControl. Just add a button and insert a label over it on the right. Then add getters and setters for the controls inside you new UserControl. Here's an example of get/set to configure the button's notification:

public String ButtonNotification {
   get { return yourUserControlLabel.Text; }
   set {
         if (value == null || value == "") { yourUserControlLabel.Visibility = Hidden; }
         else { yourUserControlLabel.Visibility = Visible; }

         yourUserControlLabel.Text = value;
   }
}

Then you can customize the label's visibility and other properties with the getters/setters.

W0lfw00ds
  • 2,018
  • 14
  • 23
1

Here is a rough way to do it with a UserControl:

public partial class btnControl : UserControl
    {
        public Label label = new Label();
        public TextBox box = new TextBox();

        public btnControl()
        {
            this.label = new System.Windows.Forms.Label();
            this.box = new System.Windows.Forms.TextBox();
            this.SuspendLayout();
            // 
            // label
            // 
            this.label.AutoSize = true;
            this.label.ForeColor = System.Drawing.Color.White;
            this.label.Location = new System.Drawing.Point(4, 7);
            this.label.Name = "label";
            this.label.Size = new System.Drawing.Size(35, 13);
            this.label.TabIndex = 0;
            this.label.Text = "label";
            // 
            // box
            // 
            this.box.Location = new System.Drawing.Point(110, 3);
            this.box.Name = "box";
            this.box.Size = new System.Drawing.Size(31, 20);
            this.box.TabIndex = 1;
            this.box.Enabled = false;
            // 
            // btnControl
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.BackColor = System.Drawing.Color.Blue;
            this.Controls.Add(this.box);
            this.Controls.Add(this.label);
            this.Name = "btnControl";
            this.Size = new System.Drawing.Size(144, 26);
            this.ResumeLayout(false);
            this.PerformLayout();
        }
    }

Then put it in your form:

private void Form1_Load(object sender, EventArgs e)
        {
            btnControl Control = new btnControl();
            this.Controls.Add(Control);
            Control.label.Text = "Home";
            Control.box.Text = "42";
        }

Gives you:

enter image description here

DrewJordan
  • 5,266
  • 1
  • 25
  • 39