Why is this IndexOf call returning -1? - c#

I understand that IndexOf returns -1 when the parameter isn't found, but this doesn't make sense to me.
I have this code that iterates over all the DevExpress Checkboxes on my form, checks to see what their tag is, and tries to find it in the "filter" parameter that was passed to the method. If the tag is found in the filter, it should check that checkbox. It is always equating to false.
public void FilterChanged(Control.ControlCollection controls, string filter)
{
filter = filter.Replace("[", String.Empty);
filter = filter.Replace("]", String.Empty);
foreach (Control control in controls)
{
if (control is CheckEdit && control.Tag != null)
{
var c = (CheckEdit)control;
var cFilter = c.Tag.ToString();
cFilter = cFilter.Replace("(", String.Empty);
cFilter = cFilter.Replace(")", String.Empty);
if (filter.ToUpper().IndexOf(c.Tag.ToString().ToUpper()) >= 0)
c.Checked = true;
else
c.Checked = false;
}
}
}
I set a breakpoint on my inner IF statement, and in my Immediate Window I entered the following:
filter.ToUpper().IndexOf(c.Tag.ToString().ToUpper()) = -1
filter.ToUpper() = "FILE NOT LIKE '%VERSIONINFO.CS%'"
cFilter.ToUpper() = "FILE NOT LIKE '%VERSIONINFO.CS%'"
Those look like pretty much the exact same thing, so shouldn't it be returning 0?
I cannot use equals because the filter might include multiple clauses, and thus wouldn't be equal.

cFilter.ToUpper() = "FILE NOT LIKE '%VERSIONINFO.CS%'"
Those look like pretty much the exact same thing, so shouldn't it be returning 0?
But you are using c.Tag.ToString() instead of cFilter.ToUpper().
So this should work as expected:
if (filter.ToUpper().IndexOf(cFilter.ToUpper()) >= 0)
c.Checked = true;
else
c.Checked = false;
Note that you should use StringComparison.OrdinalIgnoreCase instead in IndexOf
c.Checked = filter.IndexOf(cFilter, StringComparison.OrdinalIgnoreCase) >= 0;

Related

Refactoring a long list of if/else statements in C#

