Sorting enum array more efficiently - c#

Currently I have some buttons on my Winform that need to be disabled/enabled at various points depending what the user clicks.
The first draft I made was
button1.Enabled = false;
button2.Enabled = false;
To disable 2 buttons, which is obviously a horrible way of doing this, as there are currently a lot more than 2 and possibly more to come as this is still in development. So I need to have a way of easily changing a selection of buttons on the form.
Then I came up with this
private enum Buttons { Button1, Button2 } // etc with all buttons - that are named :)
private void DisableButtons(params Buttons[] buttons)
{
foreach (Buttons button in buttons)
{
switch (button)
{
case Buttons.Button1:
button1.Enabled = false;
break;
case Buttons.Button2:
button2.Enabled = false;
break;
}
}
}
Which I still wasn't overly happy with. I could scrap the switch-case and foreach for
private void DisableButtons(params Buttons[] buttons)
{
button1.Enabled = buttons.Contains(Buttons.Button1) ? false : true;
}
for each button but I just think there must be a better way.
Any ideas on how I could do this more efficiently?
Thanks

You can shorten your last code line to:
button1.Enabled = !buttons.Contains(Buttons.Button1);
Alternative solution
Or you can use the Tag property of each button to set a enum value for each button.
button1.Tag = Buttons.Button1;
button2.Tag = Buttons.Button2;
button3.Tag = Buttons.Button3;
// etc
Than you can do it for all buttons in a for loop:
var buttons = <all buttons, todo>
foreach (var button in buttons) {
button.Enabled = !button.Contains((Buttons)button.Tag));
}

I would suggest that you don't infact want to make a function that flexibly enables and disables any combination of buttons because you don't yet percieve how your Form will work. Granted, this may save you a few lines of code but, it won't impart any contextual information to the next developer that maintians your code. Neither will it run faster than directly setting the state of the controls.
I woud make a single function that is called whenever your Form changes state, that takes all possible parameters that pertain to your Forms state. Then I would decode those parameters and explicitly setup the state of the controls on the form, by name, in a single pass, using tranditional switch and if statments.
This central function will make it clear to you and future developers how the state of you form changes and how controls are expected to behave. It won't slow down the performance of you code with an unecessary level of abstraction.

Related

efficient method for enabling/disabling a button which depends on more than two fields

I was making a winforms application, my objective is to set enabled properties of a button in order to enable or disable the button, which is depending upon more than one controls (3 in my case). It can be done by two methods(if other,let me know!)
1. Should I create a different thread for this purpose which will check the properties of all controls in a loop
2. or I should check the required condition every time an event occurs on all 3 controls?
Which is more efficient/convenient to achieve the task?
There's no reason to use a loop for this unless other activity needs to occur at the same time (such as in a fast-paced game). Even then, it's unlikely to be necessary. It's better to let the EventHandler do its thing and share the handler between your controls. This will be particularly simple if you use CheckBoxes, as you are essentially testing three binary conditions. The method will be even simpler, no matter how you approach it:
private void ToggleCheck(object sender, EventArgs e)
{
List<CheckBox> Cons = new List<CheckBox>(){CheckBox1, CheckBox2, CheckBox3};
int score = 0;
foreach(CheckBox cb in Cons)
score += cb.Checked == true ? 1 : 0;
myButton.Enabled = (score >= 2);
}

Edit textbox in parallel to asynchronous updating

