Related
I have 100 random words in a notepad file & choosing a random word but it's always choosing the same word which is the last one in the file every word is a line.
Here is the code for taking a random word & showing it as:
private string[] tab;
private string current = "";
private string copycurrent = "";
private void randomword()
{
string line = "";
try
{
using (StreamReader sr = new StreamReader("Words.txt"))
{
string[] tab = null;
while ((line = sr.ReadLine()) != null)
{
tab = line.Split("\n");
}
Random r = new Random();
int j = r.Next(tab.Length);
current = tab[j];
copycurrent = "";
for (int i = 0; i < current.Length; i++)
{
copycurrent += "_";
}
label1.Text = "";
for (int i = 0; i < copycurrent.Length; i++)
{
label1.Text += copycurrent.Substring(i, 1);
label1.Text += " ";
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
Please - can anyone help? I would really appreciate it! Thanks in advance.
I don't know why it doesn't work like I've been quite wondering for some time. BTW I'm making a hangman game if anyone want the project hmu ig: rami_chalouhi. its like an actual game with login and loading etc..
You keep overwriting 'tab'. you only have the last line
string[] tab = null;
while ((line = sr.ReadLine()) != null)
{
tab = line.Split("\n");
}
Better would be a list of words
var words = new List<string>();
while ((line = sr.ReadLine()) != null)
{
var tab = line.Split("\n");
words.AddRange(tab);
}
in fact that split doesnt make any sense, ReadLine will already be splitting on new line (my first scan I assumed you were splitting on a needed delimter like space or ',') So all that code can be replaced by
var words = File.ReadAllLines("Words.txt");
see https://learn.microsoft.com/en-us/dotnet/api/system.io.file.readalllines?view=net-6.0
The rest of that code is pretty odd too
current = tab[j];
copycurrent = "";
for (int i = 0; i < current.Length; i++)
{
copycurrent += "_";
}
label1.Text = "";
for (int i = 0; i < copycurrent.Length; i++)
{
label1.Text += copycurrent.Substring(i, 1);
label1.Text += " ";
}
you want
a string of "_" thats the same length as the selected word
to fill label1.Text with that _ string plus an extra space
current = tab[j];
var copycurrent = new string('_', current.Length);
label1.Text = copycurrent + " ";
is a lot simpler and clearer
So that whole function becomes
string current;
string copycurrent;
try
{
var words = File.ReadAllLines("Words.txt");
Random r = new Random();
int j = r.Next(words.Length);
current = words[j];
copycurrent = new string('_', current.Length);
label1.Text = copycurrent + " ";
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
I am using C# WinForm, and I have a RichTextBox that I am trying to make look like a C# script.
Means when using specific words, I want them to be colored. When they edit the word by changing it, I want it to go back to be black.
My approach works, but it really messy and cause bugs when the a scroll option is created and needed to be used to see the code below. (When typing, pretty much the richtextbox jumps up and down without stop)
private void ScriptRichTextBox_TextChanged(object sender, EventArgs e)
{
ScriptTextChange = ScriptRichTextBox.Text;
ScriptColorChange();
}
private void ScriptColorChange()
{
int index = ScriptRichTextBox.SelectionStart;
ScriptRichTextBox.Text = ScriptTextChange; //Only way I found to make the all current text black again, SelectAll() didn't work well.
ScriptRichTextBox.SelectionStart = index;
String[] coloredNames = {"Main", "ClickMouseDown", "ClickMouseUp", "PressKey", "StopMoving", "Delay", "GoRight", "GoLeft", "GoUp", "GoDown", "MousePosition", "LastHorizontalDirection", "LastVerticalDirections", "CurrentDirection", "Directions" };
String[] coloredNames2 = { "cm.", "if", "else", "while", "switch", "case", "break", "return", "new" };
String[] coloredNames3 = { "MyPosition", "MyHp", "MyMp", "OtherPeopleInMap", ".RIGHT", ".LEFT", ".UP", ".DOWN", ".STOP_MOVING" };
foreach (String s in coloredNames)
this.CheckKeyword(s, Color.LightSkyBlue, 0);
foreach (String s in coloredNames2)
this.CheckKeyword(s, Color.Blue, 0);
foreach (String s in coloredNames3)
this.CheckKeyword(s, Color.DarkGreen, 0);
}
private void CheckKeyword(string word, Color color, int startIndex)
{
if (this.ScriptRichTextBox.Text.Contains(word))
{
int index = 0;
int selectStart = this.ScriptRichTextBox.SelectionStart;
while ((index = this.ScriptRichTextBox.Text.IndexOf(word, (index + 1))) != -1)
{
this.ScriptRichTextBox.Select((index + startIndex), word.Length);
this.ScriptRichTextBox.SelectionColor = color;
this.ScriptRichTextBox.Select(selectStart, 0);
this.ScriptRichTextBox.SelectionColor = Color.Black;
}
}
}
I refactored your code a little to hopefully demonstrate a better approach to colouring the text. It is also not optimal to instantiate your string arrays every time you fire the TextChanged event.
Updated:The idea is to build up a word buffer that will be matched with your set of words when typing.
The buffer records each key and if it .IsLetterOrDigit it adds it to the StringBuilder buffer. The buffer has some additional bugs, with recording key press values and not removing recorded chars if you hit backspace etc..
Instead of the word buffer, use RegEx to match any of the words in your reserve word list. Build up the reserve word RegEx so you end up with something like \b(word|word2|word3....)\b This is done in the code in the BuildRegExPattern(..) method.
Once you hit any key other than a letter or number the buffer is checked for content and if the content matches a word then only the text right before the cursor in the ScriptRichTextBox.Text is checked and changed.
Remove the .(dots) from the reserve words as this just complicates the matching criteria. The RegEx in the built up patters will match the words exactly, so if you type something like FARRIGHT or cms the words will not partially change colour.
As an extra I also covered the paste process pressing Ctrl+V because it is a bit of a pain in WinForms and will probably happen quite often.
There are older questions eg. this one that cover the scrolling behaviour, where it shows how to interop by adding the [System.Runtime.InteropServices.DllImport("user32.dll")] attribute, but it can be done without it.
To prevent all the scroll jumping you can make use of the .DefWndProc(msg) method on the form. this question pointed me towards the WM_SETREDRAW property.
There is also this list of other properties that can be set.
The full implementation is this:
public partial class Form1 : Form
{
private readonly string[] _skyBlueStrings;
private readonly string[] _blueStrings;
private readonly string[] _greenStrings;
//for pasting
bool _IsCtrl;
bool _IsV;
//value to fix the colour not setting first character after return key pressed
int _returnIdxFix = 0;
//regex patterns to use
string _LightBlueRegX = "";
string _BlueRegX = "";
string _GreenRegX = "";
//match only words
Regex _rgxAnyWords = new Regex(#"(\w+)");
//colour setup
Color _LightBlueColour = Color.LightSkyBlue;
Color _BlueColour = Color.Blue;
Color _GreenColour = Color.DarkGreen;
Color _DefaultColour = Color.Black;
public Form1()
{
InitializeComponent();
_skyBlueStrings = new string[] { "Main", "ClickMouseDown", "ClickMouseUp", "PressKey", "StopMoving", "Delay", "GoRight", "GoLeft", "GoUp", "GoDown", "MousePosition", "LastHorizontalDirection", "LastVerticalDirections", "CurrentDirection", "Directions" };
_blueStrings = new string[] { "cm", "if", "else", "while", "switch", "case", "break", "return", "new" };
_greenStrings = new string[] { "MyPosition", "MyHp", "MyMp", "OtherPeopleInMap", "RIGHT", "LEFT", "UP", "DOWN", "STOP_MOVING" };
_LightBlueRegX = BuildRegExPattern(_skyBlueStrings);
_BlueRegX = BuildRegExPattern(_blueStrings);
_GreenRegX = BuildRegExPattern(_greenStrings);
}
string BuildRegExPattern(string[] keyworkArray)
{
StringBuilder _regExPatern = new StringBuilder();
_regExPatern.Append(#"\b(");//beginning of word
_regExPatern.Append(string.Join("|", keyworkArray));//all reserve words
_regExPatern.Append(#")\b");//end of word
return _regExPatern.ToString();
}
private void ProcessAllText()
{
BeginRtbUpdate();
FormatKeywords(_LightBlueRegX, _LightBlueColour);
FormatKeywords(_BlueRegX, _BlueColour);
FormatKeywords(_GreenRegX, _GreenColour);
//internal function to process words and set their colours
void FormatKeywords(string regExPattern, Color wordColour)
{
var matchStrings = Regex.Matches(ScriptRichTextBox.Text, regExPattern);
foreach (Match match in matchStrings)
{
FormatKeyword(keyword: match.Value, wordIndex: match.Index, wordColour: wordColour);
}
}
EndRtbUpdate();
ScriptRichTextBox.Select(ScriptRichTextBox.Text.Length, 0);
ScriptRichTextBox.Invalidate();
}
void ProcessWordAtIndex(string fullText, int cursorIdx)
{
MatchCollection anyWordMatches = _rgxAnyWords.Matches(fullText);
if (anyWordMatches.Count == 0)
{ return; } // no words found
var allWords = anyWordMatches.OfType<Match>().ToList();
//get the word just before cursor
var wordAtCursor = allWords.FirstOrDefault(w => (cursorIdx - _returnIdxFix) == (w.Index + w.Length));
if (wordAtCursor is null || string.IsNullOrWhiteSpace(wordAtCursor.Value))
{ return; }//no word at cursor or the match was blank
Color wordColour = CalculateWordColour(wordAtCursor.Value);
FormatKeyword(wordAtCursor.Value, wordAtCursor.Index, wordColour);
}
private Color CalculateWordColour(string word)
{
if (_skyBlueStrings.Contains(word))
{ return _LightBlueColour; }
if (_blueStrings.Contains(word))
{ return _BlueColour; }
if (_greenStrings.Contains(word))
{ return _GreenColour; }
return _DefaultColour;
}
private void FormatKeyword(string keyword, int wordIndex, Color wordColour)
{
ScriptRichTextBox.Select((wordIndex - _returnIdxFix), keyword.Length);
ScriptRichTextBox.SelectionColor = wordColour;
ScriptRichTextBox.Select(wordIndex + keyword.Length, 0);
ScriptRichTextBox.SelectionColor = _DefaultColour;
}
#region RichTextBox BeginUpdate and EndUpdate Methods
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
//wait until the rtb is visible, otherwise you get some weird behaviour.
if (ScriptRichTextBox.Visible && ScriptRichTextBox.IsHandleCreated)
{
if (m.LParam == ScriptRichTextBox.Handle)
{
rtBox_lParam = m.LParam;
rtBox_wParam = m.WParam;
}
}
}
IntPtr rtBox_wParam = IntPtr.Zero;
IntPtr rtBox_lParam = IntPtr.Zero;
const int WM_SETREDRAW = 0x0b;
const int EM_HIDESELECTION = 0x43f;
void BeginRtbUpdate()
{
Message msg_WM_SETREDRAW = Message.Create(ScriptRichTextBox.Handle, WM_SETREDRAW, (IntPtr)0, rtBox_lParam);
this.DefWndProc(ref msg_WM_SETREDRAW);
}
public void EndRtbUpdate()
{
Message msg_WM_SETREDRAW = Message.Create(ScriptRichTextBox.Handle, WM_SETREDRAW, rtBox_wParam, rtBox_lParam);
this.DefWndProc(ref msg_WM_SETREDRAW);
//redraw the RichTextBox
ScriptRichTextBox.Invalidate();
}
#endregion
private void ScriptRichTextBox_TextChanged(object sender, EventArgs e)
{
//only run all text if it was pasted NOT ON EVERY TEXT CHANGE!
if (_IsCtrl && _IsV)
{
_IsCtrl = false;
ProcessAllText();
}
}
protected void ScriptRichTextBox_KeyPress(object sender, KeyPressEventArgs e)
{
if (!char.IsLetterOrDigit(e.KeyChar))
{
//if the key was enter the cursor position is 1 position off
_returnIdxFix = (e.KeyChar == '\r') ? 1 : 0;
ProcessWordAtIndex(ScriptRichTextBox.Text, ScriptRichTextBox.SelectionStart);
}
}
private void ScriptRichTextBox_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
if (e.KeyCode == Keys.ControlKey)
{
_IsCtrl = true;
}
if (e.KeyCode == Keys.V)
{
_IsV = true;
}
}
private void ScriptRichTextBox_KeyUp(object sender, System.Windows.Forms.KeyEventArgs e)
{
if (e.KeyCode == Keys.ControlKey)
{
_IsCtrl = false;
}
if (e.KeyCode == Keys.V)
{
_IsV = false;
}
}
}
It looks like this when you paste some "code" with keywords:
and typing looks like this:
Ok after 2 days of not finding something that actually works good or has annoying bugs. I managed to find a solution myself after a big struggle of trying to make it work. The big idea is people try to edit all the RichTextBox words at once, which cause bugs. Why to edit all of the rich text box when you can do your checks on the current word only to get the same result. Which is what I did, I checked if any of my array strings is in the current word, and colored all of them.
private void ScriptRichTextBox_TextChanged(object sender, EventArgs e)
{
FindStringsInCurrentWord();
}
private void FindStringsInCurrentWord()
{
RichTextBox script = ScriptRichTextBox;
String finalWord, forwards, backwards;
int saveLastSelectionStart = script.SelectionStart;
int index = script.SelectionStart;
String[] coloredNames = { "Main", "ClickMouseDown", "ClickMouseUp", "PressKey", "StopMoving", "Delay", "GoRight", "GoLeft", "GoUp", "GoDown", "MousePosition", "LastHorizontalDirection", "LastVerticalDirections", "CurrentDirection", "Directions" };
String[] coloredNames2 = { "cm.", "if", "else", "while", "switch", "case", "break", "return", "new" };
String[] coloredNames3 = { "MyPosition", "MyHp", "MyMp", "OtherPeopleInMap", ".RIGHT", ".LEFT", ".UP", ".DOWN", ".STOP_MOVING" };
String[] arr2 = coloredNames.Union(coloredNames2).ToArray();
Array arrAll = arr2.Union(coloredNames3).ToArray(); //Gets all arrays together
Array[] wordsArray = { coloredNames, coloredNames2, coloredNames3 }; //All found strings in the word
List<String> wordsFoundList = new List<String>();
int foundChangedColor = 0;
int wordsFound = 0;
char current = (char)script.GetCharFromPosition(script.GetPositionFromCharIndex(index)); //Where the editor thingy is
//Check forward text where he uses space and save text
while (!System.Char.IsWhiteSpace(current) && index < script.Text.Length)
{
index++;
current = (char)script.GetCharFromPosition(script.GetPositionFromCharIndex(index));
}
int lengthForward = index - saveLastSelectionStart;
script.Select(script.SelectionStart, lengthForward);
forwards = script.SelectedText;
//Debug.WriteLine("Forwards: " + forwards);
script.SelectionStart = saveLastSelectionStart;
this.ScriptRichTextBox.Select(script.SelectionStart, 0);
index = script.SelectionStart;
current = (char)script.GetCharFromPosition(script.GetPositionFromCharIndex(index));
int length = 0;
//Check backwords where he uses space and save text
while ((!System.Char.IsWhiteSpace(current) || length == 0) && index > 0 && index <= script.Text.Length)
{
index--;
length++;
current = (char)script.GetCharFromPosition(script.GetPositionFromCharIndex(index));
}
script.SelectionStart -= length;
script.Select(script.SelectionStart + 1, length - 1);
backwards = script.SelectedText;
//Debug.WriteLine("Backwards: " + backwards);
script.SelectionStart = saveLastSelectionStart;
this.ScriptRichTextBox.Select(saveLastSelectionStart, 0);
this.ScriptRichTextBox.SelectionColor = Color.Black;
finalWord = backwards + forwards; //Our all word!
//Debug.WriteLine("WORD:" + finalWord);
//Setting all of the word black, after it coloring the right places
script.Select(index + 1, length + lengthForward);
script.SelectionColor = Color.Black;
foreach (string word in arrAll)
{
if (finalWord.IndexOf(word) != -1)
{
wordsFound++;
wordsFoundList.Add(word);
script.Select(index + 1 + finalWord.IndexOf(word), word.Length);
if (coloredNames.Any(word.Contains))
{
script.SelectionColor = Color.LightSkyBlue;
foundChangedColor++;
}
else if (coloredNames2.Any(word.Contains))
{
script.SelectionColor = Color.Blue;
foundChangedColor++;
}
else if (coloredNames3.Any(word.Contains))
{
script.SelectionColor = Color.DarkGreen;
foundChangedColor++;
}
//Debug.WriteLine("Word to edit: " + script.SelectedText);
this.ScriptRichTextBox.Select(saveLastSelectionStart, 0);
this.ScriptRichTextBox.SelectionColor = Color.Black;
}
}
//No strings found, color it black
if (wordsFound == 0)
{
script.Select(index + 1, length + lengthForward);
script.SelectionColor = Color.Black;
//Debug.WriteLine("WORD??: " + script.SelectedText);
this.ScriptRichTextBox.Select(saveLastSelectionStart, 0);
this.ScriptRichTextBox.SelectionColor = Color.Black;
}
}
I was wondering how I would make a picture box become invisible or visible when a certain integer matches.
My program revolves around 2 players going around a board, and when they add there 2 Di up, they will move the amount of spaces.
My problem being, My friend and I have no idea what is wrong with the current code we have, it throws no errors which baffles him, especially myself.
I've made it so my program add's the Di up on every roll, and add's it to the integer.
Anyone have any idea's on whats wrong? If not, a better approach?
Code
private void SelectPos(PictureBox pic)
{
PictureBox[] numbers = { P1_1, P1_2, P1_3, P1_4, P1_5, P1_6, P1_7, P1_8, P1_9, P1_10, P1_11, P1_12, P1_13, P1_14, P1_15, P1_16, P1_17, P1_18, P1_19, P1_20, P1_21, P1_22, P1_23, P1_24, P1_25, P1_26, P1_27, P1_28, P1_29, P1_30, P1_31, P1_32, P1_33, P1_34, P1_35, P1_36, P1_37, P1_38, P1_39, P1_40, P1_41, P1_42, P1_43, P1_44, P1_45, P1_46, P1_47, P1_48, P1_49 };
for (int i = 0; i < numbers.Length; i++)
{
if (pic == numbers[i])
{
numbers[i].Visible = true;
MessageBox.Show("k");
}
{
numbers[i].Visible = false;
MessageBox.Show("l");
}
}
}
private void bunifuFlatButton1_Click(object sender, EventArgs e)
{
Roll();
System.Threading.Thread.Sleep(100);
Roll2();
Goes_Num.Text = (int.Parse(Goes_Num.Text) + 1).ToString();
if (Convert.ToInt32(Goes_Num.Text) % 2 == 0)
{
WhichPlayer.Text = "Player 2";
P2_Number.Text = (int.Parse(P2_Number.Text) + 1).ToString();
int p2Int = Convert.ToInt32(P2_Pos.Text);
P2_Pos.Text = (p2Int + dice + dice2).ToString();
}
else if (Convert.ToInt32(Goes_Num.Text) % 2 != 0)
{
WhichPlayer.Text = "Player 1";
P1_Number.Text = (int.Parse(P1_Number.Text) + 1).ToString();
int p1Int = Convert.ToInt32(P1_Pos.Text);
P1_Pos.Text = (p1Int + dice + dice2).ToString();
int P1 = (Convert.ToInt32(P1_Pos.Text));
SelectPos(P1_1);
/*switch (P1)
{
case 1:
P1_1.Visible = true;
break;
case 2:
P1_2.Visible = true;
break;
}*/
/*String[] hi = { "1", "2" };
for (int i = 0; i < hi.Length; i++)
{
var visible = p1
if(visible == hi[i])
{
hi[i].Visible = true;
}
else
{
hi[i].Visible = false;
}
}*/
}
}
(P1-1 all the way to P1-49 are images)
Thanks,
James
It looks like you're trying to pass an int to your SelectPos function but it expects a PictureBox. You could fix this doing something similar to the following:
private void SelectPos(int pic)
{
PictureBox[] numbers = { P1_1, P1_2, P1_3, P1_4, P1_5, P1_6, P1_7, P1_8, P1_9, P1_10, P1_11, P1_12, P1_13, P1_14, P1_15, P1_16, P1_17, P1_18, P1_19, P1_20, P1_21, P1_22, P1_23, P1_24, P1_25, P1_26, P1_27, P1_28, P1_29, P1_30, P1_31, P1_32, P1_33, P1_34, P1_35, P1_36, P1_37, P1_38, P1_39, P1_40, P1_41, P1_42, P1_43, P1_44, P1_45, P1_46, P1_47, P1_48, P1_49 };
//Set all picture boxes to be not visible
for (int i = 0; i < numbers.Length; i++)
{
numbers[i].Visible = false;
}
//Set the picture at the given index to visible
numbers[pic].Visible = true;
}
private void bunifuFlatButton1_Click(object sender, EventArgs e)
{
Roll();
System.Threading.Thread.Sleep(100);
Roll2();
Goes_Num.Text = (int.Parse(Goes_Num.Text) + 1).ToString();
if (Convert.ToInt32(Goes_Num.Text) % 2 == 0)
{
WhichPlayer.Text = "Player 2";
P2_Number.Text = (int.Parse(P2_Number.Text) + 1).ToString();
int p2Int = Convert.ToInt32(P2_Pos.Text);
P2_Pos.Text = (p2Int + dice + dice2).ToString();
}
else if (Convert.ToInt32(Goes_Num.Text) % 2 != 0)
{
WhichPlayer.Text = "Player 1";
P1_Number.Text = (int.Parse(P1_Number.Text) + 1).ToString();
int p1Int = Convert.ToInt32(P1_Pos.Text);
P1_Pos.Text = (p1Int + dice + dice2).ToString();
int P1 = (Convert.ToInt32(P1_Pos.Text));
SelectPos(P1);
}
}
You may have to manipulate the value of pic so that it is within the bounds of the array (0-48). For example if pic is between 1 and 49 you would need to subtract 1: numbers[pic-1]. Without seeing your whole program I can't tell you exactly how that part of the code would look but it should be pretty easy to figure out. If you aren't familiar with arrays and indexing check out this link or just Google C# Arrays.
As a side note it would be better to the numbers array as a private member of the class this code is in. Unless the values in the array change there's no point in building the array every time the method is called.
Complete code:
private void SelectPos(int pic)
{
PictureBox[] numbers = { P1_1, P1_2, P1_3, P1_4, P1_5, P1_6, P1_7, P1_8, P1_9, P1_10, P1_11, P1_12, P1_13, P1_14, P1_15, P1_16, P1_17, P1_18, P1_19, P1_20, P1_21, P1_22, P1_23, P1_24, P1_25, P1_26, P1_27, P1_28, P1_29, P1_30, P1_31, P1_32, P1_33, P1_34, P1_35, P1_36, P1_37, P1_38, P1_39, P1_40, P1_41, P1_42, P1_43, P1_44, P1_45, P1_46, P1_47, P1_48, P1_49 };
//Set all picture boxes to be not visible
for (int i = 0; i < numbers.Length; i++)
{
numbers[i].Visible = false;
}
//Set the picture at the given index to visible
numbers[pic].Visible = true;
}
private void SelectPos2(int pic2)
{
PictureBox[] numbers2 = { P2_1, P2_2, P2_3, P2_4, P2_5, P2_6, P2_7, P2_8, P2_9, P2_10, P2_11, P2_12, P2_13, P2_14, P2_15, P2_16, P2_17, P2_18, P2_19, P2_20, P2_21, P2_22, P2_23, P2_24, P2_25, P2_26, P2_27, P2_28, P2_29, P2_30, P2_31, P2_32, P2_33, P2_34, P2_35, P2_36, P2_37, P2_38, P2_39, P2_40, P2_41, P2_42, P2_43, P2_44, P2_45, P2_46, P2_47, P2_48, P2_49 };
//Set all picture boxes to be not visible
for (int i = 0; i < numbers2.Length; i++)
{
numbers2[i].Visible = false;
}
//Set the picture at the given index to visible
numbers2[pic2].Visible = true;
}
private void bunifuFlatButton1_Click(object sender, EventArgs e)
{
Roll();
System.Threading.Thread.Sleep(100);
Roll2();
Goes_Num.Text = (int.Parse(Goes_Num.Text) + 1).ToString();
if (Convert.ToInt32(Goes_Num.Text) % 2 == 0)
{
WhichPlayer.Text = "Player 2";
P2_Number.Text = (int.Parse(P2_Number.Text) + 1).ToString();
int p2Int = Convert.ToInt32(P2_Pos.Text);
P2_Pos.Text = (p2Int + dice + dice2).ToString();
int P2 = (Convert.ToInt32(P2_Pos.Text));
SelectPos2(P2);
}
else if (Convert.ToInt32(Goes_Num.Text) % 2 != 0)
{
WhichPlayer.Text = "Player 1";
P1_Number.Text = (int.Parse(P1_Number.Text) + 1).ToString();
int p1Int = Convert.ToInt32(P1_Pos.Text);
P1_Pos.Text = (p1Int + dice + dice2).ToString();
int P1 = (Convert.ToInt32(P1_Pos.Text));
SelectPos(P1);
}
}
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
i was doing a ITP assignment, when i got an error. The code for the part with the problem is:
private void btnAddWord_Click(object sender, EventArgs e)
{
//if the textbox is empty
if (string.IsNullOrEmpty(tbxAddWord.Text))
{
MessageBox.Show("You have entered no characters in the textbox.");
tbxAddWord.Focus();
}
//if the number of items in the listbox is greater than 29
else if (lbxUnsortedList.Items.Count > 29)
{
MessageBox.Show("You have exceeded the maximum number of words in the list.");
tbxAddWord.Text = "";
}
//error message for entering word that is already in the list
bool contains = false;
for (int i = 0; i < lbxUnsortedList.Items.Count; i++)
{
if (lbxUnsortedList.Items[i].ToString().ToLower() == this.tbxAddWord.Text.ToString().ToLower())
{
contains = true;
}
}
//if there is no match in the list
if (!contains)
{
//add word to the listbox
lbxUnsortedList.Items.Add(tbxAddWord.Text);
//update tbxListBoxCount
tbxListboxCount.Text = lbxUnsortedList.Items.Count.ToString();
//onclick, conduct the bubble sort
bool swapped;
string temp;
do
{
swapped = false;
for (int i = 0; i < lbxUnsortedList.Items.Count - 1; i++)
{
int result = CarNameData[i].ToString().CompareTo(CarNameData[i + 1]);
if (result > 0)
{
temp = CarNameData[i];
CarNameData[i] = CarNameData[i + 1];
CarNameData[i + 1] = temp;
swapped = true;
}
}
} while (swapped == true);
tbxAddWord.Text = "";
}
// if there is a match in the list
else
{
MessageBox.Show("The word that you have added is already on the list");
tbxAddWord.Text = "";
tbxAddWord.Focus();
}
}
When i leave the textbox blank and click the add button, it comes up with the error message, but still adds a blank space. how do i stop this from happening?
You need to return from the method if you don't want to execute more code:
if (string.IsNullOrEmpty(tbxAddWord.Text))
{
MessageBox.Show("You have entered no characters in the textbox.");
tbxAddWord.Focus();
return;
}
//if the number of items in the listbox is greater than 29
else if (lbxUnsortedList.Items.Count > 29)
{
MessageBox.Show("You have exceeded the maximum number of words in the list.");
tbxAddWord.Text = "";
return;
}
First thing is if you are using CarNameData list as Generic collection list List<string> its allows inbuilt sort method like CarNameData.Sort();
Second thing you have to put your code in else part like this
private void btnAddWord_Click(object sender, EventArgs e)
{
//if the textbox is empty
if (string.IsNullOrEmpty(tbxAddWord.Text))
{
MessageBox.Show("You have entered no characters in the textbox.");
tbxAddWord.Focus();
}
else
{
//if the number of items in the listbox is greater than 29
if (lbxUnsortedList.Items.Count > 29)
{
MessageBox.Show("You have exceeded the maximum number of words in the list.");
tbxAddWord.Text = "";
}
//error message for entering word that is already in the list
bool contains = false;
for (int i = 0; i < lbxUnsortedList.Items.Count; i++)
{
if (lbxUnsortedList.Items[i].ToString().ToLower() == this.tbxAddWord.Text.ToString().ToLower())
{
contains = true;
}
}
//if there is no match in the list
if (!contains)
{
//add word to the listbox
lbxUnsortedList.Items.Add(tbxAddWord.Text);
//update tbxListBoxCount
tbxListboxCount.Text = lbxUnsortedList.Items.Count.ToString();
//onclick, conduct the bubble sort
bool swapped;
string temp;
do
{
swapped = false;
for (int i = 0; i < lbxUnsortedList.Items.Count - 1; i++)
{
int result = CarNameData[i].ToString().CompareTo(CarNameData[i + 1]);
if (result > 0)
{
temp = CarNameData[i];
CarNameData[i] = CarNameData[i + 1];
CarNameData[i + 1] = temp;
swapped = true;
}
}
} while (swapped == true);
tbxAddWord.Text = "";
}
// if there is a match in the list
else
{
MessageBox.Show("The word that you have added is already on the list");
tbxAddWord.Text = "";
tbxAddWord.Focus();
}
}
}