How to implement GetLineStartPosition for a WPF TextBox UI Control? - c#

How to implement the RichTextBox API, GetLineStartPosition, for a WPF TextBox UI Control?
var charpos_x1 = GetLineStartPosition(0); // get charpos start of the current line
var charpos_x2 = GetLineStartPosition(1); // get charpos start of the next line
var charpos_x3 = GetLineStartPosition(-1); // get charpos start of the prev line

A TextPointer is just a reference to a location in the content of the rich text. TextBox doesn’t expose that but you can use an integer representing the offset of the character in the text. So based on the docs for GetLineStartPosition I think something like the following may work:
public int? GetLineStartPosition(this TextBox tb, int charIndex, int count)
{
/// get the line # for character idx then adjust the line
/// number by the specified # of lines
var l = tb.GetLineIndexFromCharacterIndex(charIndex) + count;
/// if its not valid return null
if (l < 0 || l >= tb.LineCount) return null;
/// otherwise return the character index of the start
/// of the adjusted line
return tb.GetCharacterIndexFromLineIndex(l);
}
Then just use it like this:
tb.GetLineStartPosition(tb.CaretIndex, 0);

public struct LinePos_t
{
public int indexz; // Multiline TextBox.Line End-Position
public int index0; // n-Line Begin Position
public int index1; // n-Line End Position
public int len; // n-Line Length
public string str; // n-line String
};
private static LinePos_t TextBoxMultiline_GetLinePosition(
TextBox tb,
int n
)
{
// (n == 0): current Line
// (n == 1): next line
// (n == -1): prev line
// Notes: end of line marked by: "\r\n" for Textbox.Text
// User can never postion Cursor on '\n' character...
// but can position it on \r character
LinePos_t linepos = new LinePos_t();
int anchor;
int repeat;
string c;
tb.Focus();
if (tb.Text.Length == 0)
{
linepos.indexz = 0;
linepos.index0 = 0;
linepos.index1 = 0;
linepos.len = 0;
linepos.str = "";
return linepos;
}
// Position End of Text
linepos.indexz = tb.Text.Length;
if (linepos.indexz != 0)
linepos.indexz--;
// Position Init Carrot
if (tb.CaretIndex > linepos.indexz)
anchor = linepos.indexz;
else
anchor = tb.CaretIndex;
// Rewind when n is negative
if (n < 0)
{
repeat = -n;
for (int i = 0; i < repeat; i++)
{
anchor = tb.Text.LastIndexOfAny(new char[] { '\r' }, anchor);
if (anchor == -1)
{
anchor = 0;
break;
}
}
//Select Current Line
n = 0;
}
repeat = n + 1;
for (int i = 0; i < repeat; i++)
{
linepos.index0 = tb.Text.LastIndexOfAny(new char[] { '\n' }, anchor);
if (linepos.index0 == -1)
{
linepos.index0 = 0;
break;
}
else
{
linepos.index0++;
}
}
linepos.index1 = tb.Text.IndexOfAny(new char[] { '\n' }, anchor);
if (linepos.index1 == -1)
linepos.index1 = linepos.indexz;
// Length of Line
linepos.len = linepos.index1 - linepos.index0 + 1;
linepos.str = tb.Text.Substring(linepos.index0, linepos.len);
return linepos;
}

Related

C# Remove An Empty Character Array