So in my project I have a few textBoxes that hold the coordinates of two corners (Latitude & Longitude). The textBoxes are updated by a timer (which gets a value from a server and sets the textBoxes if the value received is different from the current value). Problem is, I want the textBoxes to be available for manual editing; However if I'm in the middle of typing the number and the timer checks the current value he sees that it's a different thing from what the server returned and changes it immediately. Is there a way to check if the textBox is being edited at the moment, or a better way to solve this solution?
code (samples, the code is the same for the two corners):
if (northEastLatitude != double.Parse(neLatTB.Text)) //neLatTB is the textBox
neLatTB.Text = northEastLatitude.ToString();
else //No answer returned from the server so we need to reset the textBoxes
{
northEastLatitude = 0;
northEastLongitude = 0;
if(neLatTB.Text != "0")
neLatTB.Text = northEastLatitude.ToString();
if(neLngTB.Text != "0")
neLngTB.Text = northEastLongitude.ToString();
}
in addition, I have functions for TextChanged events for all of the textBoxes (so that when I set the coordinates manually it uploads them to the server). Is there any way to prevent this function from being called whenever I press the dot key? apparently it calls the event too (marks the ending of the text entering).
It really depends on your design but if you want to use TextBox to show updatable values and also make it possible to edit you have to suppress code from your timer to execute. WinForms TextBox doesn't have an option to show you if text is changing programmatically or by user interaction. You have to somehow make it by yourself.
There is plenty of ways to do that of courde. One way is to use Enter/Leave events to detect when your TextBox gets or loses focus. But there will be a need to click somwhere out from the control after edit.
Another one, and probably desired by you would be using TextChanged event preventing your timer from updating field until text in TextBox will be typed in full. I would do it something like that:
Fisrtly I would declare two bool variables for blocking parts of code from execution:
private bool _isDirty; // used when user types text directly
private bool _suppresTextChanged; // used when timer updates value programmatically
After that I would write TextBox.TextChanged event listener:
private void neLatTBTextChanged(object sender, EventArgs args)
{
if(_suppressTextChanged)
return;
_isDirty = true; // toggle dirty state
if(/* text has good format */)
{
// Upload changes to server
_isDirty = false; // end manual edit mode
}
}
And inside timer method i would set:
_suppresTextChanged = true; // on the beginning
if (northEastLatitude != double.Parse(neLatTB.Text)) //neLatTB is the textBox
neLatTB.Text = northEastLatitude.ToString();
else //No answer returned from the server so we need to reset the textBoxes
{
northEastLatitude = 0;
northEastLongitude = 0;
if(neLatTB.Text != "0")
neLatTB.Text = northEastLatitude.ToString();
if(neLngTB.Text != "0")
neLngTB.Text = northEastLongitude.ToString();
}
_suppresTextChanged = false; // after edit was made
Personally i think this design can lead to a lot of problems (consider what to do when user stops typing and leave the TextBox in _isDirty state etc...). Instead of using just TextBox I would add a Label to store data from timer (and probably data the user will type) and left TextBox just for entering user specific values.

Control input via button

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

Binding between controls in Windows Forms

Is there a way to make a control dependent on another control? I have a combo box and a button, and I need the button to be enabled if and only if there is an item selected in the combo box.
I know I can set the Enabled property of the button inside the SelectedIndexChanged callback, but then it will require some code, and besides there's an issue with what initial state the button would have. So I'm looking for something that wouldn't require manually handing events, is this possible?
Thanks!
No, there is no way in winforms to do this without code. What I usually do is to collect all such state-setting code into one specific method:
private void SetControlStates()
{
theButton.Enabled = theComboBox.SelectedIndex >= 0;
// code for other controls follow here
}
Then I trigger this method from all over the place, as soon as there is an interaction that may lead to the state changing (including the last thing I do when the form has finished loading; that takes care of initial state). If you want to avoid unnecessary assignments, just add code to check the value first:
private void SetControlStates()
{
bool buttonEnabled = theComboBox.SelectedIndex >= 0;
if (theButton.Enabled != buttonEnabled) theButton.Enabled = buttonEnabled;
// code for other controls follow here
}

Ignore Single Click in WPF

I have a user control wherein I would like to do something different in the case of a single click vs. double click. I'm handling mouseLeftButtonDown event as such:
private void cueCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
switch (e.ClickCount)
{
case 1:
{
cueCanvas.Focus();
e.Handled = true;
_mouseStartPoint = Mouse.GetPosition(null);
break;
}
case 2:
{
double pos = Mouse.GetPosition(cueCanvas).X;
TimeSpan time = ConvertFromPosition(pos);
AddNewEvent(time);
e.Handled = true;
break;
}
}
}
The problem is that WPF doesn't really care how many times you've clicked the mouse so I get the first click event even though I'm about to get a second one (in the case of a double-click). Has anyone come up with a way to work around this? I realize I could try to get clever and set some timers such that the first click gets "canceled" in the event that the second one comes in (I realize this is what the framework would be doing anyway). If that's the only answer, does anyone know how we query the system for the appropriate click delay?
To my knowledge, there's really no way to do this. The problem is that you're fighting reality. :-) In reality, there are 2 clicks to a double-click. You want single click to be ignored if quickly followed by a double-click.
You'll have wait a short interval to see if it's followed by a second click. You can query for that interval using the SystemInformation class from WinForms, or just call the Win32 API directly.

Categories