WPF (with C#) TextBox Cursor Position Problem - c#

I have a WPF C# program where I attempt to delete certain characters from a text box at TextChanged event. Say, for instance, the dollar sign. Here is the code I use.
private void txtData_TextChanged(object sender, TextChangedEventArgs e)
{
string data = txtData.Text;
foreach( char c in txtData.Text.ToCharArray() )
{
if( c.ToString() == "$" )
{
data = data.Replace( c.ToString(), "" );
}
}
txtData.Text = data;
}
The problem I have is that whenever the user enters $ sign (Shift + 4), at the TextChanged event it removes the $ character from the textbox text alright, but it also moves the cursor to the BEGINNING of the text box which is not my desired functionality.
As a workaround I thought of moving the cursor the the end of the text in the text box, but the problem there is that if the cursor was positioned at some middle position then it would not be very user friendly. Say, for instance the text in the textbox was 123ABC and if I had the cursor after 3, then moving the cursor to the end of the text would mean that at the next key stroke user would enter data after C, not after 3 which is the normal functionality.
Does anybody have an idea why this cursor shift happens?

Its not an answer to your question, but probably a solution for your problem:
How to define TextBox input restrictions?
If it is overkill for you, set e.Handled = true for all characters you want to avoid in PreviewKeyDown (use Keyboard.Modifiers for SHIFT key) or PreviewTextInput.
Try TextBox.CaretIndex for restoring cursor position in TextChanged event.
Hope it helps.

You can use the Select function of TextBox to change the cursor position.
private void textBox1_TextChanged(object sender, TextChangedEventArgs e)
{
textBox1.Text = textBox1.Text.Replace("$", "");
textBox1.Select(textBox1.Text.Length, 0);
}
You can see more about Position the Cursor on the MSDN

You can use the SelectionStart property of the textbox. Probably something along these lines should work:
private void txtData_TextChanged(object sender, TextChangedEventArgs e)
{
var pos = txtData.SelectionStart;
string data = txtData.Text.Replace("$", "");
txtData.Text = data;
txtData.SelectionStart = pos;
}

You can try Regular Expression
Sample
1) Use PreviewTextInput="CursorIssueHandler" in .xaml file
2) In your .cs file ,write the below code:
private void CursorIssueHandler(object sender, TextCompositionEventArgs e)
{
var TB = (sender as TextBox);
Regex regex = new Regex("[^0-9a-zA-Z-]+");
bool Valid = regex.IsMatch(e.Text);
//System.Diagnostics.Debug.WriteLine(Valid); // check value for valid n assign e.Handled accordingly your requirement from regex
e.Handled = Valid;
}

Related

How to highlight entire TextBox on triple click?

When there are spaces between the text in the Input TextBox, my triple click only highlights up to the next space instead of the whole TextBox. Help?
Subscribe to the DoubleClick event:
private void textBox1_DoubleClick(object sender, EventArgs e)
{
textBox1.SelectionStart = 0; // set the selection start index to the beginning
textBox1.SelectionLength = textBox1.Text.Length; // set the selection length to the length of the text
}
https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.control.doubleclick?view=windowsdesktop-7.0