I have character arrays seperated into multiple groups, my code uses a character from every group, so once character in a group is processed it will go to the next. I want to remove a group when it's considered empty. (or alternative if you can help fix the code so it skips empty groups)
In the form, there are checkboxes which add the specific characters to their specified group, you have the option to add those characters of choice or not, which I also have a user include input textbox if they want to add their personal characters, but in a case of a char group being empty I get an exception most likely due to the group having no characters.
Exception:
"System.IndexOutOfRangeException: 'Index was outside the bounds of the
array."
For
"result[i] = charGroups[nextGroupIdx][nextCharIdx];"
char[][] charGroups = new char[][]
{
CapitalCharacterSet.ToCharArray(),
LowercaseCharacterSet.ToCharArray(),
NumbersCharacterSet.ToCharArray(),
IncludeCharacterSet.ToCharArray(),
SpecialCharacterSet.ToCharArray()
};
FULL CODE:
char[][] charGroups = new char[][]
{
CapitalCharacterSet.ToCharArray(),
LowercaseCharacterSet.ToCharArray(),
NumbersCharacterSet.ToCharArray(),
IncludeCharacterSet.ToCharArray(),
SpecialCharacterSet.ToCharArray()
};
int[] charsLeftInGroup = new int[charGroups.Length];
for (int i = 0; i < charsLeftInGroup.Length; i++)
charsLeftInGroup[i] = charGroups[i].Length;
int[] leftGroupsOrder = new int[charGroups.Length];
for (int i = 0; i < leftGroupsOrder.Length; i++)
leftGroupsOrder[i] = i;
byte[] randomBytes = new byte[4];
RNGCryptoServiceProvider rng = new();
rng.GetBytes(randomBytes);
int seed = BitConverter.ToInt32(randomBytes, 0);
Random random = new(seed);
char[] result = null;
result = new char[length.Value];
int nextCharIdx;
int nextGroupIdx;
int nextLeftGroupsOrderIdx;
int lastCharIdx;
int lastLeftGroupsOrderIdx = leftGroupsOrder.Length - 1;
for (int i = 0; i < result.Length; i++)
{
if (lastLeftGroupsOrderIdx == 0)
nextLeftGroupsOrderIdx = 0;
else
nextLeftGroupsOrderIdx = random.Next(0, lastLeftGroupsOrderIdx);
nextGroupIdx = leftGroupsOrder[nextLeftGroupsOrderIdx];
lastCharIdx = charsLeftInGroup[nextGroupIdx] - 1;
if (lastCharIdx == 0)
nextCharIdx = 0;
else
nextCharIdx = random.Next(0, lastCharIdx + 1);
try
{
result[i] = charGroups[nextGroupIdx][nextCharIdx];
}
catch
{
if (lastCharIdx == 0)
nextCharIdx = 0;
else
nextCharIdx = random.Next(0, lastCharIdx + 1);
}
if (lastCharIdx == 0)
charsLeftInGroup[nextGroupIdx] = charGroups[nextGroupIdx].Length;
else
{
if (lastCharIdx != nextCharIdx)
{
char temp = charGroups[nextGroupIdx][lastCharIdx];
charGroups[nextGroupIdx][lastCharIdx] =
charGroups[nextGroupIdx][nextCharIdx];
charGroups[nextGroupIdx][nextCharIdx] = temp;
}
charsLeftInGroup[nextGroupIdx]--;
}
if (lastLeftGroupsOrderIdx == 0)
lastLeftGroupsOrderIdx = leftGroupsOrder.Length - 1;
else
if (lastLeftGroupsOrderIdx != nextLeftGroupsOrderIdx)
{
int temp = leftGroupsOrder[lastLeftGroupsOrderIdx];
leftGroupsOrder[lastLeftGroupsOrderIdx] =
leftGroupsOrder[nextLeftGroupsOrderIdx];
leftGroupsOrder[nextLeftGroupsOrderIdx] = temp;
}
lastLeftGroupsOrderIdx--;
}
}

I am unable to use substring. How can I fix this?

