Amorphous
Amorphous

Reputation: 697

How to find a programatically created button by text?

So my program is generating a bunch of buttons like so:

foreach (var subdir in dir.GetDirectories()) { 
    var path = subdir.Name;

    var button = new Button {
        Text = getFlavor(path) + "\t(" + path + ")",
        Width = Width,
        Height = 35,
        Top = y
    };

    button.Click += buttonClick;
    Controls.Add(button);

    if (button.Text.Contains("Kittens") 
        i++;
}

I want to try something like this

if (i == 1) {
      [Button.ThatContains("Kitten)].Click;
}

"ThatContains" is not a real method. How do I get references to buttons I've created programmatically ?

Upvotes: 1

Views: 287

Answers (5)

Tim Schmelter
Tim Schmelter

Reputation: 460058

You could use OfType<Button> to find all buttons in the container control where you've added them(f.e. a Panel). Then a liitle bit LINQ power gives you the correct button(s):

var kittenButtons = panel.Controls.OfType<Button>()
    .Where(btn => btn.Text.Contains("Kittens"));
foreach(Button btn in kittenButtons)
    btn.PerformClick();

If you just want to click the first:

Button kittenButton = panel.Controls.OfType<Button>()
    .FirstOrDefault(btn => btn.Text.Contains("Kittens"));
if(kittenButton != null)
    kittenButton.PerformClick();

For what it's worth, here is also an extension method that returns controls recursively via deferred execution which allows to use only the first found Buttton or consume all down the road:

public static IEnumerable<T> GetChildControlsRecursive<T>(this Control root) where T : Control
{
    if (root == null) throw new ArgumentNullException("root");
    var stack = new Stack<Control>();
    stack.Push(root);
    while (stack.Count > 0)
    {
        Control parent = stack.Pop();
        foreach (Control child in parent.Controls)
        {
            if (child is T)
                yield return (T)child;
            stack.Push(child);
        }
    }
    yield break;
}

Now you can use similar code as above to get for example the first matching button or all:

var kittenButtons = this.GetChildControlsRecursive<Button>()
    .Where(b => b.Text.Contains("Kittens"));
// search just until the first button is found
Button firstKittenButton = kittenButtons.FirstOrDefault();
if(firstKittenButton != null) firstKittenButton.PerformClick;
// loop all
foreach(Button btn in kittenButtons)
    btn.PerformClick();

Upvotes: 4

AWinkle
AWinkle

Reputation: 673

You could use a dictionary or you could use a simple recursive loop (in case you are sticking the buttons into different containers).

private bool ClickButton(string buttonName, Control control) {
        if (control is Button && control.Text.Contains(buttonName) {
            ((Button)control)PerformClick();
            return true;
        } 

        if (control.HasChildren) {
            foreach (Control childControl in control.Controls) {
                if (ClickButton(buttonName, childControl)) {
                    return true;
                }

            }
        }
        return false;
    }

Usage: ClickButton("Kittens", this);

Or you could use a dictionary, as some have suggested.

private Dictionary<string, Button> DynamicButtons = new Dictionary<string, Button>();

private void ClickDictionaryButton(string buttonName) {
        var matches = DynamicButtons.Where(x => x.Key.Contains(buttonName));
        foreach (var match in matches) {
            match.Value.PerformClick();
        }
}

Usage: ClickDictionaryButton("Kittens", this);

Upvotes: 0

frusDev
frusDev

Reputation: 1978

Either create a subclass of Button to store the information you want and instantiate that instead or use the Tag property

public class MyButton : Button
{
    public int ButtonID { get; set; }
}

public class MyApplication
{
    public void DoSomething()
    {
        int i; // todo: loop stuff
        var button = new MyButton
        {
            Text = getFlavor(path) + "\t(" + path + ")",
            Width = Width,
            Height = 35,
            Top = y,
            ButtonID = i
        };

    }
}

Or why not cast the sender parameter of the button click event as a Button and check the text?

public class MyApplication
{
    public void DoSomething()
    {
        var b = new Button();
        b.Click += b_Click;
    }

    public void b_Click(object sender, EventArgs e)
    {
        Button b = (Button)sender;
        switch (b.Text) {
            case "Kittens":
                return;
            default:
                return;
        }
    }
}

Upvotes: 2

Mayank
Mayank

Reputation: 8852

Something like this

var button = FirstOrDefault(y => y is Button && y.Text.Contains("Kittens"));

if(button != null) button.PerformClick();

Upvotes: 1

David
David

Reputation: 10708

In order to get the references, you may need to what you would do with getting references of any other type - store them somewhere, which does not seem to be the case here at all. Normally, you would register your buttons for interaction from a user by attaching them to a Form. Assuming you're not doing this by the looks of your sample code, I'm going to recommend storing them into a Dictionary<string, Button>.

Upvotes: 0

Related Questions