(C#) Getting the index from a remove char of a TextBox WinForms

I have a TextBox where the user can add and remove text, however each char relates to a row on a DataGridView, with a variety of user-selectable options. So knowing what char is being removed is very important because the DataGridView needs to know what row must be removed.
At first, I had a simple string-compare method, but with duplicate char sequences (e.i. "aaaa") it couldn't figure out which letter was removed and defaulted to returning the index of last char in the sequence. So I went online to see if there was a way to track the text caret's position, and there is...but not for WinForms. The only aspects I found for the Caret was SelectionStart, SelectionLength and SelectionText; which will be usefully for batch remove, but not when the user hits the backbutton/deletebutton.
I'm pretty stumped right now. The "easiest" solution is switching to XAML because it tracks the Caret Position... but that feels like quiter talk. Though with that said, I still have no idea how to tackle this problem.
You can try to define the variable originalText to save origianl textbox text and textlength to save text length.
Subscribe to textBox1_Enter to give them the initial value.
private void textBox1_Enter(object sender, EventArgs e)
{
textlength = textBox1.Text.Length;
originalText = textBox1.Text;
}
Then subscribe to textBox1_TextChanged to get the deleted chars.
int textlength;
string originalText;
private void textBox1_TextChanged(object sender, EventArgs e)
{
if (textlength > textBox1.Text.Length)
{
Console.WriteLine($"You deleted the char from {textBox1.SelectionStart} to {textBox1.SelectionStart + textlength - textBox1.Text.Length - 1}");
Console.WriteLine($"deleted substring {originalText.Substring(textBox1.SelectionStart, textlength - textBox1.Text.Length)}");
}
// reset
textlength = textBox1.Text.Length;
originalText = textBox1.Text;
}

How to set TextBox to only accept numbers?

I have already checked other questions here but the answers are not related to my issue. the following code allows textbox1 to only accept numbers if the physical keyboard (laptop) is pressed:
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
char ch = e.KeyChar;
if ( !char.IsDigit(ch))
{
e.Handled = true;
}
}
but this is not what I wanted (I dont use physical laptop keyboard).
As shown in screenshot, I have windows form with buttons and a textbox. I designed this keyboard and it works well but I want textbox1 to only accept numbers and the ".".
There are only two lines of code inside each button (and only code in the project) which is:
private void buttonName_Click(object sender, EventArgs e)
{
// each button only has this code.
textBox1.Focus();
SendKeys.Send(buttonName.Text);
}
I know how to set txtbox to accept numbers if the physical (laptop ) keys are pressed but here in this case I have control buttons in windwos form and I want to set textBox1 to only accept numbers and the ".". Please help in how to achieve this. Thank you
Declare a string variable at form level, use it to store the last valid text and to restore it when an invalid text is entered on the TextChanged event of your textbox.
string previousText;
public Form1()
{
InitializeComponent();
previousText = String.Empty;
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
int dummy, changeLenght, position;
if (!String.IsNullOrWhiteSpace(textBox1.Text) && !int.TryParse(textBox1.Text, out dummy))
{
position = textBox1.SelectionStart;
changeLenght = textBox1.TextLength - previousText.Length;
textBox1.Text = previousText;
textBox1.SelectionStart = position - changeLenght;
}
else
{
previousText = textBox1.Text;
}
}
position and changeLenght are used to keep the cursor where it was before restoring the text.
In case you want to accept numbers with decimals or something bigger than 2147483647, just change dummy to double and use double.TryParse instead of int.TryParse.
private void textBox1_TextChanged(object sender, EventArgs e)
{
int changeLenght, position;
double dummy;
if (!String.IsNullOrWhiteSpace(textBox1.Text) && !double.TryParse(textBox1.Text, out dummy))
{
...
}
}
Suppose button1 is your button control, you could do this:
private void allButtons_Click(object sender, EventArgs e)
{
Button btn = sender as Button;
char c = btn.Text[0]; //assuming all buttons have exactly 1 character
if(Char.IsDigit(c) || c == '.')
{
//process
textBox1.Focus();
SendKeys.Send(btn.Text);
}
//otherwise don't
}
I'm assuming you put this in a common handler, to which you already wired all your buttons (i.e. allButtons_Click).
Problem with this approach, it allows you to type values like 0.0.1, which are most likely invalid in your context. Another way to handle this is to process TextChanged event, store previous value, and if new value is invalid, restore the old one. Unfortunately, TextBox class does not have TextChanging event, which could be a cleaner option.
The benefit of you determining the invalid value is modularity. For example, if you later decide your user can enter any value, but only numbers can pass validation, you could move your check from TextChanged to Validate button click or similar.
Why users may want that - suppose one of the options for input is copy/paste - they want to paste invalid data and edit it to become valid, for example abc123.5. If you limit them at the entry, this value will not be there at all, so they now need to manually paste into Notepad, cut out in the invalid characters, and paste again, which goes against productivity.
Generally, before implementing any user interface limitation, read "I won't allow my user to...", think well, whether it's justified enough. More often than not, you don't need to limit the user, even for the good purpose of keeping your DB valid etc. If possible, never put a concrete wall in front of them, you just need to guide them correctly through your workflow. You want users on your side, not against you.

How to make new event when hover on a text in RichTextBox?

I have a RichTextBox that contains a string, e.g: "Hello", and I want to create a new event when I hover the mouse over the word "Hello", or to simplify that, showing a message box when hover on the word "Hello". So how to achieve that?
First off let's define a method that gets the word nearest to the cursor:
public static class Helper
{
public static string GetWordUnderCursor(RichTextBox control, MouseEventArgs e)
{
//check if there's any text entered
if (string.IsNullOrWhiteSpace(control.Text))
return null;
//get index of nearest character
var index = control.GetCharIndexFromPosition(e.Location);
//check if mouse is above a word (non-whitespace character)
if (char.IsWhiteSpace(control.Text[index]))
return null;
//find the start index of the word
var start = index;
while (start > 0 && !char.IsWhiteSpace(control.Text[start - 1]))
start--;
//find the end index of the word
var end = index;
while (end < control.Text.Length - 1 && !char.IsWhiteSpace(control.Text[end + 1]))
end++;
//get and return the whole word
return control.Text.Substring(start, end - start + 1);
}
}
In order to raise MouseMove event ONLY if the cursor is above RichTextBox and the nearest word is "Hello" you'll need to define your own control deriving from RichTextBox and override the OnMouseMove method, and use it in your form instead RichTextBox:
public class MyRichTextBox : RichTextBox
{
protected override void OnMouseMove(MouseEventArgs e)
{
//get the word under the cursor
var word = Helper.GetWordUnderCursor(this, e);
if (string.Equals(word, "Hello"))
{
//let RichTextBox raise the event
base.OnMouseMove(e);
}
}
}
However, in my opinion, it's better to let the RichTextBox raise the MouseMove event normally and take action only if conditions are met. In order to do that you only need to register MouseMove handler and check the conditions:
private void richTextBox1_MouseMove(object sender, MouseEventArgs e)
{
var control = sender as RichTextBox;
//get the word under the cursor
var word = Helper.GetWordUnderCursor(control, e);
if (string.Equals(word, "Hello"))
{
//do your stuff
}
}
I think you can use the Cursor class to achieve this. Some people tried to achieve similar things. Have a look here.
Make sure you have an event 'richTextBox1_MouseHover' wired to the hover of your Rich Text Box.
private void richTextBox1_MouseHover(object sender, EventArgs e)
{
MessageBox.Show("Hello");
}

How to move caret to the beginning of text inside a Textbox?

I tried using this code but It doesn't work
private void textBox1_Enter(object sender, EventArgs e)
{
this.textBox1.Select(0, 0);
}
I want whenever the user click on the textbox, the caret position will be at the beginning of text instead of being in the position when user clicked ?
How to move caret to the beginning of text inside a Textbox ?
Use the MouseClick Event :
private void textBox1_MouseClick(object sender, MouseEventArgs e)
{
textBox1.Select(0, 0);
}
Note that this will not work if you enter the TextBox through Tab.
You can use SelectionStart and SelectionLenght property . for example ,
SelectionStart = 0;
Selectionlenght = 0;
You can use these code in Enter event .

Categories