I am trying to see weather the string is in alphabetical order or no and this error pops up
System.ArgumentOutOfRangeException: Index and length must refer to a location within the string.
Parameter name: length
at System.String.Substring(Int32 startIndex, Int32 length)
at Rextester.Program.Main(String[] args)**
public static void Main(string[] args)
{
string str = "bat\ncat\ndog\n";
int c = 0;
for (int i = 0; i < str.Length; i++)
{
if ((str.Substring(i,i + 1).Equals("\n")))
{
c++;
}
}
String[] strArray = new String[c + 1]; //declare with size
int g = 0;
String h = "";
for (int i = 0; i < str.Length; i++)
{
if ((str.Substring(i,i + 1).Equals("\n")))
{
strArray[g] = h;
h = "";
g = g + 1;
}
else
{
h = h + str.Substring(i,i + 1);
}
}
String p = "True";
for (int i = 0; i < g; i++)
{
if (i < (g - 1))
{
String f = strArray[i];
String g2 = strArray[i + 1];
char d = f[0];
char s = g2[0];
int d1 = (int)d;
int s1 = (int)s;
if (d1 > s1)
{
p = "False";
}
}
}
Console.WriteLine(p);
}
}
Not sure about what you are doing in your second loop and why is it so complex. We can do the same like this. Hope this helps.
using System;
public class Program
{
public static void Main()
{
string str = "abcd";
str = str.Replace('\n',' ');
String p = "True";
for (int i = 1; i < str.Length; i++) {
// if element at index 'i' is less
// than the element at index 'i-1'
// then the string is not sorted
if (str[i] < str[i - 1]) {
p = "false";
}
}
Console.WriteLine(p);
}
}
Pay attention to the definition of substring
The substring starts at a specified character position and has a
specified length
Considering your first use of substring, here
for (int i = 0; i < str.Length; i++)
{
if (str.Substring(i, i + 1).Equals("\n"))
{
c++;
}
}
What happens when we get to i=6 here? Substring tries to give you a new string that starts at position i = 6, and is length = 7 characters long. So it tries to give you 7 characters starting from str[6] to str[12]. Well, there is no str[12], so you get an exception.
Its clear that your intent is NOT to get a string that starts at position 6 and is 7 characters long. You want ONE character, so your loop should be this
for (int i = 0; i < str.Length; i++)
{
if (str.Substring(i, 1).Equals("\n"))
{
c++;
}
}
But theres a much simpler way to get your words in alphabetical order using LINQ
string str = "bat\ncat\ndog\n";
//Removes the trailing \n so you don't have one entry that is only whitespace
str = str.Trim();
string[] strSplit = str.Split('\n').OrderBy(x => x[0]).ToArray();
Now all substrings are sorted alphabetically and you can do whatever you want with them

trying to parse a linktext in c#

How to parse a link as example: 'a/b/c' ?
How could I fix this code that returns: 1. 'a' 2. 'b/c' 3. empty
int getSizeOfParser(string links, char c)
{
int size = 0;
if (!string.IsNullOrEmpty(links))
{
for (int i = 0; i < links.Length; i++)
{
if (links[i] == c)
size++;
}
return size + 1;
}
return -1;
}
string[] parsedLink(string links, char c)
{
int size = getSizeOfParser(links, c);
if (size == -1)
return null;
string[] parsed = new string[size];
int i = 0, index = 0, tmp = 0;
while (i < links.Length)
{
if (links[i] == c)
{
parsed[index++] = links.Substring(tmp, i++);
tmp = i;
}
else
i++;
}
return parsed;
}
According to documentation, the second argument of SubString is its length from the index int he first argument:
Substring(Int32, Int32)
Retrieves a substring from this instance. The substring starts at a specified character position and has a specified length.
So what you want to do is:
if (links[i] == c)
{
parsed[index++] = links.Substring(tmp, i-tmp);
tmp = i+1;
}
i++;
instead of:
if (links[i] == c)
{
parsed[index++] = links.Substring(tmp, i++);
tmp = i;
}
else
i++;

How to increment mixed Alphanumeric in C#?

