I have a RichTextBox for which I have created a "scroll through sent messages" feature. This means that if the user sends (by pressing enter) any messages (up to 20), the user can then scroll through them in order by pressing the up/down arrows on the keyboard. This part works as intended.
The issue is: I want to select all text in the RichTextBox after each arrow press.
Here is my code:
private void rtb_inputField_KeyDown(object sender, KeyEventArgs e) {
RichTextBox inputField = (RichTextBox)sender;
string userInput = inputField.Text.Trim();
string[] clientScriptString = userInput.Split(' ');
string modifiedInput = string.Empty;
string macroString = string.Empty;
//Other code...
if (e.KeyData == Keys.Enter) {
//Other code..
if ((client != null) && (client.Connected)) {
macroString = runInputThroughMacroDictionary(userInput);
sendInputToServer(macroString); //Sent message
}
addSentMessageToHistory(userInput); //Added sent message to history
e.Handled = true;
e.SuppressKeyPress = true;
hasUpBeenPressed = false;
}
if ((e.KeyData == Keys.Up) || (e.KeyData == Keys.Down)) {
scrollThroughSentMessages(e);
rtb_inputField.SelectAll();
}
}
private void scrollThroughSentMessages(KeyEventArgs e) {
if (sentMessageHistory.Count > 0) {
if (e.KeyData == Keys.Up) {
if (!hasUpBeenPressed) {
scrollThroughMsgHist = sentMessageHistory.Count - 1;
}
else {
scrollThroughMsgHist--;
if (scrollThroughMsgHist < 0) {
scrollThroughMsgHist = sentMessageHistory.Count - 1;
}
}
hasUpBeenPressed = true;
}
if ((e.KeyData == Keys.Down) && (hasUpBeenPressed)) {
scrollThroughMsgHist++;
if ((scrollThroughMsgHist >= 20) || (scrollThroughMsgHist > (sentMessageHistory.Count - 1))) {
scrollThroughMsgHist = 0;
}
}
rtb_inputField.Text = sentMessageHistory[scrollThroughMsgHist];
}
}
private void addSentMessageToHistory(string message) {
int currentAmountOfMessage = sentMessageHistory.Count;
if (!string.IsNullOrWhiteSpace(message)) {
if (currentAmountOfMessage == 0) {
sentMessageHistory.Add(message);
}
else {
if (sentMessageHistory[currentAmountOfMessage - 1] != message) {
if (currentAmountOfMessage == 20) {
sentMessageHistory.RemoveAt(0);
}
sentMessageHistory.Add(message);
}
}
}
}
The first method does a whole bunch of things. (I removed some of the irrelevant code.) It adds the last sent message to sentMessageHistory (which is a List) via the third method. It also listens for Up/Down, and calls upon the second method.
The second method "scrolls through" each of the entries in the sentMessageHistory -List, and writes it in the RichTextBox. All of this works fine.
What doesn't work is the rtb_inputField.SelectAll(); after the second method has run.
I found the solution. I added the following:
if ((e.KeyData == Keys.Up) || (e.KeyData == Keys.Down)) {
scrollThroughSentMessages(e);
e.Handled = true;
e.SuppressKeyPress = true;
rtb_inputField.SelectAll();
}
I don't know what e.Handled and e.SupressKeyPress does, but it works. So now my code successfully selects the text after "scrolling through history".
Related
I must capitalize first letter of every word but with keypress event using C#. Right now every letter in the textbox gets capitalized, I added the code I use. I cant figure out how to capitalize just the first letter, or what am I doing wrong. Can you help me?
private void txt_name_KeyPress(object sender, KeyPressEventArgs e)
{
e.KeyChar = (e.KeyChar.ToString()).ToUpper().ToCharArray()[0];
}
You will need to keep track of previous key presses if you must go this route:
private char PreviousChar;
private void txt_name_KeyPress(object sender, KeyPressEventArgs e)
{
if (Char.IsWhiteSpace(PreviousChar) || PreviousChar == '\0')
{
e.KeyChar = Char.ToUpper(e.KeyChar);
}
PreviousChar = e.KeyChar;
}
I got by using Keydown
private void tbxName_KeyDown(object sender, KeyEventArgs e)
{
if (tbxName.Text.Length == 0 || tbxName.SelectedText.Length == tbxName.Text.Length)
{
if ((e.Key >= Key.A) && (e.Key <= Key.Z))
{
tbxName.Text = e.Key.ToString().ToUpper();
tbxName.SelectionStart = tbxName.Text.Length;
e.Handled = true;
}
}
if (tbxName.Text.Length > 0)
{
int selectionStart = tbxName.SelectionStart;
string character = tbxName.Text.Substring(selectionStart - 1, 1);
if (character.Trim() == string.Empty)
{
if ((e.Key >= Key.A) && (e.Key <= Key.Z))
{
tbxName.Text = tbxName.Text.Insert(selectionStart, e.Key.ToString().ToUpper());
tbxName.SelectionStart = selectionStart + 1;
e.Handled = true;
}
}
}
if (e.Key == Key.Enter || e.Key == Key.Tab)
{
if (tbxName.Text.Length > 2)
{
tbxDegree.Focus();
if (e.Key == Key.Tab)
e.Handled = true;
}
else
{
MessageBox.Show("Kindly verify the Name Entered");
tbxName.Focus();
}
}
}
I have textbox and when i create that form i pass some values and then i prevent user to input some things into textbox based on passed values with this code:
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
if (!brojevi && char.IsDigit(e.KeyChar))
{
e.Handled = true;
return;
}
if (!slova && char.IsLetter(e.KeyChar))
{
e.Handled = true;
return;
}
if (!znakovi && char.IsPunctuation(e.KeyChar) || !znakovi && char.IsSymbol(e.KeyChar))
{
e.Handled = true;
return;
}
if (!razmak && char.IsSeparator(e.KeyChar))
{
e.Handled = true;
return;
}
if (maxKaraktera != -1 && (textBox1.Text.Length + 1) > maxKaraktera)
{
e.Handled = true;
return;
}
if (String.IsNullOrWhiteSpace(textBox1.Text))
{
return;
}
if (maxBroj != -1 && Convert.ToDouble(textBox1.Text) > maxBroj)
{
e.Handled = true;
return;
}
}
Problem is that i enabled brojevi and set maxBroj to 10
Now when i try typing some char, it checks and see that slova is not true, set e.Handled = true and return and in my textbox that char is not imputed which is ok.
But when i try inserting number which is greater than 10 (let's say 12), it goes to if statement where it checks if(maxBroj != -1 && Convert.ToDoube(textBox1.Text) > maxBroj) and it enters it, set e.Handled = true and return but number is implemented in textbox.
Why this happens?
EDIT: Code from creating form and form that has textbox
Creating from:
MessageBoxWithValue msg = new MessageBoxWithValue("Unesite kolicinu", "Unesite zeljenu kolicinu. Maksimum: " + aa.maxKolicina.ToString());
msg.brojevi = true;
msg.maxBroj = aa.maxKolicina;
msg.ShowDialog();
if(msg.DialogResult == DialogResult.OK)
{
kol = Convert.ToDouble(msg.returnValue);
}
else
{
return;
}
Here is MessageBoxWithValue form:
public partial class MessageBoxWithValue : Form
{
public bool brojevi = false;
public bool slova = false;
public bool znakovi = false;
public bool razmak = false;
public double maxBroj = -1;
public int maxKaraktera = -1;
public string returnValue;
public MessageBoxWithValue(string naslov, string opis)
{
InitializeComponent();
this.Text = naslov;
label1.Text = opis;
}
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
if (!brojevi && char.IsDigit(e.KeyChar))
{
e.Handled = true;
return;
}
if (!slova && char.IsLetter(e.KeyChar))
{
e.Handled = true;
return;
}
if (!znakovi && char.IsPunctuation(e.KeyChar) || !znakovi && char.IsSymbol(e.KeyChar))
{
e.Handled = true;
return;
}
if (!razmak && char.IsSeparator(e.KeyChar))
{
e.Handled = true;
return;
}
if (maxKaraktera != -1 && (textBox1.Text.Length + 1) > maxKaraktera)
{
e.Handled = true;
return;
}
if (String.IsNullOrWhiteSpace(textBox1.Text))
{
return;
}
if (maxBroj != -1 && Convert.ToDouble(textBox1.Text) > maxBroj)
{
e.Handled = true;
return;
}
}
private void button2_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
this.Close();
}
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if(e.KeyCode == Keys.Enter || e.KeyCode == Keys.Return)
{
Uspesno();
}
}
private void button1_Click(object sender, EventArgs e)
{
Uspesno();
}
private void Uspesno()
{
this.DialogResult = DialogResult.OK;
returnValue = textBox1.Text;
}
}
But when i try inserting number which is greater than 10 (let's say 12), it goes to if statement where it checks if(maxBroj != -1 && Convert.ToDoube(textBox1.Text) > maxBroj) and it enters it, set e.Handled = true and return but number is implemented in textbox.
I hope I've got your issue right. You are trying to filter numbers by their values (e.g. maxBroj is set to 10) and you are expecting
if (maxBroj != -1 && Convert.ToDouble(textBox1.Text) > maxBroj)
{
e.Handled = true;
return;
}
not to allow entering a number >10. Unfortunately this will not work as intended, since textBox1.Text won't be set until the KeyPress event handler has finished.
Let's say you are entering 14. When the first KeyPress event is raised (1), textBox1.Text is empty. You are returning from
if (String.IsNullOrWhiteSpace(textBox1.Text))
{
return;
}
Afterwards textBox1.Text is set to "1". Then the second KeyPress event is raised. textBox1.Text is "1", hence the method won't enter the block
if (maxBroj != -1 && Convert.ToDouble(textBox1.Text) > maxBroj)
{
e.Handled = true;
return;
}
Afterwards textBox1.Text will be set to "14", but this is too late for your validation.
You'll have to calculate the expected new value. Please see this question on how to insert the new character in the existing string.
Rewrite: I'm writing a program to get a better understanding of C# and taking user input in. Right now I'm using WFA in C# with a textbox where I put the text of what I want pasted/typed out. Then I have two other boxes that have an interval input and a amount input. Everything worked perfectly fine until I added the amount input, I've added a while loop to a timer that is used to type/paste the text. However, for some reason that I'm not sure of.. The program disregards any input for the interval.
I have it basically setup like this
private void timerPaste_Tick(object sender, EventArgs e)
{
interval = Int32.Parse(interNum.Text);
timerPaste.Interval = interval;
if (typeModebool == true && pasteModebool == false)
{
while (amount > 0) //Need to find a way to make GUI responsive during while loop.
{
SendKeys.Send(textBox1.Text);
SendKeys.Send("{Enter}");
amount--;
}
timerPaste.Enabled = false;
amount = Int32.Parse(amountSetBox.Text);
}
if (pasteModebool == true && typeModebool == false)
{
while (amount > 0) //Need to find a way to make GUI responsive during while loop.
{
Clipboard.SetText(textBox1.Text);
SendKeys.Send("^{c}");
SendKeys.Send("^{v}");
SendKeys.Send("{Enter}");
amount--;
}
timerPaste.Enabled = false;
amount = Int32.Parse(amountSetBox.Text);
}
}
something like that..?
timerPaste.Elapsed += OnTickEvent;
private async void OnTickEvent(Object source,EventArgs e)
{
interval = Int32.Parse(interNum.Text);
timerPaste.Interval = interval;
if (typeModebool == true && pasteModebool == false)
{
while (amount > 0) //Need to find a way to make GUI responsive during while loop.
{
SendKeys.Send(textBox1.Text);
SendKeys.Send("{Enter}");
amount--;
}
timerPaste.Enabled = false;
amount = Int32.Parse(amountSetBox.Text);
}
if (pasteModebool == true && typeModebool == false)
{
while (amount > 0) //Need to find a way to make GUI responsive during while loop.
{
Clipboard.SetText(textBox1.Text);
SendKeys.Send("^{c}");
SendKeys.Send("^{v}");
SendKeys.Send("{Enter}");
amount--;
}
timerPaste.Enabled = false;
amount = Int32.Parse(amountSetBox.Text);
}
}
I actually was able to fix this, the problem was that the while loop listened to the timer the first time, then after that just started going off on it's own. I switched it to a if, with an else statement and everything works as intended now.
int i = 0;
private void timerPaste_Tick(object sender, EventArgs e)
{
if (typeModebool == true && pasteModebool == false) //Check what mode is being used
{
if (i < amount) //If runs until i == amount
{
SendKeys.Send(textBox1.Text);
SendKeys.Send("{Enter}");
amount--;
}
else
{
timerPaste.Enabled = false;
}
}
if (pasteModebool == true && typeModebool == false)
{
if (i < amount)
{
Clipboard.SetText(textBox1.Text);
SendKeys.Send("^{c}");
SendKeys.Send("^{v}");
SendKeys.Send("{Enter}");
amount--;
}
else
{
timerPaste.Enabled = false;
}
}
}
I have a strange problem. In Winforms app i have a datagridview where i implemented some code for moving inside of it with arrow keys and enter key...
class CalibDataGridView : System.Windows.Forms.DataGridView
{
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
int row = 0;
int col = 0;
// return base.ProcessCmdKey(ref msg, keyData);
switch (keyData)
{
case Keys.Down:
return true;
case Keys.Up:
return true;
case Keys.Right:
row = this.CurrentCell.RowIndex;
col = this.CurrentCell.ColumnIndex;
while (true)
{
col++;
if (col == this.Columns.Count)
{
row++;
col = 0;
}
if (row == this.Rows.Count)
{
col = 0;
row = 0;
}
if (!this[col, row].ReadOnly && this[col, row].Visible) break;
}
this.CurrentCell = this[col, row];
return true;
case Keys.Left:
row = this.CurrentCell.RowIndex;
col = this.CurrentCell.ColumnIndex;
while (true)
{
col--;
if (col < 0)
{
row--;
col = this.Columns.Count - 1;
}
if (row < 0)
{
row = this.Rows.Count - 1;
col = this.Columns.Count - 1;
}
if (!this[col, row].ReadOnly && this[col, row].Visible) break;
}
this.CurrentCell = this[col, row];
return true;
case Keys.Enter:
row = this.CurrentCell.RowIndex;
col = this.CurrentCell.ColumnIndex;
while (true)
{
row++;
if (row == this.Rows.Count)
{
row = 0;
}
if (!this[col, row].ReadOnly && this[col, row].Visible) break;
}
this.CurrentCell = this[col, row];
return true;
default:
return base.ProcessCmdKey(ref msg, keyData);
}
}
}
The code works fine, but i'm having a problem implement function to move to next control when TAB key is pressed. I added some code in PreviewKeyDown function of datagridview:
private void dgvRepeatability1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
if (e.KeyCode == Keys.PageDown && tbcMain.SelectedIndex > 0)
{
tbcMain.SelectedIndex--; //changes tab page
}
else if (e.KeyCode == Keys.PageUp && tbcMain.SelectedIndex < 7)
{
tbcMain.SelectedIndex++;
}
else if (e.KeyCode == Keys.Tab) //does not work like it should!!!!
{
Console.Beep(1000, 200);
this.SelectNextControl((Control)sender, true, true, true, true);
}
}
This last code works in strange way... when triggered it somewhere in the background changes focus like it should (it beeps and the next control changes background color so i can see it is focused), but then i lose focus on the whole form... i can press TAB or ARROW KEYS or any other key and nothing happens....
If i minimize the form and then maximize it again the focus goes to the next control like it should in the first place and everything works like it should.
I also tryed change focus with .Select() and .Focus() and did not work.
Thanks in advance for help!
You can use SelectNextControl:
private void dataGridView1_KeyDown(object sender, KeyEventArgs e)
{
if ( e.KeyCode == Keys.Tab )
{
e.Handled = true;
this.SelectNextControl((Control)sender, true, true, true, true);
}
You can also find the next control yourself:
private void dataGridView1_KeyDown(object sender, KeyEventArgs e)
{
if ( e.KeyCode == Keys.Tab )
{
e.Handled = true;
var dataGrid = (DataGridView) sender;
var tabIndex = dataGrid.TabIndex;
var controls = this.Controls.Cast<Control>().Where( r => r.TabIndex > tabIndex );
if ( controls.Any() )
{
controls.OrderBy(r => r.TabIndex).First().Select();
}
else
{
this.Controls.Cast<Control>()
.Where( r => r.TabIndex <= tabIndex )
.OrderBy( r => tabIndex )
.First()
.Select();
}
}
}
Based on:
C# windows form & using WMPLib
The Problem:
when pressing next or prev button too fast, the apps will freeze a bit (uncontrollable).
The Code:
private void next_event()
{
_paused = false;
if (list[current_index].Rows.Count != 0 && _playing == true)
{
if (option.random.Checked == true)
{
nextRandom(1);
}
else if (option.order.Checked == true)
{
if (list[current_index].Rows.IndexOf(_musicData[_NowPlaying]) == list[current_index].Rows.Count - 1)
{
setNowPlaying((Guid)list[current_index].Rows[0].Cells["Key"].Value);
}
else
{
setNowPlaying((Guid)list[current_index].Rows[list[current_index].Rows.IndexOf(_musicData[_NowPlaying]) + 1].Cells["Key"].Value);
}
}
seek_bar.Value = 0;
play();
}
}
private void prev_event()
{
_paused = false;
if (list[current_index].Rows.Count != 0 && _playing == true)
{
if (option.random.Checked == true)
{
nextRandom(2);
}
else if (option.order.Checked == true)
{
if (list[current_index].CurrentRow.Index == 0)
{
setNowPlaying((Guid)list[current_index].Rows[list[current_index].Rows.Count - 1].Cells["Key"].Value);
}
else
{
setNowPlaying((Guid)list[current_index].Rows[list[current_index].Rows.IndexOf(_musicData[_NowPlaying]) - 1].Cells["Key"].Value);
}
}
seek_bar.Value = 0;
play();
}
}
private void play() // play
{
button3.Text = "Pause";
dmp_status.Text = "Playing...";
_paused = false;
if (list[current_index].Rows.Count != 0)
{
if (File.Exists(_musicData[_NowPlaying].Cells["FileLocation"].Value.ToString()))
{
if (_playing == true) wmp.controls.stop();
if(wmp != null) wmp.close();
wmp = new WMPLib.WindowsMediaPlayer();
wmp.PlayStateChange += new WMPLib._WMPOCXEvents_PlayStateChangeEventHandler(PlayStateChange);
seek_bar.Value = 0;
setNowPlayingLabel(_musicData[_NowPlaying]);
if (!_musicData.ContainsKey(_LastPlaying)) _LastPlaying = Guid.Empty;
setNowPlayingColor(_musicData[_LastPlaying],_musicData[_NowPlaying]);
//wmp.URL = list[current_index].CurrentRow.Cells["FileLocation"].Value.ToString();
wmp.URL = _musicData[_NowPlaying].Cells["FileLocation"].Value.ToString();
wmp.controls.play();
wmp.settings.rate = wmp_rate;
wmp.settings.volume = volume_pos;
if (_playing == false) _playing = true;
}
else
{
next_event();
}
}
}
The Question:
I have hard time figuring why they need a delay(freeze) before playing each music, this making a problem when user press next button consecutively.
I think it is due to reentry. Happened when subsequent events are fire while the first event hasn't complete. To prevent this:
// add this line to your class member
private Object syncRoot = new Object();
// add this block to your next_event() method
if (!Monitor.TryEnter(syncRoot)) return;
try {
// add your existing code here
}
finally {
Monitor.Exit(syncRoot);
}