I would like to allow only one instance of a character to appear in sequence in a textbox. In other words, I would like to prevent the user from entering sequential duplicate characters in my textbox. For example, if I were to make a calculator program, I would like to prevent the user from entering (( by accident instead of (.
You can detect the textbox's keydown event. And in the event, you check the last and second last character to see if it's the same. If it's the same, you can just remove the last character of the string.
But keep in mind that the above method has problem when your user pastes the entire string to the textbox.
The function will look the same for any textbox-like object, but of course the event to attach to will differ. Assuming Winforms TextBox, you will attach to the TextChanged event (i.e. mytextbox.TextChanged = mytextbox_TextChanged), and the function will look similar to the one below:
private void mytextbox_TextChanged(object sender, EventArgs e)
{
if (mytextbox.Text.Length < 2) { return; }
var stringToCheck = mytextbox.Text;
for (var i = 1; i <= stringToCheck.Length-1; i += 1)
{
if (stringToCheck[i].Equals(stringToCheck[i-1])
{
mytextbox.Text = stringToCheck.Remove(i, 1);
// Next two lines put cursor at end of textbox instead of beginning
mytextbox.SelectionStart = mytextbox.Text.Length;
mytextbox.SelectionLength = 0;
}
}
}
Related
Im trying to make a cookie clicker style thing where every time you click something it adds 1 to a variable. Im trying to do int clicks = clicks + 1 but it says that it is use of an uninitialized variable. I tried to set int clicks = 0 but then it says that clicks is already defined in the scope. I tried to see if i could do something like if (clicks == null) but obviously it cant check it because it is not a variable. I have only used c# for like a day, can someone please help?
private void cookie_Click(object sender, EventArgs e)
{
int clicks = 0;
clicks = clicks + 1;
numClicks.Text = "" + clicks;
}
^ this is the code. i also realized when i click it, it resets itself to 0 and goes back to 1, so it cant go 1, 2, 3 etc. is there a way to set the variable when the form starts and then start to add on clicks? im so dumb
int clicks = clicks + 1;
Is indeed nonsensical. This is the code that is declaring and initializing clicks, so it makes perfect sense that we can't ask "what is the value of clicks?" (in order so we can add one to it); until we have definitely assigned a value to clicks, the value is undefined.
Instead:
int clicks = 0;
And then when you want to increment it:
clicks++;
You'll want to do something like this:
int clicks = 0;//Define an integer 'clicks' and set it to 0
//and in your click handler:
clicks = clicks + 1;//Increment your count.
Note:
Clicks is an integer, it can never be null. (look up c# primitives for more info)
Using int clicks = clicks + 1 doesn't make sense. At the right side of the equation, what's the value of clicks? It's not defined yet.
The problem with your implementation is that every time the function gets called the first thing it does is sets clicks to 0. You need to initialize it outside of the function to avoid this. If you initialize it in the class it will be set to 0 initially when the class is created then you can increase the value everytime the functions is called. Not sure exactly your use case but without more information I would recommend doing something like this.
class YourClass {
// initialize outside of the function that increases the counter
int clicks = 0;
// rest of the code in class
private void cookie_Click(object sender, EventArgs e)
{
clicks++;
// this would also work: clicks = clicks + 1;
numClicks.Text = "" + clicks;
}
}
The following code is being used as a generic event handler for 16 digit buttons on a hexadecimal calculator (0-9, A-F).
The following instructions define what I need to be accomplishing:
If the calculator is in display mode when a digit is pressed, that digit will replace the current content of the display and place the calculator in input mode. If the calculator is in input mode, there are three cases:
If the content of the display is "0", the digit on the button pressed will replace the content of the display.
Otherwise, if the content of the display contains fewer than eight characters (because we are dealing with 32-bit words), the digit on the button pressed will be appended to the content of the display.
Otherwise, the button press is ignored.
One button press on my calculator will update the display correctly. However, if I press another button, instead of appending the StringBuilder with the new character, it will instead display a double character for the last button pressed. Eg. One press of 'C' will display a 'C'. A press of 'C' then say '8' will display '88'. Where is my problem here?
public void ProcessClick(object sender, EventArgs e)
{
StringBuilder _button = new StringBuilder();
_button.Append(((Button)sender).Text);
if (mode)
{
uxDisplay.Text = _button.ToString();
mode = false;
}
else
{
if (uxDisplay.Text == "0")
{
uxDisplay.Text = _button.ToString();
}
else if (uxDisplay.Text.Length < 8)
{
uxDisplay.Text = _button.Append(((Button)sender).Text).ToString();
}
else
{
return;
}
}
}
You appear to be appending the value of sender.Text twice.
Here:
_button.Append(((Button)sender).Text);
and here:
uxDisplay.Text = _button.Append(((Button)sender).Text).ToString();
You are also creating a new StringBuilder on each call to Process so you aren't persisting the last value (apart from in the uxDisplay control)
How about something simple like:
...
else if (uxDisplay.Text.Length < 8)
{
uxDisplay.Text += ((Button)sender).Text;
}
You are only appending a small number of strings so you won't really gain all that much performance from using a StringBuilder (especially if you create a new one on each call! :P )
You are appending the pressed button text to your StringBuilder object directly after its creation, that's why you get twice the character.
You can go with something simple like this:
public void ProcessClick(object sender, EventArgs e)
{
if (mode)
{
uxDisplay.Text = _button.ToString();
mode = false;
}
else
{
if (uxDisplay.Text == "0")
{
uxDisplay.Text = _button.ToString();
}
else if (uxDisplay.Text.Length < 8)
{
uxDisplay.Text += ((Button)sender).Text;
}
else
{
return;
}
}
}
I have an if statement, inside that if statement is a foreach, for accessing each string from a string[].
The strings are some parameters for an NPC which are read from a file. The first one represents the NPC type which are two : "battle" and "teach", the last string[] for a "teach" NPC is "end", and the rest of parameters represents photo names, which I want to load in a "dialog" PictureBox.
My test file looks like this:
teach
poza1
poza2
end
So I have 2 photos to load in the dialog PictureBox. The idea is that I must pause for 5 seconds that foreach statement, otherwise the dialog PictureBox pictures will be loaded too fast, and I won't see them.
So I tried to do that, and here is how the code looks:
if (date[0].Equals("teach")) //the first line of the date[] string, date represent the text from the file
{
foreach (string parametru in date) // i think that you know what this does
{
if (parametru != "teach" && parametru != "end") // checking if the parameter isn't the first or the last line of the file
{
dialog.ImageLocation = folder + "/npc/" + score_npc + "/" + parametru + ".png"; //loading the photo
System.Threading.Thread.Sleep(5000);
}
}
//other instructions , irelevants in my opinion
}
In my attempt of debugging this, I realised that if I use a MessageBox, the function will load the both photos. Also I'm sure of the fact that the parameters will pass the if statement.
It seems so easy to fix this error, but I can't figure out how to do it.
What you're doing now just freezes the UI. Use a System.Windows.Forms.Timer instead. Drop a Timer from the toolbox onto your Form.
Then create some fields that the Timer can access, to store your pics and the current pic position:
private List<string> pics = new List<string>();
private int currentPic = 0;
Finally, load it up with the pics you want to display, and start the Timer to go through them:
pics.Clear();
pics.AddRange(date.Where(x => x != "teach" && x != "end"));
timer1.Interval = 5000;
timer1.Start();
Then you'll have to tell your Timer to display the next picture. Increase the counter, and reset it when necessary. Something like this should work. Modify as necessary.
private void timer1_Tick(object sender, EventArgs e)
{
dialog.ImageLocation = string.Format("{0}/npc/{1}/{2}.png", folder, score_npc, pics[currentPic]);
currentPic++;
if (currentPic >= pics.Count)
currentPic = 0;
// Alternatively, stop the Timer when you get to the end, if you want
// if (currentPic >= pics.Count)
// timer1.Stop();
}
You probably need to issue a PictureBox.Refresh and/or a DoEvents command for the picture box to actually get a chance to load and display the picture.
The MessageBox automatically performs a DoEvents ... which is why it is working during debugging.
I implemented a capitalize after interpunctation. BUT how to implement it, that the user can go BACK and delete the first word or character because he wants to continue in lower-case?
KeyPress(object sender, KeyPressEventArgs e)
{
if(EndOfSentence())
{
e.KeyChar = Char.ToUpper(e.Keychar);
}
}
//
private bool EndOfSentence()
{
//return true if end of sentence found
}
Example: If I write this sentence. I cannot go back and change the "I" to "i"! And I cannot change the "A" to "a" but I want to! How to code this?
Example Project here: http://www.filefactory.com/file/3ecbn51bhbrv/n/Capi.zip
The only Solution I see, is to save the current and previous key and check if backspace or delete was pressed like:
if (!EndOfSentence())
{
previousKeyChar = e.KeyChar;
return;
}
//
if(previousKeyChar.Equals('\b')) return;
else
e.KeyChar = Char.ToUpper(e.KeyChar);
//
//
// And in the EndOfSentence I Check
// if the cursor is at the end of the text
if(textbox1.Text.Length != textbox1.SelectionStart)
return false; //allow editing in the middle of the text
There may be better ways, but one is to store the entire sentence in a collection and check against the collection before modifying it again.
Or you could try changing your EndOfSentence function to only return true if you are also at the end of the input. That way, it should only capitalise characters typed at the end of the input, if you edit something in the middle of the text it should remain unchanged
I have an event handler for the TextBox.TextChanged event on a form of mine. In order to support undo, I'd like to figure out exactly what has changed in the TextBox, so that I can undo the change if the user asks for it. (I know the builtin textbox supports undo, but I'd like to have a single undo stack for the whole application)
Is there a reasonable way to do that? If not, is there a better way of supporting such an undo feature?
EDIT: Something like the following seems to work -- are there any better ideas? (It's times like this that I really wish .NET had something like the STL's std::mismatch algorithm...
class TextModification
{
private string _OldValue;
public string OldValue
{
get
{
return _OldValue;
}
}
private string _NewValue;
public string NewValue
{
get
{
return _NewValue;
}
}
private int _Position;
public int Position
{
get
{
return _Position;
}
}
public TextModification(string oldValue, string newValue, int position)
{
_OldValue = oldValue;
_NewValue = newValue;
_Position = position;
}
public void RevertTextbox(System.Windows.Forms.TextBox tb)
{
tb.Text = tb.Text.Substring(0, Position) + OldValue + tb.Text.Substring(Position + NewValue.Length);
}
}
private Stack<TextModification> changes = new Stack<TextModification>();
private string OldTBText = "";
private bool undoing = false;
private void Undoit()
{
if (changes.Count == 0)
return;
undoing = true;
changes.Pop().RevertTextbox(tbFilter);
OldTBText = tbFilter.Text;
undoing = false;
}
private void UpdateUndoStatus(TextBox caller)
{
int changeStartLocation = 0;
int changeEndTBLocation = caller.Text.Length;
int changeEndOldLocation = OldTBText.Length;
while (changeStartLocation < Math.Min(changeEndOldLocation, changeEndTBLocation) &&
caller.Text[changeStartLocation] == OldTBText[changeStartLocation])
changeStartLocation++;
while (changeEndTBLocation > 1 && changeEndOldLocation > 1 &&
caller.Text[changeEndTBLocation-1] == OldTBText[changeEndOldLocation-1])
{
changeEndTBLocation--;
changeEndOldLocation--;
}
changes.Push(new TextModification(
OldTBText.Substring(changeStartLocation, changeEndOldLocation - changeStartLocation),
caller.Text.Substring(changeStartLocation, changeEndTBLocation - changeStartLocation),
changeStartLocation));
OldTBText = caller.Text;
}
private void tbFilter_TextChanged(object sender, EventArgs e)
{
if (!undoing)
UpdateUndoStatus((TextBox)sender);
}
You might be better off using the Enter and Leave events instead. When entering, store the current text in a class variable, then when leaving compare the new text to the old.
Yes, don't tie it directly to the textbox. Your forms' state should be in some model object somewhere that isn't directly tied to the form (MVC is one way to do this, MVVM is another). By decoupling them like that, you can compare the new textbox value to the current model value whenever a change request comes in.
Actually, all I can think of is having some kind of collection where you store different string versions (so you can undo many times, not just once).
I would store the reference to TextBox's collections in TextBox.Tag, so it is straightforward to store/use it.
Last but not least, you update your collection of strings during the event TextChange. With no much work, you can maintain a full history, gettinjg the previous value from your own structure.
This is probably overkill for what you're trying to accomplish, but CSLA support n-level undo. CSLA is a great business objects framework written by Rocky Lhotka. The business objects handle the undo history and it flows to the UI through data binding.
Switching your app to use CSLA would be a big commitment, but another option would be to look through the freely available source code to see how he implemented it.
I am actually making an own Syntax-Highlight-System so I also need to know the changed text.
My solution is to watch for an enter or space or an depositioning of the cursor.
As WinForms provide the Keydown event I used the KeyEventArguments (e) and converted them to a char.
After that I storage the char into a string like :
string i="";
i+=convertedToChar; // convertedToChar = kc.ConvertToString(e.KeyData)
And as soon as there is a enter or space or depositioning - "event" I delete the string.
Result:
If a user enters a few chars and hit space I am able to read the last chars (till the last space).
An advantage would be the fact that you can use any delimiter char for that (as soon as they are storaged and provided by e.KeyCode)
However I hope that this is a solution for everybody watching this after 9years :D.
It´s never too late.