I have requirements in a project to generate sequential rows and columns which are alphanumeric values.
The end user will pass the start value of row and column he would like to start from, and how many rows and columns he wants to generate.
For letters the max value is Z
For numbers the max values is 9
If the end user passed these parameters:
StartRow = 0A
StartColumn = A9Z
rowsCount = 2
columnsCount = 5
I would like to get this result:
You might want to reconsider your approach. Rather than maintaining an alphanumeric value and trying to increment it, maintain the value as a class containing Row and Column values, and then use ToString to convert it to the alphanumeric representation. Like this:
class RowCol
{
private int _row;
private int _col;
public int Row
{
get { return _row; }
set
{
// Row is of the form <digit><letter
// giving you 260 possible values.
if (value < 0 || value > 259)
throw new ArgumentOutOfRangeException();
_row = value;
}
}
public int Col
{
get { return _col; }
set
{
// Col is <letter><digit><letter>,
// giving you 6,760 possible values
if (value < 0 || value > 6759)
throw new ArgumentOutOfRangeException();
_col = value;
}
}
public string RowString
{
get
{
// convert Row value to string
int r, c;
r = Math.DivMod(_row, 26, out c);
r += '0';
c += 'A';
return string.Concat((char)r, (char)c);
}
set
{
// convert string to number.
// String is of the form <letter><digit>
if (string.IsNullOrEmpty(value) || value.Length != 2
|| !Char.IsDigit(value[0] || !Char.IsUpper(value[1]))
throw new ArgumentException();
_row = 26*(value[0]-'0') + (value[1]-'A');
}
}
public string ColString
{
get
{
int left, middle, right remainder;
left = Math.DivRem(_col, 260, out remainder);
middle = Math.DivRem(remainder, 26, out right);
left += 'A';
middle += '0';
right += 'A';
return string.Concat((char)left, (char)middle, (char)right);
}
set
{
// Do standard checking here to make sure it's in the right form.
if (string.IsNullOrEmpty(value) || value.Length != 3
|| !Char.IsUpper(value[0] || !Char.IsDigit(value[1]) || !Char.IsUpper(value[2]))
throw new ArgumentException();
_col = 260*(value[0] - 'A');
_col += 26*(value[1] - '0');
_col += value[2] - 'A';
}
}
public override string ToString()
{
return RowString + '-' + ColString;
}
public RowCol(int row, int col)
{
Row = _row;
Col = _col;
}
public RowCol(string row, string col)
{
RowString = row;
RowString = col;
}
}
(Code not yet tested, but that's the general idea.)
That's a bit more code than you have, it hides the complexity in the RowCol class rather than forcing you to deal with it in your main program logic. The point here is that you just want to increment the row or column; you don't want to have to think about how that's done. It makes your main program logic easier to understand. For example:
string startRow = "0A";
string startCol = "B0A";
RowCol rc = new RowCol("0A", "B0A");
for (int r = 0; r < rowsCount; r++)
{
rc.ColString = "B0A";
for (int c = 0; c < columnsCount; c++)
{
Console.WriteLine(rc);
rc.Row = rc.Row + 1;
}
rc.Col = rc.Col + 1;
}
By casting this as a simple conversion problem and encapsulating it in a class, I've made the code more robust and flexible, and easier to test, understand, and use.
I have come up with very simple solution to implement that and I would like to share this Console application :
class Program
{
static void Main(string[] args)
{
var row = "0A";
var column = "A9Z";
var rowsCount = 2;
var columnsCount = 5;
var rowCharArray =row.ToArray().Reverse().ToArray();
var columnCharArray = column.ToArray().Reverse().ToArray();
for (int i = 0; i < rowsCount; i++)
{
for (int j = 0; j < columnsCount; j++)
{
columnCharArray = incrementChar(columnCharArray);
var currentColumn = string.Join("", columnCharArray.Reverse().ToArray());
var currentRow= string.Join("", rowCharArray.Reverse().ToArray());
Console.WriteLine(currentRow + "-" + currentColumn);
}
columnCharArray = column.ToArray().Reverse().ToArray();
rowCharArray= incrementChar(rowCharArray);
Console.WriteLine("-------------------------------");
}
Console.ReadLine();
}
static char[] incrementChar(char[] charArray,int currentIndex=0)
{
char temp = charArray[currentIndex];
if (charArray.Length -1 == currentIndex && (temp == '9' || temp == 'Z'))
throw new Exception();
temp++;
if(Regex.IsMatch(temp.ToString(),"[A-Z]"))
{
charArray[currentIndex] = temp;
}
else
{
if (Regex.IsMatch(temp.ToString(), "[0-9]"))
{
charArray[currentIndex] = temp;
}
else
{
currentIndex++;
incrementChar(charArray, currentIndex);
}
}
if (currentIndex != 0)
charArray = resetChar(charArray, currentIndex);
return charArray;
}
static char[] resetChar(char[] charArray,int currentIndex)
{
for (int i = 0; i < currentIndex; i++)
{
if (charArray[i] == 'Z')
charArray[i] = 'A';
else if (charArray[i] == '9')
charArray[i] = '0';
}
return charArray;
}
}

Splitting text into lines with maximum length

I have a long string and I want to fit that in a small field. To achieve that, I break the string into lines on whitespace. The algorithm goes like this:
public static string BreakLine(string text, int maxCharsInLine)
{
int charsInLine = 0;
StringBuilder builder = new StringBuilder();
for (int i = 0; i < text.Length; i++)
{
char c = text[i];
builder.Append(c);
charsInLine++;
if (charsInLine >= maxCharsInLine && char.IsWhiteSpace(c))
{
builder.AppendLine();
charsInLine = 0;
}
}
return builder.ToString();
}
But this breaks when there's a short word, followed by a longer word. "foo howcomputerwork" with a max length of 16 doesn't break, but I want it to. One thought I has was looking forward to see where the next whitespace occurs, but I'm not sure whether that would result in the fewest lines possible.
Enjoy!
public static string SplitToLines(string text, char[] splitOnCharacters, int maxStringLength)
{
var sb = new StringBuilder();
var index = 0;
while (text.Length > index)
{
// start a new line, unless we've just started
if (index != 0)
sb.AppendLine();
// get the next substring, else the rest of the string if remainder is shorter than `maxStringLength`
var splitAt = index + maxStringLength <= text.Length
? text.Substring(index, maxStringLength).LastIndexOfAny(splitOnCharacters)
: text.Length - index;
// if can't find split location, take `maxStringLength` characters
splitAt = (splitAt == -1) ? maxStringLength : splitAt;
// add result to collection & increment index
sb.Append(text.Substring(index, splitAt).Trim());
index += splitAt;
}
return sb.ToString();
}
Note that splitOnCharacters and maxStringLength could be saved in user settings area of the app.
Check the contents of the character before writing to the string builder and or it with the current count:
public static string BreakLine(string text, int maxCharsInLine)
{
int charsInLine = 0;
StringBuilder builder = new StringBuilder();
for (int i = 0; i < text.Length; i++)
{
char c = text[i];
if (char.IsWhiteSpace(c) || charsInLine >= maxCharsInLine)
{
builder.AppendLine();
charsInLine = 0;
}
else
{
builder.Append(c);
charsInLine++;
}
}
return builder.ToString();
}
update a code a bit, the #dead.rabit goes to loop sometime.
public static string SplitToLines(string text,char[] splitanyOf, int maxStringLength)
{
var sb = new System.Text.StringBuilder();
var index = 0;
var loop = 0;
while (text.Length > index)
{
// start a new line, unless we've just started
if (loop != 0)
{
sb.AppendLine();
}
// get the next substring, else the rest of the string if remainder is shorter than `maxStringLength`
var splitAt = 0;
if (index + maxStringLength <= text.Length)
{
splitAt = text.Substring(index, maxStringLength).LastIndexOfAny(splitanyOf);
}
else
{
splitAt = text.Length - index;
}
// if can't find split location, take `maxStringLength` characters
if (splitAt == -1 || splitAt == 0)
{
splitAt = text.IndexOfAny(splitanyOf, maxStringLength);
}
// add result to collection & increment index
sb.Append(text.Substring(index, splitAt).Trim());
if(text.Length > splitAt)
{
text = text.Substring(splitAt + 1).Trim();
}
else
{
text = string.Empty;
}
loop = loop + 1;
}
return sb.ToString();
}

Categories