ShortcutKey from string - c#

I want to provider user with option to set ToolStripMenuItem.ShortcutKeys via configuration file, so I've figured out that I need somehow transform string to Keys.
I've already found how to do that for simple values using Enum.Parse, but it won't recognize formats like:
Ctrl+i (with space at the end)
i
Ctrl+Alt+Esc
Q: Is there any standardized way of parsing stings (Ctrl+i) to Keys?
I'd like to avoid writing my own function that will split text into pieces and then handle each case/special string separately.

The string you see in the Properties window for the ShortcutKeys property is generated by a TypeConverter. You can use that type converter in your own code as well. Like this:
var txt = "Ctrl+I";
var cvt = new KeysConverter();
var key = (Keys)cvt.ConvertFrom(txt);
System.Diagnostics.Debug.Assert(key == (Keys.Control | Keys.I));
Do beware that I or Ctrl+Alt+Escape are not valid short-cut keys. KeysConverter will not complain, you'll get the exception when you assign the ShortCutKeys property. Wrap both with try/except to catch invalid config data.

Yes, you can separate the values with a , - however your string would still need massaged because Ctrl and Esc are not valid Keys values. They still need to be valid values. So consider the following code:
var keys = "Ctrl+Alt+Esc";
keys = keys.Replace("+", ",").Replace("Ctrl", "Control").Replace("Esc", "Escape");
var k = (Keys)Enum.Parse(typeof(Keys), keys);
Further, here is the code in the Enum class that's going to pick that up:
string[] array = value.Split(Enum.enumSeperatorCharArray);
ulong[] array2;
string[] array3;
Enum.GetCachedValuesAndNames(runtimeType, out array2, out array3, true, true);
for (int i = 0; i < array.Length; i++)
{
array[i] = array[i].Trim();
bool flag = false;
int j = 0;
while (j < array3.Length)
{
if (ignoreCase)
{
if (string.Compare(array3[j], array[i], StringComparison.OrdinalIgnoreCase) == 0)
{
goto IL_152;
}
}
else
{
if (array3[j].Equals(array[i]))
{
goto IL_152;
}
}
j++;
continue;
IL_152:
ulong num2 = array2[j];
num |= num2;
flag = true;
break;
}
if (!flag)
{
parseResult.SetFailure(Enum.ParseFailureKind.ArgumentWithParameter, "Arg_EnumValueNotFound", value);
return false;
}
}
The value of enumSeperatorCharArray is:
private static readonly char[] enumSeperatorCharArray = new char[]
{
','
};

Related

Input string not in correct format exception c#

I have read data from a text file. I want to change data in that file into an Int array. I don't know why it wrong.
class Program
{
public static int[,] provincial = new int[100, 100];
public static void loadProvincial()
{
string[] s = System.IO.File.ReadAllLines("C:\\Users\\Lyn203\\Desktop\\data\\final-graph.txt");
for (int i = 0; i < s.Length; ++i)
{
string[] splitedArray = s[i].Replace("\t","_").Split('_');
//Console.WriteLine(splitedArray[i]);
for (int j = 0; j < splitedArray.Length-1; ++j)
{
provincial[i,j] = int.Parse(splitedArray[j].ToString());
}
}
Console.ReadLine();
}
static void Main(string[] args)
{
loadProvincial();
}
}
and TextFile:
http://textuploader.com/djhbe
I suggest you to call the overload of Split that removes the empty entries. In other words, if you have two consecutive tabs without a value between the two tabs your result will contain an empty string. That string is not convertible by Int32.Parse
string[] splitedArray = s[i].Replace("\t","_")
.Split(new char[] {'_'},
StringSplitOptions.RemoveEmptyEntries);
Instead if you want to add a zero when your code meets an empty string then replace the Int32.Parse with Int32.TryParse. This will allow you to check the result of the conversion without getting an exception in case of a missing value
for(.....)
{
int value;
Int32.TryParse(splitedArray[j], out value);
// if conversion fails, value will be set to the default value for integers (0)
provincial[i,j] = value;
}

Change values of dictionary C#