So I have some code that is pretty ugly, and I want to know if there is a cleaner way to do this. This code is essentially responsible for invoking a selected method for a device. The user will be given a list of available devices, from that chosen device they are given a list of methods (actions the device can perform). Some methods require arguments be gathered by the user, which is where the trickiness begins. In order to actually call on that method I have to invoke it. I can't invoke it unless I have an object array of all the entered arguments with their correct types, but the arguments are ALL gathered as strings from the user. So I have to parse this string and determine what the type of each argument is by comparing it to the type of the parameter at currMethod.GetParameters.
foreach (Control control in panel1.Controls)
{
if (control.GetType().ToString() == "System.Windows.Forms.TextBox")
{
int i = 0;
string paramTypeStr = currMethod.GetParameters()[i].ParameterType.ToString().Replace("&", "");
if (paramTypeStr == "System.Int16")
{
short shortArg = Int16.Parse(control.Text.ToString());
methodArgs.Add(shortArg);
}
else if (paramTypeStr == "System.Int32")
{
int intArg = Int32.Parse(control.Text.ToString());
methodArgs.Add(intArg);
}
else if (paramTypeStr == "System.Double")
{
double doubleArg = Double.Parse(control.Text.ToString());
methodArgs.Add(doubleArg);
}
else if (paramTypeStr == "System.Single")
{
float floatArg = float.Parse(control.Text.ToString());
methodArgs.Add(floatArg);
}
else if (paramTypeStr == "System.Boolean")
{
bool boolArg = bool.Parse(control.Text.ToString());
methodArgs.Add(boolArg);
}
else if (paramTypeStr == "System.Int64")
{
long longArg = long.Parse(control.Text.ToString());
methodArgs.Add(longArg);
}
else if (paramTypeStr == "System.Byte")
{
byte byteArg = byte.Parse(control.Text.ToString());
methodArgs.Add(byteArg);
}
else
{
methodArgs.Add(control.Text.ToString());
}
currMethod.Invoke(DeviceTypeAndInstance.Values.FirstOrDefault(), methodArgs.ToArray());
i += 1;
}
}
If it helps, here is where the arguments are being gathered:
for (int i = 0; i < paramCount; i++)
{
TextBox textBox = new TextBox();
textBox.Name = i.ToString();
Label label = new Label();
//a.Text = (i + 1).ToString();
textBox.Location = new Point(pointX, pointY);
textBox.Width = 100;
label.Location = new Point(pointX + 110, pointY);
label.Text = $"Argument {i}: {currMethodParamArray[i]}"
if (currMethodParamArray[i].ToString() == "System.String profile")
{
textBox.Text = profile;
}
label.Width = 1000;
panel1.Controls.Add(textBox);
panel1.Controls.Add(label);
panel1.AutoScroll = true;
panel1.Show();
pointY += 30;
}
You can make it look much cleaner by using a switch expression (if you're using C# 8.0 and up). Note a switch expression is different from a switch statement.
This is your code as a switch expression:
foreach (Control control in panel1.Controls)
{
if (control.GetType().ToString() != "System.Windows.Forms.TextBox") { continue; }
int i = 0;
string paramTypeStr = currMethod.GetParameters()[i].ParameterType.ToString().Replace("&", "");
dynamic arg = paramTypeStr switch
{
"System.Int16" => Int16.Parse(control.Text.ToString()),
"System.Int32" => Int32.Parse(control.Text.ToString()),
"System.Double" => Double.Parse(control.Text.ToString()),
"System.Single" => float.Parse(control.Text.ToString()),
"System.Boolean" => bool.Parse(control.Text.ToString()),
"System.Int64" => long.Parse(control.Text.ToString()),
"System.Byte" => byte.Parse(control.Text.ToString()),
_ => control.Text.ToString()
};
methodArgs.Add(arg);
i++;
}
You can read more about the switch expression here: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/switch-expression
Generally, when three or more if statements are querying the same variable data, it is good to use a switch statement. You could separate out the long if statements into a separate method, that could also be formatted in the switch expression syntax if your C# version allows it.
If it were possible, the datatypes would not be strings; for example, by including the selections in a ListView or ComboBox as Type objects. You might also consider using the nameof(Int32) syntax in order to use constants instead of strings.
Without a complete reproducible example of what you are attempting, it's difficult to judge how to improve the code without seeing the interface. Generally though, it's better to create a template (for example, a DataTemplate), along with a template selector, instead of generating the UI from the code-behind. This separates the visuals into markup and the logic into code, which is very helpful.
Use a switch case, The switch statement selects a statement list to execute based on a pattern match with a match expression https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/selection-statements
All these if/else-if statements seem like the perfect opportunity to implement a switch.
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/selection-statements#the-switch-statement

How to create a method where multiple controls are false in an "if" statement for C#?

What is the best way to simplify this (via method creation or otherwise):
if ((radioButton1.Checked == false) && (radioButton2.Checked == false) && (radioButton1.Checked == false) && ...more similar controls... && ((radioButton99.Checked == false))
{
MessageBox.Show("Please select an option!);
}
Thank you for your consideration. Apologies for any inconvenience or grievances caused.
You could put all those controls in a List and then check whether any of the controls in the list is checked. This can be done in several ways. Below examples of two of those.
Example using loop:
bool optionSelected = false;
foreach(var control in controls) // the List is in this case called controls
{
if(control.Checked)
{
optionSelected = true;
}
}
// Check the boolean
Example using System.Linq:
if(!controls.Any(c => c.Checked))
{
MessageBox.Show("Please select an option!);
}
You need to add your controls into a public collection to simply iterate them.
if you have a bunch of the same type controls, it's better to put them into a array in your form's constructor :
CheckBox[] MyBoxes = new CheckBox[]{ check01, check02 , ... }
// MyBoxes is filled at Form_Load and it's usable in throughout of the form
bool result = true;
for(int i=0; i<MyBoxes.Length; i++)
{
if (MyBoxes[i].Checked == false)
{ result = false; break; }
}
another solution is to iterate whole controls on the form:
bool result = true;
for(int i=0; i<this.Controls.Count; i++)
{
if (this.Controls[i] is CheckBox)
{
if ((this.Controls[i] as CheckBox).Checked == false)
{ result = false; break; }
}
}
If you have lots of controls (in this case - RadioButtons), then you can use the following trick - Tag property.
To summarize:
1) Set Tag property with the some string to differentiate from other controls (in particular, there may be the case when not all the controls of one type must be processed).
2) Collect these controls with defined string in Tag and process them.
In your particular case, you can set the string ToCheck in Tag and then check whether all RadioButtons are checked:
// Collect controls with defined string in Tag property
var radioButtons = this.Controls
.OfType<RadioButton>() //Filter controls by type
.Where(rb => ((string)rb.Tag) == "ToCheck"); //Get controls with defined string in Tag
// Check whether all RadioButtons are checked
bool allChecked = radioButtons.All(rb => rb.Checked);
The possible approach could be to introduce variable for condition validation
bool optionIsSelected = ((radioButton1.Checked == true) || (radioButton2.Checked == true)...;
And then use it in if:
if (!optionIsSelected)
{
MessageBox.Show("Please select an option!);
}
private bool AreAllFalse(params bool[] thingsToCheck)
{
return things.All(t => !t);
}
Usage
if (AreAllFalse(radioButton1.Checked,radiobutton2.Checked, etc)
{
//Do what you want
}
The benefit of using the params key word is that it creates the array for you when you call the method. It also allows you to pass in just one thing to check if you want. While that would be quite pointless here, it just keeps things felxible and is something worthwhile knowing

is there a way to set a variable to something at the end of foreach loop if condition not met in C#?

foreach (Objecta a in aList())
{
foreach (Objectb b in bList)
{
if (a.variable != b.variable1 && a.variable() != b.variable2)
{
a.setVariable("Error");
}
}
}
The problem I am getting is that it goes through the foreach loop the first time and it sets variable to error without checking if other values (when it goes through the loop again) finds a match.
What I would like is to wait until it goes through all the lists and at the last foreach loop iteration if nothing in aList matches the variable target && variable source in bList then finally set it to Error flag.
Any suggestions to get around this will be appreciated.
Try doing it the other way around. Search for a match instead of searching for non-matches.
foreach (Objecta a in aList())
{
bool foundMatch = false;
foreach (Objectb b in bList)
{
if (a.variable == b.variable1 || a.variable() == b.variable2)
{
foundMatch = true;
break;
}
}
if (!foundMatch)
{
a.setVariable("Error");
}
}
I think this is what you are looking for. So if StoreList is the outer loop and LinkList is the inner loop. You want to search all the links to see if there's an ID that matches the store ID. If you find a match, stop searching the links. After the search through the links, set an error on the store if there was no match, then go to the next store.
foreach (Objecta a in aList())
{
var foundMatch = false;
foreach (Objectb b in bList)
{
if (a.variable == b.variable1 || a.variable() == b.variable2)
{
fondMatch = true;
break;
}
}
if (!foundMatch) a.setVariable("Error");
}
I think you want something like this:
First select all the item values from aList and bList and put them in a seperate array:
var aVals = aList.Select(x=>x.value1).ToArray();
var bListVals1 = bItems.Select(x=>x.value1).ToArray();
var bListVals2 = bItems.Select(x=>x.value2).ToArray();
var bVals = bListVals1.Concat(bListVals2);
Then, get the values both lists have in common:
var correctVals = bVals.Intersect(aVals);
These are the correct values and so all the other values are wrong:
var wrongVals = aVals.Except(correctVals);
Now you have the values that are wrong and can act accordingly:
wrongAItems = aList.Where(a => wrongVals.Contains(a.value));
foreach(wrongA in wrongAItems){
wrongA.setVariable("Error");
}
foreach (Store s in processFlowStores.getStoresList())
{
if (!processFlowLinks.Any(l => s.getNodeId() == l.getLinkSource() ||
s.getNodeId() == l.getLinkTarget()))
{
s.setID("Error: FailedOperation Error - 123.123.121");
}
}
EDIT: more compact solution using Linq. Basically, if none of the links has it as either source or target, mark it as error.

How to make a checkbox disabled without using any specific condition?

I have a checkbox whose value is handled from the DB on the basis of the column isAllow. We have 5 names and corresponding to these name we have diff isAllow property. If the value of isAllow is true then the checkbox get checked.
For four names, I have to make the checkbox as checked, but for one specific name out of 5, I have to mark the checkbox as disabled as well. I can achieve this by adding a condition as:
if(name=="Josh" && isAllow)
{
chkBrand.Enable=false;
chkBrand.Checked=true;
}
I dont want to use the above condition here. I just want to get the checkbox as disable for a specific name without the condition.
Below is the code that I have used:
bool isAllow = false;
bool SigCard = false;
List<AOTLuAffinity> oAotluAffinity = bsAffinity.DataSource as List<AOTLuAffinity>;
foreach (AOTLuAffinity oAffinity in oAotluAffinity)
{
if (oAffinity.Name == comboName.SelectedValue.ToString())
{
isAllow = oAffinity.isAllowName;
signCard = AOTHelper.GetHasSigCardValue(workflowid, Acct.AffinityNum, comboPaBranch.SelectedValue.ToString(), Applicants.AffinityNum, isSignCard, isAllow );
if (isAllow)
{
chkBrand.Checked=true;// here for a specific name, I want to make the checkbox as disabled, but I dont want to use the condition.
}
else
{
chkBrand.Checked=false;
}
break;
}
}
I am finding a hard time in getting this. Any help will be great.
I think the right approach here is to add a new field to the database, isDisabled, and then in your loop evaluate that field:
...
else
{
chkBrand.Checked=false;
}
if (oAffinity.isDisabled)
{
chkBrand.Enabled = false;
}
break;
You can use LINQ conditions to check specific names on a list. You must declare a list like this:
List<Person> peopleList = new List<Person>();
And here is your condition code:
foreach (AOTLuAffinity oAffinity in oAotluAffinity)
{
if (oAffinity.Name == comboName.SelectedValue.ToString())
{
isAllow = oAffinity.isAllowName;
signCard = AOTHelper.GetHasSigCardValue(workflowid, Acct.AffinityNum, comboPaBranch.SelectedValue.ToString(), Applicants.AffinityNum, isSignCard, isAllow );
chkBrand.Checked =
peopleList
.Count(c => c.Name == comboName.SelectedValue.ToString()) > 0 ? true : false;
break;
}
}
Loop on the items you got from database and each time read value (isAllow),then
chkBrand.Checked=isAllow;

Use a numeric value in a linq dynamic query string

I am trying to make a dynamic linq query that will check for values based on a string.
First of all, here's the query:
objQry = from o in m_Db.OBJECTS.Where(whereConditions)
select o;
if(!objQry.Any())
{
return null;
}
The whereConditions variable is a string I build and pass as parameter to find out the values I need. Here's examples of valid string:
OBJ_NAME == \"Sword\" and OBJ_OWNER == \"Stan\"
This will return any item whose name is "Sword" and owner is "Stan;
OBJ_COLOR == \"Blue\" OR OBJ_COLOR == \"Red\"
This will return any item which color is either blue or red.
Up to there, I'm fine, but now I have a problem: I need to check a decimal field. So I've tried this string:
OBJ_NUMBER == 1
But the query returns null even if there are objects which OBJ_NUMBER value is 1. It's a decimal. How can I indicate the query that they need to check for a decimal value?
**** EDIT ****
I have tried to "modify" the value passed so that it looks like this:
"CARD_NUMBER == Convert.ToDecimal(1)"
And now I have a different kind of error telling me this:
LINQ to Entities does not recognize the method 'System.Decimal ToDecimal(Int32)' method, and this method cannot be translated into a store expression.
Any clues anyone? I'm still looking for a way to do this. Thanks!
EDIT 2
You can get an example of how my code is shaped by looking at this question.
Let's come back at this problem. I want to check decimal values. Let's say that OBJ_NUMBER is a decimal field.
Using Dynamic Linq, I tried to read the decimal field. Say that I want to get each object which number is 1.27. The whereConditions field would then be shaped like this:
OBJ_NUMBER == 1.27
But then I would get an Invalid real literal '1.27' error. I don't know why.
So I have tried Gert Arnold's solution and done this instead:
decimal bDecimal = decimal.Parce(valueToParse);
param = new ObjectParameter("cardNumber", typeof(decimal)) { Value = bDecimal };
valuesToUse.Add("CARD_NUMBER == #cardNumber");
listParams.Add(param);
But I ended up having 2 problems:
The first problem is that my whereConditions string is shaped this way:
CARD_NUMBER == #cardNumber
But I get the following error:
No property or field 'cardNumber' exists in type 'CARD'
Leading me to believe that it cannot make the link between the object parameter and the string used to do the query.
As you can see, I have a list of Params. This is because I cannot know for sure how many parameters the user will chose. So each time the user enters a new search field, I have to create a new ObjectParameter and store it in a list. Here's how I try to do the thing after:
ObjectParameter[] arrayParameters = listParams.ToArray();
// Convert the list to an array
And then, when I try to make the query:
cardQry = from c in mDb.CARD.Where(whereConditions, arrayParameters)
select c;
But to no avail.
RESULTS
Based on the answered question below, I have developped something "awful", yet functional.
First of all, I ignore every decimal fields because I could never reach them with dynamic linq. Instead, I do this:
var valuesToParse = keyValuePair.Value.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
// Here I parse the value and, if that's the case, the symbol.
decimal baseValue = decimal.Parse(valuesToParse[0]);
if (valuesToParse.Count() > 1)
{
string baseMethod = valuesToParse[1];
if (baseMethod == ">" || baseMethod == ">=")
{
if (baseMethod == ">=")
{
baseValue--;
}
// The list is actually like this: Dictionary<string, object> list = new Dictionary<string, object>();
list.Add("low", baseValue);
// I kind of activate a tag telling me that the user is looking for a higher value.
cardHigher = true;
}
else
{
if (baseMethod == "<=")
{
baseValue++;
}
list.Add("low", baseValue);
cardLower = true;
}
}
else
{
//lowParam = new ObjectParameter("dec", typeof(decimal)) { Value = baseValue };
list.Add("low", baseValue);
}
cardNumberActivated = true;
At the end, when I get the list of objects, I do this:
if (list.Count > 0)
{
(example)
if (cardNumberActivated)
{
if (cardHigher)
{
q = mDb.CARD.Where("CARD_NUMBER >= #0", list["low"]).ToList();
}
else if (cardLower)
{
q = mDb.CARD.Where("CARD_NUMBER <= #0", list["low"]).ToList();
}
else
{
q = mDb.CARD.Where("CARD_NUMBER == #0", list["low"]).ToList();
}
}
}
// Here we get the orinalData with the basic filters.
listToReturn.AddRange(cardQry);
if (q != null)
{
//listToReturn.AddRange(q);
for (int i = 0; i < listToReturn.Count; i++)
{
var priceList1 = listToReturn[i];
if (!q.Any(_item => _item.CARD_NUMBER == priceList1.CARD_NUMBER))
{
listToReturn.RemoveAt(i);
i--;
}
}
}
And it works. This is not an elegant way to make it work, but I can validate the fields the way I wanted, and for this, I am thankful at last.
You should not build a query string with inline predicate values. Use parameters in stead. Then will also be able to specify the type:
var whereConditions= "it.CARD_NUMBER = #cardNumber";
var param = new ObjectParameter("cardNumber", typeof(decimal)) { Value = 1 };
objQry = from o in m_Db.OBJECTS.Where(whereConditions, param);
Edit
I don't know what doesn't work in your code. Here's just a random piece of working code derived from one of my own projects:
var param1 = new ObjectParameter("dec", typeof(decimal)) { Value = 90000m };
var param2 = new ObjectParameter("int", typeof(int)) { Value = 90000 };
var q = ValueHolders.Where("it.DecimalValue >= #dec OR it.IntegerValue > #int",
param1, param2).ToList();
Note that param1, param2 could also be an array of ObjectParameter.

Categories

Resources