I made a simple tic tac toe game in C# (Visual Studio Winform). I added a menu strip containing a file menu which has a "New Game" option in it. What i'm trying to do is to reset all the buttons' text and I had disabled them so I want to enable them again. I used the following code when I only had a menustrip and the buttons on my form, and it worked. But now I added a label to my form, and this code is not working. I know I can simply reset the value one by one for each button, but I wanted to know what went wrong when I added a label. Thanks in advance. :)
try
{
foreach (Control c in Controls)
{
Button b = (Button)c;
b.Text = "";
b.Enabled = true;
}
}
catch { }
Your code doesn't work anymore because the Controls collection now contains also the Label, so when your loop reaches the Label control it throws an exception at this line
Button b = (Button)c;
You cannot cast a Label to be a Button, but this exception is swallowed by the empty try catch and your loop terminates without any warning. It is considered a bad practice to have empty try/catch, better remove them and let the exception show.
Your problem could be avoided using the OfType enumerable extension to get only the controls of type Button. The rest of your code now doesn't need any other changes.
foreach (Button b in Controls.OfType<Button>())
{
b.Text = "";
b.Enabled = true;
}
However keep in mind that this loop works on the Form Controls container. If you add some other kind of container (like a GroupBox or a Panel) and move your buttons inside that control then you need to loop on that container Controls property.
EDIT
To exclude a particular button from the loop you could add a call to IEnumerable<T>.Where to the sequence returned by OfType. For example
foreach (Button b in Controls.OfType<Button>().Where(x => x.Name != "button10")
.....
this.Controls.OfType<Button>().ToList().ForEach(b => { b.Text = ""; b.Enabled = true; });
Related
I am facing an issue while running through all the User Controls in my Windows form.
I am creating a Windows Form that has the following features:
The Main form has 3 User Controls embedded in it
The Main form also has a combo box. Selecting a particular value in the Combo box will bring the corresponding User Control to the front.
Each User Control has two Check boxes as well as two Combo boxes.
The User can summon each User Control through the Main Form's combo box and check the check boxes and/or modify the combo boxes inside each User Control
Once this is done, there is a button, which on being pressed, executes the following code. This code is supposed to check which check boxes have been checked from every User Control, and execute some functionality :
private void button1_Click(object sender, EventArgs e)
{
foreach (Control c in this.Controls)
{
if (c is UserControl)
{
foreach (Control ctl in c.Controls)
{
if (ctl is CheckBox && (ctl as CheckBox).Checked)
{
Indicator.Text = "It's in";
}
}
}
}
//Some other code after this
}
Here, I have included a Text Box called "Indicator" that shows whether the compiler has entered a particular "for" loop or "if" block. And I'm observing that the innermost "if" alone is not getting executed.
Could someone point out why exactly this is happening?
You need a recursive algorithm,
void ProcessControls(Control ctrlContainer)
{
foreach (Control ctrl in ctrlContainer.Controls)
{
if (ctrl is CheckBox && (ctrl as CheckBox).Checked)
{
Indicator.Text = "It's in";
}
if (ctrl.HasChildren)
ProcessControls(ctrl);
}
}
I do think you might be better off adding some functionality to your user control so it can describe the state of its own checkboxes rather than going digging inside it to find it and do logic. Generally in OO programming, when we encapsulate things within a class, we also provide general purpose accessors "visible to the outside" to describe the internal state of affairs, rather than letting external code interests go poking around inside class to find out what they want
At some point in time you've added these usercontrols to the form either directly in the designer, or programmatically. In the first case they will have their own name:
var u1 = usercontrol1.GetCheckboxStateArray();
var u2 = usercontrol2.GetCheckboxStateArray();
Etc
Or maybe you added them programmatically, in which case it would make sense to keep track of them in a list as you're adding them:
protected List<UserControl> _ucList = new List<UserControl>();
...
foreach(var result in somedatabasequery){
var uc = new UserControl(result.Whatever);
this.Controls.Add(uc);
_ucList.Add(uc);
}
Then this list can be iterated. Sure you could argue that "well .Controls is a collection too, so why add them to another list when they're already in an accessible collection" - for the reasons you're here; .Controls is a general purpose description of the hierarchy of all controls on a form, it contains stuff we don't want and is hard to iterate. This List is purely and simply all and only the stuff we're interested in
As an aside, the UI you have described is atypical. The more usual way of hiding and showing controls under the selection of something that holds a bit of text would be a TabControl. It might be easier to loop through too, if you will persist with this "search for UserControls in a collection of controls" method - tabcontrols have tabpages, tabpages would probably have a .Controls that just contains your UserControl. The tabpage intrinsically takes care of showing and hiding controls as pages are clicked on which could simplify your code
Thanks to everyone for the answers. As it happens, the issue was hiding in plain sight, right under my nose. In each of the User Controls, I had placed the Checkboxes and Combo Boxes inside a Group Box. It completely slipped my mind, so much so that I didn't even mention them in my question.
Thus, as #Caius had suggested in the comments, the code wasn't functioning because I had not addressed the Group Box Container holding these Controls. Once I removed the Group Boxes (used only for aesthetic purpose), the code started functioning properly.
I have a WinForms application that has a TextBox control (search box) at the top of it. This TextBox is constantly receiving focus during normal application use, and it is very distracting.
I would like the TextBox to only receive the focus if the user explicitly clicks on it.
I can think of a couple rather complicated ways to accomplish this:
Change an image of a text box into a text box when clicked
Keep track of mouse clicks and shift the focus away based on mouse state
Is there something simpler that I can do to accomplish this?
Edit to add better description of problem based on new understanding
Based on the answers that I have received, I now have a bit of a better understanding of what was causing this problem. As the user interacted with my application, various actions would cause controls to either be disabled or to completely disappear. If one of these controls happened to have the focus at the time, then the next control in the tab order would receive the focus.
I don't know what was the "next control" before I added the text box in question. The application has hundreds of controls on screen at any given time, and I'm pretty sure that tab order was never intentionally defined. Whatever it was before, it was innocuous. After adding the search text box, it seemed like that control would always end up with the focus.
Here is a very simple example that demonstrates what was happening:
public class Form1 : Form
{
public Form1()
{
var button = new Button
{
Location = new System.Drawing.Point(159, 67),
Size = new System.Drawing.Size(75, 23),
TabIndex = 0,
Text = #"Click me"
};
button.Click += (sender, args) => button.Enabled = false;
var textBox = new TextBox
{
Location = new System.Drawing.Point(159, 142),
Name = "textBox1",
Size = new System.Drawing.Size(174, 20),
TabIndex = 1
};
SuspendLayout();
ClientSize = new System.Drawing.Size(486, 392);
Controls.Add(textBox);
Controls.Add(_button);
ResumeLayout(false);
PerformLayout();
}
}
After starting the application, clicking on the button will force the text box to get the focus, since it is the next in the tab order. As mentioned by Handbag Crab in the accepted answer, this behavior can be avoided by setting TabStop = false on the text box.
textBox1.TabStop = false;
The above should stop it receiving focus from tabbing.
Subclass the TextBox and over WndProc function to capture the focus message and handle it. Maybe something like this:
if (m.Msg == WM_MOUSEACTIVATE) {
m.Result = (IntPtr)MA_NOACTIVATEANDEAT;
return;
}
base.WndProc(ref m);
I have a UserControl that is dynamically added to a FlowLayoutPanel. In that same UserControl I have a button to remove itself if the user wants it, obviously at runtime. To eliminate I mean not only to eliminate that tight button, but also the full UserControl that contains the button.
The code of when the UserControl are added dynamically at the moment is as follows:
private void agregaUC() {
UserControl1 UC = new UserControl1();
aux += 1;
UC.Tag = aux.ToString();
flowLayoutPanel2.Controls.Add(UC);
}
The code to eliminate this is on the side of the form, that is, where the UserControl are being added. The button event to remove the UserControl is thrown by code through the operator + =, then there I write the suggestions that you give me.
EDIT: Based on the sample of code you've added, I've modified the below code to work better with what you are looking for. You need to find out how to access the Tag of the control you're trying to remove.
Since you don't have a reference, then you should make sure that the .Tag property can be found, because then you can do something like
foreach (Control c in flowLayoutPanel2.Controls) {
if (c.Tag == "Aux") {
flowLayoutPanel2.Controls.Remove(c);
c.Dispose();
break;
}
}
EDIT
Reading through all the comments everywhere, it seems like this is what's happening. There is a UserControl, inside that user control is a Button (Delete) and the button's Click event is subscribed to by the window, and it's in this event handler that we're trying to remove the UserControl from flowLayoutPanel2
Based on these assumptions, your function should look like this:
void UserControl_Delete_Click(object sender, EventArgs e)
{
Button Delete = (Button)sender;
UserControl UC = (UserControl)Delete.Parent;
flowLayoutControl2.Controls.Remove(UC);
UC.Dispose();
}
This is assuming a lot about the internal structure of everything, as I don't have the code to confirm this will work. It will get you a long ways down the path, though, and should only need a little tweaking based on the actual structure of the UserControl.
You can try something like that.
this.Parent.Controls.Remove(this);
Control.Parent Property.
Remark: Setting the Parent property value to null removes the control from the Control.ControlCollection of its current parent control.
So
this.Parent = null;
Edit
The code is intended to be called from within the user control itself.
I made a Tic Tac Toe game and I'm trying to add a few features.
I was used to handle the taken buttons with
button.enabled=false;
The problems is that the text on the buttons turns grey.
So I made a button click for each button:
A1_Click, A2_Click, and so on
This is my code into A1_click, and it's the same for the other buttons, the only thing that changes is "A1", "A2", and so on
Button b = (Button)sender;
if (!A1.Text.Equals(""))
{
MessageBox.Show("Not A Valid Input");
}
I get "not a valid input" when I click a button I've already clicked before, I'd just like to be able to click an another button.
I don't want to lose my turn if I click on an already taken button
Based on what I have read I think I know what you are looking for. You are looking for a way to force the user to lose a turn when they click a button that has already been taken, and you do not want to set the enabled property to false. If that is what you want, then I might have some code that could help. First if you are assigning the same click event to multiple buttons with different functions you should try something like this:
for (int i = 1; i < 10; i++)
{
string currentButtonName = "A" + i;
Control currentButton = this.Controls.Find(currentButtonName, true).FirstOrDefault();
currentButton.Click += OnGameButton_Click;
}
What this is doing is searching your form for a control that has a specified name, and since your buttons have a similar name we can easily search for them. Then we can bind a specific function to all of them so that instead of 9 functions to modify you only have 1, and you can validate that they all work the same. Here is the OnGameButton_Click() event code:
private void OnGameButton_Click(object sender, EventArgs e)
{
if (!hasGameStarted || shouldLoseTurn)
return;
// This is the current button user pressed
Button b = (sender as Button);
if (b.Name.Contains('A') && b.Enabled)
{
if (!b.Text.Equals(""))
{
MessageBox.Show("Not A Valid Input! You have lost your turn.");
shouldLoseTurn = true;
}
else
{
b.Text = currentPlayersLetter;
shouldLoseTurn = false;
}
}
}
As you can see with some flags we can monitor the game, and force the buttons to react accordingly take a look at the beginning of the function. We have to validate that the game is engaged, and that the current user has not lost their turn due to pressing the same button. From there we just need modify what you had so that if they do press the same button twice then we modify the shouldLoseTurn flag as needed.
The other approach is to just simply use the Button.Enabled property to disable the button from use. I know you do not what the button to be grayed out, but if you create your own style guide for the button you could make it how you want. This can be challenging though because you will have to modify the default style template for the button to achieve this. Here is another question that discusses just that here
I have a Windows Form that contains only buttons. The final goal is to make a simple logic game I saw but for now the problem is that I want to perform different actions when my New button is clicked, but now it is part from all the buttons in the form so sometimes an action is performed on him too which should not happen. To make myself clear I have two screenshots :
So this is how I want it to be - I have a matrix - 3x3 (in this case, at the end it can be NxN). By clicking New I want to be able to do various things one of which is to make N buttons colored red. What happens now is sometimes my New button also get painted because I go over the buttons like this:
foreach (Control c in this.Controls)
{
if (c is Button)
{
...
and thus sometimes New get selected too, so I end up with this:
What I'm thinking right now is just to perform check whenever I need in the code and exclude my New button explicitly but I don't think it's a good way cause I may end up with a code doing this thing in a lot of places in my program so what is the right solution in this case? If some code is needed please ask.
Quite possibly the easiest solution is to put the Grid in its own Panel (pnlGrid). Put all of the buttons in there, then you could just do the following instead:
foreach (Control ctl in pnlGrid.Controls) {
if (ctl is Button) {
// Do your logic here
}
}
Instead of looping through controls, add all the matrix buttons to a list, and have the new button separated:
private Button[] buttons;
private Button newButton;
Now you can add as many buttons as you'd like to:
for (int i = 0; i < 9; i++)
{
buttons[i] = new Button();
buttons[i].Text = "Button" + i;
Controls.Add(buttons[i])
}
And lastly, your New button will loop through buttons:
private void newButton_Click(object sender, EventArgs e)
{
foreach (Button b in buttons)
{
...
}
}
You could inherit from the button class. Make your own button, use this control (that will have the same functionality that the parent one) for the set, and check for it when you iterate over the controls.
You could also use the Tag property for this pourpose, but I think that inherit will be more clear, adding semantic meaning to your code.