I create a dictionary (Dictionary<string, bool> values).
I create the function:
private Dictionary<string, bool> ChangeValues(int line, Dictionary<string, bool> values)
{
for (int i = 0; i < line; i++)
{
}
return values;
}
I want to change bool values according the number of line.
The table below is the result of a dictionary (only show the bool):
line 0 = values["A"][false]
values["B"][false]
values["C"][false]
line 1 = values["A"][false]
values["B"][false]
values["C"][true]
line 2 = values["A"][false]
values["B"][true]
values["C"][false]
line n...
How I can change the bool value according the number of line?
Thanks.
[update]
After some help, the function is done:
private Dictionary<string, bool> ChangeValues(int line, Dictionary<string, bool> values)
{
string[] keys = values.Keys.ToArray();
keys = (from k in keys orderby k descending select k).ToArray();
for (int i = 0; i < line; i++)
{
for (int j = 0, bit = 1; j < values.Count; j++, bit <<= 1)
{
values[keys[j]] = (i & bit) != 0;
}
}
return values;
}
Thanks to PeterDuniho
If I understand the question correctly, all you want is to be able to iterate all possible combinations of the flags stored in your dictionary. In addition, these flags correspond exactly to the bits in your loop variable i.
If so, then you can just use the bits from the loop variable directly to set the flag values in the dictionary. For a general solution, you do need to pass the key names as well, in the correct order according to the bits you want to set.
For example:
private Dictionary<string, bool> ChangeValues(int line, Dictionary<string, bool> values, string[] keys)
{
for (int i = 0; i < line; i++)
{
for (int j = 0, bit = 1; j < keys.Length; j++, bit <<= 1)
{
values[keys[j]] = (i & bit) != 0;
}
// do something with values here
}
return values;
}
Call like this:
var result = ChangeValues(8, values, new [] { "C", "B", "A" });
Note that the above simply modifies the dictionary that was passed in. I infer from the way the question was asked — "I want to change bool values" — that this was your intent. However, if you actually want to return a whole new dictionary object, you can do so:
private Dictionary<string, bool> ChangeValues(int line, string[] keys)
{
Dictionary<string, bool> values = new Dictionary<string, bool>();
for (int i = 0; i < line; i++)
{
for (int j = 0, bit = 1; j < keys.Length; j++, bit <<= 1)
{
values[keys[j]] = (i & bit) != 0;
}
// do something with values here
}
return values;
}
Of course, in that case the values of the original dictionary are of no use, so there's no need to pass it in, so I've left it out of the parameter list for this version of the method.
If in fact you want to return a new dictionary but still need the old one for some reason (e.g. you want to do something with it, like copying values or something, which you failed to mention in your question), then you can combine the two examples above to suit your needs.
Do you mean like this:
static Dictionary<string, bool> ChangeValues(int line, Dictionary<string, bool> values)
{
switch (line)
{
case 0:
values["A"] = false;
values["B"] = false;
values["C"] = false;
break;
case 1:
values["A"] = false;
values["B"] = false;
values["C"] = true;
break;
case 2:
values["A"] = false;
values["B"] = true;
values["C"] = false;
break;
}
return values;
}
Note: You can also have void return type (as in static void ChangeValues) and omit the return values; statement since you always return the same instance anyway, but that depends on whether you want a "fluent syntax" or not.
After comments, try this:
static Dictionary<string, bool> ChangeValues(int line, Dictionary<string, bool> values)
{
var binary = Convert.ToString(line, 2);
var key = 'A';
foreach (var c in binary)
{
values[key.ToString()] = c == '1';
key++;
}
return values;
}
The comment about void is the same as before.

I stumbled upon problems with controll testing string in my Mastermind application

