I have Xamarin TextInput. It's some kind of 'mm/yy' format. I need to insert / after second symbol.
I have this method for adding / after 2 character
private static string AppendAtPosition(string baseString, int position, string character)
{
var sb = new StringBuilder(baseString);
for (int i = position; i < sb.Length; i += (position + character.Length))
sb.Insert(i, character);
return sb.ToString();
}
And I call it like this
ExpiresInput.EditingChanged += (object sender, EventArgs e) =>
{
var creditcardyear = ExpiresInput.Text;
if (creditcardyear.Length <= 2) return;
if (creditcardyear.Length > 2)
{
ExpiresInput.Text = AppendAtPosition(creditcardyear, 2, "/");
}
if (creditcardyear.Length == 5) {
return;
}
But when I want to insert 22/12 for example I have this.
How I can fix this?
ExpiresInput.EditingChanged += (object sender, EventArgs e) =>
{
var currentText = ExpiresInput.Text;
if (currentText.Length == 1)
return;
int strLength = currentText.Length;
if (Char.IsDigit(currentText, strLength - 1) && Char.IsDigit(currentText, strLength - 2))
currentText = $"{currentText}/";
}
This code adds "/" after every two-digit character. You need to remove last "/" after you are sure that you do not type any other character to textbox.
Related
This seems an easy task, however, I can't find any working method to do it. I want a textbox add extra line to itself automatically when we type at the end of a full line (so, we are redirected to a new line).
Maybe I show it better as follows:
Textbox current value: asdfghj(this is full length of a textbox)
We type new string after j: asd. And I see:
asd
Only one line, to see the first line I need to scroll up ^
And I want to see:
asdfghj
asd
Two lines.
I tried this code:
private void textBox1_TextChanged(object sender, EventArgs e)
{
Size size = TextRenderer.MeasureText(textBox1.Text, textBox1.Font);
textBox1.Width = size.Width;
textBox1.Height = size.Height;
}
But the extra line is being created when I press enter or shift-enter only. And I want it to be automatically added. I also have Multiline=true and Wordwrap=true.
Kind of a hack, but try this and see if it fits your needs:
int previouslines = 1;
private void textBox2_TextChanged(object sender, EventArgs e)
{
int size=textBox2.Font.Height;
int lineas = textBox2.Lines.Length;
int newlines = 0;
if (textBox2.Text.Contains(Environment.NewLine))
{
newlines = textBox2.Text.Split(new string[] { Environment.NewLine }, StringSplitOptions.None).Length - 1;
lineas += newlines - (textBox2.Lines.Length - 1);
}
for(int line_num= 0;line_num<textBox2.Lines.Length;line_num++)
{
if (textBox2.Lines[line_num].Length > 1)
{
int pos1=textBox2.GetFirstCharIndexFromLine(line_num);
int pos2= pos1 + textBox2.Lines[line_num].Length-1;
int y1 = textBox2.GetPositionFromCharIndex(pos1).Y;
int y2 = textBox2.GetPositionFromCharIndex(pos2).Y;
if (y1 != y2)
{
int aux = y2+size;
lineas = (aux / size);
if (y1 != 1)
{
lineas++;
}
lineas += newlines - (textBox2.Lines.Length - 1);
}
}
}
if (lineas > previouslines)
{
previouslines++;
textBox2.Height = textBox2.Height + size;
}
else if (lineas<previouslines)
{
previouslines--;
textBox2.Height = textBox2.Height - size;
}
}
If MultiLine is set, you could do it like this:
1) Estimate the lenght of the last line of text in the TextBox by splitting it into an array (may be not the fasted possible)
2) If that line has more the MAX_CHARS chars, then
3) Take all of the text, except the last char, and add new line and then that char
4) Correct selection and position
const int MAX_CHARS = 10;
private void textBox1_TextChanged(object sender, EventArgs e)
{
string[] sTextArray = textBox1.Text.Split( new string[] {Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries );
int nLines = sTextArray.Length;
string sLastLine = sTextArray[nLines -1];
if (sLastLine.Length > MAX_CHARS)
{
int nTextLen = textBox1.Text.Length;
string sText = textBox1.Text.Substring(0, nTextLen - 1) + Environment.NewLine + textBox1.Text[nTextLen - 1];
textBox1.Text = sText;
textBox1.SelectedText = "";
textBox1.Select(nTextLen +2, 0);
}
}
text box area that automatically cuts a long paragraph text into 30 characters sentences
i am trying run this code but occurs exception [exception of type 'System.StackOverflowException']
private void txtCutParagraph_TextChanged(object sender, EventArgs e)
{
int limitNum = 30;
string sentence = txtCutParagraph.Text;
string[] words = sentence.Split(' ');
string line = "";
foreach (string word in words)
{
if ((line + word).Length > limitNum)
{
newLine += line + "\r\n";
line = "";
}
line += word + " ";
}
if (line.Length > 0)
newLine += line + "\r\n";
txtCutParagraph.Text = newLine;
}
If the form is freezing is because that txtCutParagraph_TextChanged event is firing infinitely, because you are changing the text of textbox at the end of the event: txtCutParagraph.Text = newLine;, so it means that change text in the textbox, and the event will fire again and again.
To prevent this form from freezing please move your code to another event of textbox, named KeyPress as:
private void txtCutParagraph_KeyPress(object sender, KeyPressEventArgs e)
{
int limitNum = 30;
string sentence = txtCutParagraph.Text;
string[] words = sentence.Split(' ');
string line = "";
foreach (string word in words)
{
if ((line + word).Length > limitNum)
{
newLine += line + "\r\n";
line = "";
}
line += word + " ";
}
if (line.Length > 0)
newLine += line + "\r\n";
txtCutParagraph.Text = newLine;
}
What you are trying to do is called Word wrapping. TextBox class has Wordwrap option by default. unfortunately you cant limit number of characters per line.
You have to write an algorithm instead. I have noticed that your algorithm does not work correctly. so i decided to write one my self (as it was a good practice!). It is hard to handle all situations that can happen inside text formatting. I tried my best anyway you have to write one your self if you are not satisfied with results.
Before using this algorithm you have to disable Wordwrap feature of Textbox. So they will not Interfere each other. In InitializeComponent inside Form Designer add this line.
this.textBox1.WordWrap = false;
Now use this algorithm to do it for you! Note that textbox1 is a multi line text box.
private StringBuilder stringBuilder = new StringBuilder();
private bool _isInsideTextChanged = false;
private const int MaximumChars = 30; // Maximum characters
private StringBuilder WrapText(StringBuilder text, ref int position)
{
StringBuilder newStringBuilder = new StringBuilder(text.ToString());
int charsPerLine = 0;
int lastSpace = -1; // index of last space per line
for (int i = 0; i < newStringBuilder.Length; i++)
{
if (newStringBuilder[i] == ' ')
{
if (newStringBuilder.Length > i + 2 && newStringBuilder.ToString(i + 1, 2) == "\r\n")
{
if (newStringBuilder.Length > i + 3)
{
int next = newStringBuilder.ToString().IndexOf(' ', i + 3);
if (next != -1 && charsPerLine + next - i <= MaximumChars || charsPerLine + newStringBuilder.Length - i - 2 <= MaximumChars)
{
newStringBuilder.Remove(i + 1, 2);
if (i <= textBox1.SelectionStart)
{
position -= 2;
}
continue;
}
}
i++;
continue;
}
if (newStringBuilder.Length > i + 1 && newStringBuilder[i + 1] != ' ')
{
lastSpace = i;
}
}
if (newStringBuilder[i] == '\n' || newStringBuilder[i] == '\r')
{
lastSpace = -1;
charsPerLine = 0;
}
if (++charsPerLine > MaximumChars && lastSpace != -1)
{
newStringBuilder.Insert(lastSpace + 1, "\r\n");
if (lastSpace <= textBox1.SelectionStart)
{
position += 2;
}
charsPerLine = i - lastSpace;
lastSpace = -1;
i++;
}
}
return newStringBuilder;
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
if (_isInsideTextChanged) return;
_isInsideTextChanged = true;
stringBuilder.Clear();
stringBuilder.Append(textBox1.Text);
int position = textBox1.SelectionStart;
string newText = WrapText(stringBuilder, ref position).ToString();
textBox1.Text = newText;
textBox1.SelectionStart = position;
_isInsideTextChanged = false;
}
Here is the test that shows the results.
How this wroks?
This algorithm will count the number of characters from last line break index (default value is 0) up to last space character index per line.(default value is -1 means no space in that line). Then it will put line break after last space if the number of characters on that line is more than 30. How ever this algorithm test other things too to better handle text formatting.
This is done every time a textbox value is changed. StringBuilder is used instead of string to increase performance.
To prevent stack overflow exception as described by #KhaksarWeqar I used a boolean value _isInsideTextChanged with TextChanged event:
private bool _isInsideTextChanged = false;
private void textBox1_TextChanged(object sender, EventArgs e)
{
if (_isInsideTextChanged) return; // return if was inside TextChanged.
_isInsideTextChanged = true; // inside TextChanged
// Do stuff...
_isInsideTextChanged = false; // outside TextChanged
}
There is also a better way explained on wiki. you can create your own even better!. https://en.wikipedia.org/wiki/Line_wrap_and_word_wrap
For TextBox, has a attribute "MaxLength",but it count all ascii and unicode as 1 character。
But in database, we set the field varchar(n) . It treats the ascii 1 and the unicode 2。
How can I limit the textbox input by the byte?
Because no notify before text changed, workaround like this.
public class TextBoxEx : TextBox
{
private bool bIsChanging;
public TextBoxEx()
{
TextChanged += TextBoxEx_TextChanged;
}
public int MaxByteLength { private get; set; }
private void TextBoxEx_TextChanged(object sender, TextChangedEventArgs e)
{
if (bIsChanging || MaxByteLength == 0 || Text.Length*2 <= MaxByteLength)
return;
bIsChanging = true;
int start = SelectionStart;
Text = TruncateString(Text, MaxByteLength);
SetLimit();
SelectionStart = start;
bIsChanging = false;
}
private void SetLimit()
{
MaxLength = MaxByteLength - Encoding.UTF8.GetBytes(Text).Length + Text.Length;
}
private static string TruncateString(string text, int max)
{
if (max == 0) return text;
byte[] bytes = Encoding.UTF8.GetBytes(text);
if (bytes.Length <= max) return text;
char[] c = text.ToCharArray();
var sb = new StringBuilder();
int count = 0;
foreach (char t in c)
{
count += Encoding.UTF8.GetByteCount(t.ToString());
if (max >= count)
{
sb.Append(t);
}
else
{
break;
}
}
return sb.ToString();
}
}
ascii code is hexadecimal as well, which means one character stands for 2 bytes of information
EX: "A" is 41(Hx) 0100 0001 which is 2 bytes and so on
I think you should refer this answer
It suggests to handle this your self (In this case, it limits 12 bytes length):
private void textBox1_TextChanged(object sender, EventArgs e)
{
var textBytes = Encoding.UTF8.GetBytes(textBox1.Text);
var textByteCount = Encoding.UTF8.GetByteCount(textBox1.Text);
var textCharCount = Encoding.UTF8.GetCharCount(textBytes);
if (textCharCount != textByteCount && textByteCount >= 12)
{
textBox1.Text = Encoding.UTF32.GetString(Encoding.UTF32.GetBytes(textBox1.Text), 0, 12);
}
else if (textBox1.Text.Length >= 6)
{
textBox1.Text = textBox1.Text.Substring(0, 6);
}
}
I'd simply use MaxLength of half the n in varchar(n).
I'm trying to add numbered list functionality to a text editor. RichTextbox already provides the SelectionBullet property to change a selection to a bulleted list. But i was unable to find a similar property to generate numbered list. Is there any standard way to create a numbered list on Richtextbox. If not, i would have to implement it myself so code snips that could help me do that will help, Thank you.
I know that a link is not gernerally accepted as a good answer, however the article RichTextBox with Search Line Numbering, Bulleting, Printing, Searching Support on CodeProject could probably help you out quite a bit with what you are looking for.
In this article, the author extends the RichTextBox control into something that can do what you are asking (and more), plus the code is posted there for all to see.
Well, i implemented it as follows.
private void btnNumbers_Click(object sender, EventArgs e)
{
string temptext = rtbMain.SelectedText;
int SelectionStart = rtbMain.SelectionStart;
int SelectionLength = rtbMain.SelectionLength;
rtbMain.SelectionStart = rtbMain.GetFirstCharIndexOfCurrentLine();
rtbMain.SelectionLength = 0;
rtbMain.SelectedText = "1. ";
int j = 2;
for( int i = SelectionStart; i < SelectionStart + SelectionLength; i++)
if (rtbMain.Text[i] == '\n')
{
rtbMain.SelectionStart = i + 1;
rtbMain.SelectionLength = 0;
rtbMain.SelectedText = j.ToString() + ". ";
j++;
SelectionLength += 3;
}
}
private void rtbMain_KeyDown(object sender, KeyEventArgs e)
{//this piece of code automatically increments the bulleted list when user //presses Enter key
int tempNum;
if (e.KeyCode == Keys.Enter)
try
{
if (char.IsDigit(rtbMain.Text[rtbMain.GetFirstCharIndexOfCurrentLine()]))
{
if (char.IsDigit(rtbMain.Text[rtbMain.GetFirstCharIndexOfCurrentLine() + 1]) && rtbMain.Text[rtbMain.GetFirstCharIndexOfCurrentLine() + 2] == '.')
tempNum = int.Parse(rtbMain.Text.Substring(rtbMain.GetFirstCharIndexOfCurrentLine(),2));
else tempNum = int.Parse(rtbMain.Text[rtbMain.GetFirstCharIndexOfCurrentLine()].ToString());
if (rtbMain.Text[rtbMain.GetFirstCharIndexOfCurrentLine() + 1] == '.' || (char.IsDigit(rtbMain.Text[rtbMain.GetFirstCharIndexOfCurrentLine() + 1]) && rtbMain.Text[rtbMain.GetFirstCharIndexOfCurrentLine() + 2] == '.'))
{
tempNum++;
rtbMain.SelectedText = "\r\n" + tempNum.ToString() + ". ";
e.SuppressKeyPress = true;
}
}
}
catch{}
}
Here is my answer... which is easily readable and refineable. I took a much different approach but added the ability to remove the numbered list within the selection if it already exists. Please note that so far I have only lightly tested it and it seems to work good... but it may need further refinement.
private void btnOrdered_Click(object sender, EventArgs e)
{
string[] splitSelection = null;
// If selection split selection else split everything
if (this.txtCaptionEditor.SelectionLength > 0)
{
splitSelection = this.txtCaptionEditor.SelectedText.Replace("\r\n", "\n").Split("\n".ToCharArray());
}
else
{
splitSelection = this.txtCaptionEditor.Text.Replace("\r\n", "\n").Split("\n".ToCharArray());
}
bool Exists = false;
for (int i = 0; i < splitSelection.GetLength(0); i++)
{
// If Ordered List Allready exists in selection then remove else add
if (!string.IsNullOrEmpty(splitSelection[i]))
{
if (splitSelection[i].Substring(0, 2) == "1.") { Exists = true; }
}
}
for (int i = 0; i < splitSelection.GetLength(0); i++)
{
int lineCount = (i + 1);
if (Exists)
{
this.txtCaptionEditor.Text = this.txtCaptionEditor.Text.Replace(Convert.ToString(lineCount) + ". ", "");
}
else
{
if(!string.IsNullOrEmpty(splitSelection[i]))
{
this.txtCaptionEditor.Text = this.txtCaptionEditor.Text.Replace(splitSelection[i], Convert.ToString(lineCount) + ". " + splitSelection[i]);
}
}
}
}
private void txtCaptionEditor_KeyDown(object sender, KeyEventArgs e)
{
string[] splitSelection = this.txtCaptionEditor.Text.Replace("\r\n", "\n").Split("\n".ToCharArray());
if (e.KeyCode == Keys.Enter)
{
// Get Current Line Position
int currentLine = this.txtCaptionEditor.GetLineFromCharIndex(this.txtCaptionEditor.SelectionStart);
// Only Run if the previous line is greater than zero
if ((currentLine) >= 0)
{
// Loop through 100 possible numbers for match you can go higher
// If you think your numbered list could go above 100
for (int i = 0; i < 100; i++)
{
if (splitSelection[(currentLine)].Substring(0, 2) == Convert.ToString((i + 1)) + ".")
{
// If the substring of the current line equals a numbered list value.. enumerate next line
this.txtCaptionEditor.SelectedText = "\n" + (i + 2) + ". ";
e.SuppressKeyPress = true;
}
}
}
}
}
I want to be able to use a RegEx to parse out ranges like a Windows Print dialog (such as 1-50,100-110,111,112). The following is my current code and I am not clear on how to parse for the additional commas and numbers. I can parse out the hyphen, but not sure how to do it for additional commas or hyphens
private void tboxRowNum_Leave(object sender, EventArgs e)
{
Regex.Replace(tboxRowNum.Text, #"(?<first>\d+)-(?<last>\d+)",
new MatchEvaluator(this.parseSpaceDefinition));
}
private string parseSpaceDefinition(Match m)
{
int first = int.Parse(m.Groups["first"].Value);
int last = int.Parse(m.Groups["last"].Value);
StringBuilder sb = new StringBuilder(first.ToString());
for (int i = first + 1; i <= last; i++)
{
if (spaceItems == 0)
{
if (isNumeric(sb.ToString(), System.Globalization.NumberStyles.Integer))
{
startingSpace = Convert.ToInt32(sb.ToString());
}
}
sb.Append("," + i.ToString().Replace(" ", ""));
spaceItems++;
endingSpace = i;
}
tboxRowDesc.Text = sb.ToString();
return sb.ToString();
}
Edit 1: The modified code gets me what I want:
private void tboxRowNum_Leave(object sender, EventArgs e)
{
string[] parts = tboxRowNum.Text.Split(',');
for (int i = 0; i < parts.Length; i++)
{
if (parts[i].IndexOf('-') >= 0)
{
Regex.Replace(parts[i], #"(?<first>\d+)-(?<last>\d+)",
new MatchEvaluator(this.parseSpaceDefinition));
}
else
{
int number;
if(!(int.TryParse(parts[i], out number)))
{
MessageBox.Show("Incomplete/Invalid formula", "Invalid Space Definition");
tboxRowDesc.Text = "";
}
else
{
tboxRowDesc.Text += "," + number;
spaceItems++;
}
}
}
}
string[] ranges = inputString.split(',');
foreach (string rangeCandidate in ranges) {
// See if matches regex
}
Split it first on comma, and for each part check if it matches your regexp. If it does, do what you are already doing, otherwise just use int.Parse (or int.TryParse for robustness).