well first of all I'm quite new to programing so please keep that in mind. I'm currently working on a MasterMind game, done in C# console application. I stumbled upon a problem while testing my diffrent methods. I wrote all test methods responds in the system.debug and the method that I've recived a fault from is the method "isValidKey(string key)"
I've got a "true" response form the "createSecretKey()" and the MasterMind constructor;
So I assume they are fine.
So if you could please help me find whats wrong with the "isVaildKey(string key" method I would really appreciate it.
So here is my code:
namespace MasterMind
{
class MasterMindModel
{
private string _secretKey;
public MasterMindModel()
{
_secretKey = createSecretKey();
}
private static string createSecretKey()
{
string secretKey;
string[] t_secretKey = new string[4];
Random rnd = new Random();
string symbols = "1234567";
for (int i = 0; i < 4; i++)
{
char ch = symbols[rnd.Next(symbols.Length)];
t_secretKey[i] += ch;
}
secretKey = t_secretKey[0] + t_secretKey [1] + t_secretKey[2] + t_secretKey[3];
return secretKey;
}
public static bool SelfTest()
{
bool ok = true;
for (int i = 0; i < 1000 && ok; ++i)
{
ok = isValidKey(createSecretKey());
}
System.Diagnostics.Debug.WriteLine("CreateSecretKey: " + ok);
MasterMindModel model = new MasterMindModel();
ok = isValidKey(model._secretKey);
System.Diagnostics.Debug.WriteLine("MasterMindModel constructor: " + ok);
ok = isValidKey("2361") && !isValidKey("2368")
&& !isValidKey("ABCD") && !isValidKey("+-*=")
&& !isValidKey("2301") && !isValidKey("23611")
&& !isValidKey("231");
System.Diagnostics.Debug.WriteLine("MasterMindModel isValidKey " + ok);
return ok;
}
public static bool isValidKey(string key)
{
Random rnd = new Random();
string symbols = "1234567";
for (int i = 0; i < 4; i++)
{
char ch = symbols[rnd.Next(symbols.Length)];
key += ch;
}
if (key != "2368" && key != "ABCD" && key != "+-*=" && key != "2301" && key != "23611" && key != "231")
{
key = t_key[0] + t_key[1] + t_key[2] + t_key[3];
}
return true;
}
}
}
The compiler don't give me any errors and I can't seem too figure out what the problem is.
As I said I'm quite new, so any tips on what I should research and what syntaxes I should read more about etc. I would also apprieciate it.
Also please tell me if this post is to vauge and I will delete it and make another one. It's my first post here.
Thanks.
The immediate issue I see in your method is that t_key is not defined anywhere, can't explain why compiler isn't complaining unless you are not showing all the code.
You are mixing test cases in both SelfTest and isValidKey functions. You want to keep them in one place. Also you want to make your code more flexible by not using hard coded values so much. Also don't use static if not needed, you might want to run several instances of your model at once. Below is how I would do what you are trying to achieve:
class MasterMindModel
{
private string createSecretKey(int keyLength, List<char> keyItems)
{
StringBuilder secretKey = new StringBuilder();
for (int i = 0; i < keyLength; i++)
{
secretKey.Append(keyItems[_rnd.Next(keyItems.Count)]);
}
return secretKey.ToString();
}
private bool isValidKey(string key, int keyLength, List<char> keyItems)
{
if (key.Length != keyLength)
{
return false;
}
foreach (char c in key)
{
if (!keyItems.Contains(c))
{
return false;
}
}
return true;
}
public void SelfTest()
{
int keyLength = 4;
List<char> keyItems = new List<char> { '1', '2', '3', '4', '5', '6', '7' };
Debug.Assert(isValidKey(createSecretKey(keyLength, keyItems), keyLength, keyItems));
Debug.Assert(isValidKey(createSecretKey(keyLength, keyItems), 3, keyItems) == false);
Debug.Assert(isValidKey(createSecretKey(keyLength, keyItems), keyLength, new List<char> { '0', '8', '9' }) == false);
}
private static Random _rnd = new Random();
}

Sorting objects using IComparable

I am trying to implement the IComparable interface in my custom object so that List.Sort() can sort them alphabetically.
My object has a field called _name which is a string type, and I want it to sort based on that. Here is the method I implemented:
public int CompareTo(object obj)
{
//Int reference table:
//1 or greater means the current instance occurs after obj
//0 means both elements occur in the same position
//-1 or less means the current instance occurs before obj
if (obj == null)
return 1;
Upgrade otherUpgrade = obj as Upgrade;
if (otherUpgrade != null)
return _name.CompareTo(otherUpgrade.Name);
else
throw new ArgumentException("Passed object is not an Upgrade.");
}
Not sure if I did something wrong or if it's just the way the string CompareTo works, but basically my List was sorted like this:
Test Upgrade
Test Upgrade 10
Test Upgrade 11
Test Upgrade 12
Test Upgrade 13
Test Upgrade 14
Test Upgrade 15
Test Upgrade 2
Test Upgrade 3
Test Upgrade 4
Test Upgrade 5
I want them to be sorted like this:
Test Upgrade
Test Upgrade 2
Test Upgrade 3
...etc
You want "natural order" -- the collation that a human who is familiar with the conventions of English would choose -- as opposed to what you've got, which is "lexicographic" collation: assign every letter a strict ordering, and then sort by each letter in turn.
Jeff has a good article on some of the ins and outs here, with links to different algorithms that try to solve the problem:
http://www.codinghorror.com/blog/2007/12/sorting-for-humans-natural-sort-order.html
and Raymond discussed how Windows dealt with it here:
http://technet.microsoft.com/en-us/magazine/hh475812.aspx
Basically the problem is: natural order collation requires solving an artificial intelligence problem; you're trying to emulate what a human would do, and that can be surprisingly tricky. Good luck!
Strings are sorted in lexicographic order. You'll have to either format all your numbers to have the same length (eg: Test Upgrade 02) or parse the number in your comparer and incorporate it in your comparison logic.
The reason this happens is that you are doing string comparison, which has no explicit knowledge of numbers. It orders each string by the respective character codes of each character.
To get the effect you want will require a bit more work. See this question: Sort on a string that may contain a number
AlphaNumeric Sorting
public class AlphanumComparatorFast : IComparer
{
public int Compare(object x, object y)
{
string s1 = x as string;
if (s1 == null)
{
return 0;
}
string s2 = y as string;
if (s2 == null)
{
return 0;
}
int len1 = s1.Length;
int len2 = s2.Length;
int marker1 = 0;
int marker2 = 0;
// Walk through two the strings with two markers.
while (marker1 < len1 && marker2 < len2)
{
char ch1 = s1[marker1];
char ch2 = s2[marker2];
// Some buffers we can build up characters in for each chunk.
char[] space1 = new char[len1];
int loc1 = 0;
char[] space2 = new char[len2];
int loc2 = 0;
// Walk through all following characters that are digits or
// characters in BOTH strings starting at the appropriate marker.
// Collect char arrays.
do
{
space1[loc1++] = ch1;
marker1++;
if (marker1 < len1)
{
ch1 = s1[marker1];
}
else
{
break;
}
} while (char.IsDigit(ch1) == char.IsDigit(space1[0]));
do
{
space2[loc2++] = ch2;
marker2++;
if (marker2 < len2)
{
ch2 = s2[marker2];
}
else
{
break;
}
} while (char.IsDigit(ch2) == char.IsDigit(space2[0]));
// If we have collected numbers, compare them numerically.
// Otherwise, if we have strings, compare them alphabetically.
string str1 = new string(space1);
string str2 = new string(space2);
int result;
if (char.IsDigit(space1[0]) && char.IsDigit(space2[0]))
{
int thisNumericChunk = int.Parse(str1);
int thatNumericChunk = int.Parse(str2);
result = thisNumericChunk.CompareTo(thatNumericChunk);
}
else
{
result = str1.CompareTo(str2);
}
if (result != 0)
{
return result;
}
}
return len1 - len2;
}
}
Usage :
using System;
using System.Collections;
class Program
{
static void Main()
{
string[] highways = new string[]
{
"100F",
"50F",
"SR100",
"SR9"
};
//
// We want to sort a string array called highways in an
// alphanumeric way. Call the static Array.Sort method.
//
Array.Sort(highways, new AlphanumComparatorFast());
//
// Display the results
//
foreach (string h in highways)
{
Console.WriteLine(h);
}
}
}
Output
50F 100F SR9 SR100
Thanks for all the replies guys. I did my own method and it seems to work fine. It doesn't work for all cases but it works for my scenario. Here is the code for anyone who is interested:
/// <summary>
/// Compares the upgrade to another upgrade
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public int CompareTo(object obj)
{
//Int reference table:
//1 or greater means the current instance occurs after obj
//0 means both elements occur in the same position
//-1 or less means the current instance occurs before obj
if (obj == null)
return 1;
Upgrade otherUpgrade = obj as Upgrade;
if (otherUpgrade != null)
{
//Split strings into arrays
string[] splitStringOne = _name.Split(new char[] { ' ' });
string[] splitStringTwo = otherUpgrade.Name.Split(new char[] { ' ' });
//Will hold checks to see which comparer will be used
bool sameWords = false, sameLength = false, bothInt = false;
//Will hold the last part of the string if it is an int
int intOne = 0, intTwo = 0;
//Check if they have the same length
sameLength = (splitStringOne.Length == splitStringTwo.Length);
if (sameLength)
{
//Check to see if they both end in an int
bothInt = (int.TryParse(splitStringOne[splitStringOne.Length - 1], out intOne) && int.TryParse(splitStringTwo[splitStringTwo.Length - 1], out intTwo));
if (bothInt)
{
//Check to see if the previous parts of the string are equal
for (int i = 0; i < splitStringOne.Length - 2; i++)
{
sameWords = (splitStringOne[i].ToLower().Equals(splitStringTwo[i].ToLower()));
if (!sameWords)
break;
}
}
}
//If all criteria is met, use the customk comparer
if (sameWords && sameLength && bothInt)
{
if (intOne < intTwo)
return -1;
else if (intOne > intTwo)
return 1;
else //Both equal
return 0;
}
//Else use the default string comparer
else
return _name.CompareTo(otherUpgrade.Name);
}
else
throw new ArgumentException("Passed object is not an Upgrade.");
}
Will work for strings spaced out using a " " character, like so:
Test data:
Hello 11
Hello 2
Hello 13
Result
Hello 2
Hello 11
Hello 13
Will not work for data such as Hello11 and Hello2 since it cannot split them. Not case sensitive.

how to load a comma separated numbers to List<int> in c#

string formIdList = "8256, 8258, 8362, 8120, 8270, 8271, 8272, 8273, 8257, 8279, 8212, 8213, 8214, 8215, 8216, 8217, 8218, 8219, 8231, 8232, 8233, 8234, 8235, 8242, 8248, 8251, 8252, 8254, 8255, 8262, 8263, 8264, 8265, 8266, 8290, 8292, 8293, 8294, 8300, 8320, 8230, 8227, 8226, 8225, 8224, 8223, 8222, 8221, 8291, 8261, 8241, 8228, 8220, 8211, 8208, 8207, 8206, 8205, 8204, 8203, 8202, 8201, 8153, 8151, 8150, 8130, 8122, 8000, 8101, 8102, 8103";
var temp = formIdList.Split(',');
List<int> ids = new List<int>();
I need to load the temp into ids. I can use a for-loop but I'm sure there is a better way.
You could use LINQ:
string formIdList = ...
List<int> ids = formIdList.Split(',').Select(int.Parse).ToList();
List<int> ids = formIdList.Split(',').Select(i=>int.Parse(i)).ToList();
Maybe you should use something like ids.AddRange(temp), did you try it?
Linq is great and all, but you do this with a little less heap-thrashing on your own and have more control over what input you find acceptable. The following will yield the integers from any character enumeration separated by comma's and ignoring all white space.
public static IEnumerable<int> ParseInts(IEnumerable<char> idList)
{
bool valid = false;
int working = 0;
foreach (char c in idList)
{
if (c >= '0' && c <= '9')
{
valid = true;
working = (working*10) + (c - '0');
}
else if (c == ',')
{
if(valid)
yield return working;
valid = false;
working = 0;
}
else if(!Char.IsWhiteSpace(c))
{
throw new ArgumentOutOfRangeException();
}
}
if (valid)
yield return working;
}
Then you can fill your collection of ints easily enough by using the List<int> constructor:
string formIdList = "8256, 8258, 8362";
List<int> ids = new List<int>(ParseInts(formIdList));
Just depends on what you intend to do with this, how often, and how large the input will be. For small arrays parsed infrequently I would use the Linq method you already accepted. For higher volumes you might try this instead.

Categories

Resources