Long time listener, first time caller here. I'm having a strange issue with the TextBox in WinRT C#/XAML that I hope someone may be able to help me with.
Basically, I'm working on creating a Custom Control that essentially requires a second TextBox to be a copy of the first, including showing the same Text, and showing the same Selected Text. Obviously for the Text requirement I simply respond to the TextChanged event on the first TextBox and set the Text of the second TextBox to the Text from the first, which works great.
For the Selected Text requirement I started with a similar solution, and my code for this is as follows:
void TextBox1_SelectionChanged(object sender, RoutedEventArgs e)
{
this.TextBox2.Select(this.TextBox1.SelectionStart, this.TextBox1.SelectionLength);
}
This seemed to work pretty well when initially used with a mouse:
But I'm having a problem when selecting text with Touch. I double-tap within the TextBox to create the first "anchor" as you do in Touch, then drag to begin the selection; but I only ever manage to select a single character normally before the selection stops. The TextBox doesn't lose focus exactly, but the behaviour is similar to that; the selection anchors disappear and I can't continue selecting anything unless I re-double-tap to start a new selection. If I remove the code to select text in TextBox2 then the Touch selection behaves perfectly in TextBox1.
I've been trying to fix this for a while and cannot, I'm not sure if I can get the desired behaviour with WinRT TextBoxes. Does anyone have any ideas? Or perhaps another way to implement a solution with two TextBoxes with this behaviour?
Thanks a lot.
So this is far from an answer, but discovered a few things that maybe will help you or others come up with a potential workaround. Apologies if these are things you've already seen and noted.
First, it's not the call to TextBox2.Select() that's the problem per se. This for instance, works fine for me
private void txt1_SelectionChanged(object sender, RoutedEventArgs e)
{
var start = TextBox1.SelectionStart;
var length = TextBox1.SelectionLength;
TextBox2.Select(3, 5);
}
unfortunately, using start and length versus the hard-coded 3 and 5, that is, the following, DOES NOT WORK:
private void txt1_SelectionChanged(object sender, RoutedEventArgs e)
{
var start = TextBox1.SelectionStart;
var length = TextBox1.SelectionLength;
TextBox2.Select(start, length);
}
I also discovered that I could select TWO characters if I started from the end, but only one from the beginning. That got me to thinking about dispatching the call to set the second selection:
private void txt1_SelectionChanged(object sender, RoutedEventArgs e)
{
var start = TextBox1.SelectionStart;
var length = TextBox1.SelectionLength;
Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Low,
() => TextBox2.Select(start, length));
}
Now I can select 2 from the front and 3 and sometimes 4 from the back. Took it a step further, and was able to select as many as six or seven with a really fast swipe.
private void txt1_SelectionChanged(object sender, RoutedEventArgs e)
{
var start = TextBox1.SelectionStart;
var length = TextBox1.SelectionLength;
Dispatcher.RunIdleAsync((v) => Highlight());
}
public void Highlight()
{
TextBox2.Select(TextBox1.SelectionStart, TextBox1.SelectionLength);
}
Seems like the trick to working around this is not setting TextBox2 until whatever vestiges of the TextBox1 SelectionChanged event have completed.
This may be worth registering on Connect.
Mine is only a partial solution as well.
I did some debugging and noticed that the SelectionChanged event is fired throughout the text selection process. In other words, a single finger "swipe" will generate multiple SelectionChanged events.
As you found out, calling TextBox.Select during a text selection gesture affects the gesture itself. Windows seems to stop the gesture after the programmatic text selection.
My workaround is to delay as long as possible calling the TextBox.Select method. This does work well, except for one edge case. Where this method fails is in the following scenario:
The user begins a select gesture, say selecting x characters. The user, without taking their finger off the screen, pauses for a second or two. The user then attempts to select more characters.
My solution does not handle the last bit in the above paragraph. The touch selection after the pause does not actually select anything because my code will have called the TextBox.Select method.
Here is the actual code. As I mentioned above, there are multiple selection changed events fired during a single selection gesture. My code uses a timer along with a counter to only do the programmatic selection when there are no longer any pending touch generated selection changed events.
int _selectCounter = 0;
const int SELECT_TIMER_LENGTH = 500;
async private void TextBox1_SelectionChanged(object sender, RoutedEventArgs e)
{
// _selectCounter is the number of selection changed events that have fired.
// If you are really paranoid, you will want to make sure that if
// _selectCounter reaches MAX_INT, that you reset it to zero.
int mySelectCount = ++_selectCounter;
// start the timer and wait for it to finish
await Task.Delay(SELECT_TIMER_LENGTH);
// If equal (mySelectCount == _selectCounter),
// this means that NO select change events have fired
// during the delay call above. We only do the
// programmatic selection when this is the case.
// Feel free to adjust SELECT_TIMER_LENGTH to suit your needs.
if (mySelectCount == _selectCounter)
{
this.TextBox2.Select(this.TextBox1.SelectionStart, this.TextBox1.SelectionLength);
}
}
Related
Yes, I have already researched this question. I've found this: How to display remaining textbox characters in a label in C#? and many others just like it. That's how I managed to get this following code pieced together:
protected void rtdDisclaimer(object sender, EventArgs e)
{
lblCharCount.Text = "Characters Remaining:" + (700 - rtbDisclaimer.Text.Length).ToString(); // char count limit set to 700
}
I've never coded in c# before, but am working on a group project and that's the language the group lead chose. I'm new to programming and have minimal experience with java. This program is being done in visual studio. I'm trying to make the label show the number of characters remaining depending upon what's typed in the richtextbox. There are no errors, but the label isn't displaying anything at all.
You must associate the method to the textchanged event of the control.
protected void rtbDisclaimerTextChanged(object sender, EventArgs e)
{
lblCharCount.Text = "Characters Remaining:" + (700 - rtbDisclaimer.Text.Length).ToString(); // char count limit set to 700
}
On the constructor, after InitializeComponent(), yo must add this line:
rtbDisclaimer.TextChanged += new EventHandler(rtbDisclaimerTextChanged);
Your code will work, but you need to attach the method to the correct event on your RichTextBox, so it will invoke that code when the event fires.
Add this to the constructor of your Form:
rtbDisclaimer.TextChanged += rtdDisclaimer;
The function you mentioned in your question seems a server side and you can call it on a an event like a button's click.
If you want to display characters length while typing then use JavaScript/jQuery
like
$('textarea').keyup(updateCount);
$('textarea').keydown(updateCount);
function updateCount() {
var cs = $(this).val().length;
$('#characters').text(cs);
}
Check this too. JsFiddle example (by Dreami)
Hepe this helps!
I am using ScintillaNET to make a basic IntelliSense editor. However, I have a problem when I call _editor.CallTip.Show("random text") in the AutoCompleteAccepted Event.
If I type pr for example, and scroll and select printf in the drop-down list, it goes to my AutoCompleteAccepted event and when I call the CallTip.Show, the rest of the word does not get added (however, without that CallTip code, the rest of the word is filled).
So, if I typed pr then it stays pr and I get my CallTip. How do I make sure the rest of the word gets inserted AND the CallTip shows?
Is the AutoCompleteAccepted Event not the right place to call it? If so, where should I call the CallTip.Show so that it works side-by-side with my AutoComplete?
Finally figured it out! The AutoCompleteAccepted Event isn't the right place to put CallTip.Show
What is going on is the fact that when AutoCompleteAccepted Event is called and you add text to the ScintillaNET control, it takes time for the UI to update and so, when you call to show the CallTip, it interferes with the text being inserted into the control.
The better way to do it is to call CallTip.Show in the TextChanged event, as they you know that the text has been inserted when the AutoCompleteAccepted Event was called.
It would now look something like this:
String calltipText = null; //start out with null calltip
...
private void Editor_TextChanged(object sender, EventArgs e)
{
if (calltipText != null)
{
CallTip.Show(calltipText); //note, you may want to assign a position
calltipText = null; //reset string
}
...
}
...
private void Editor_AutoCompleteAccepted(object sender, AutoCompleteAcceptedEventArgs e)
{
if (e.Text == "someThing")
{
/* Code to add text to control */
...
calltipText = "someKindOFText"; //assign value to calltipText
}
}
That is essentially what can be done to ensure the AutoComplete fills correctly and you get the CallTip to show.
Just note, that the CallTip MAY end up in unintended places, so it is recommended to set the value of where you want the CallTip to show up
Currently my code looks something like this:
private void appbariconNext_Click(object sender, EventArgs e)
{
timerPivot.SelectedIndex += 1;
}
private void appbariconPrevious_Click(object sender, EventArgs e)
{
timerPivot.SelectedIndex -= 1;
}
Problem is, if I press the Previous button from the first PivotItem, I will get an ArgumentException. Same thing if I press the Next button from the last PivotItem.
I currently have a changing number of PivotItem depending on the option chose by users, so it's inefficient for me to get the number of PivotItems from all those different scenarios.
Is there a way to get the total number of PivotItems that a Pivot has? Or is there a way to solve this issue?
Thanks!
First of all, it is bad UI to create buttons to navigate from one page to another using buttons. What about swiping? Have you considered disabling it and restyling Pivot to make it appear as wizard (if that is what you are trying it to be)?
You can get the total number of Pivot items via the Items property. Simply do modular division with that number to keep the index number in range [0, Items.Length>.
I really hope that you are not abusing Pivot in any way.
I have a user control wherein I would like to do something different in the case of a single click vs. double click. I'm handling mouseLeftButtonDown event as such:
private void cueCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
switch (e.ClickCount)
{
case 1:
{
cueCanvas.Focus();
e.Handled = true;
_mouseStartPoint = Mouse.GetPosition(null);
break;
}
case 2:
{
double pos = Mouse.GetPosition(cueCanvas).X;
TimeSpan time = ConvertFromPosition(pos);
AddNewEvent(time);
e.Handled = true;
break;
}
}
}
The problem is that WPF doesn't really care how many times you've clicked the mouse so I get the first click event even though I'm about to get a second one (in the case of a double-click). Has anyone come up with a way to work around this? I realize I could try to get clever and set some timers such that the first click gets "canceled" in the event that the second one comes in (I realize this is what the framework would be doing anyway). If that's the only answer, does anyone know how we query the system for the appropriate click delay?
To my knowledge, there's really no way to do this. The problem is that you're fighting reality. :-) In reality, there are 2 clicks to a double-click. You want single click to be ignored if quickly followed by a double-click.
You'll have wait a short interval to see if it's followed by a second click. You can query for that interval using the SystemInformation class from WinForms, or just call the Win32 API directly.
Using Windows Mobile 6.5 and C#
The CharacterCasing property seems to be missing from WinMo 6.5 and I decide to just catch the textchanged event and set the text with ToUpper.
This works - but on every keypress, it sends the cursor back to the beginning of the string, not the end.
I know this is old, but hopefully this can help somebody else. I implemented the KeyPress event like the following.
private void TextBox_KeyPress(object sender, KeyPressEventArgs e)
{
e.KeyChar = Char.ToUpper(e.KeyChar);
}
Ritu, just to comment on your answer. You should keep in mind that this might be confusing for a user if the user has positioned the caret in the middle of the string to perform some edit and then the caret jumps to the end of the string on the key press.
An alternative might be to change the text to upper case when the edit control loses focus.
Try here such implementation.
public MainForm()
{
InitializeComponent();
InitializeUpperCaseTextBox();
}
private void InitializeUpperCaseTextBox()
{
txtbox.CharacterCasing = CharacterCasing.Upper;
//... etc.
}
The soluttion of setting the text position to the end of the string seems like would be a hassle if you ever need to edit text that you have already entered.
It's been a while since I thought about the C# event model but, one alternative might be to catch the KeyPress event and change any lowercase KeyChar values to uppercase before passing them on to the next handler.
The way you are approaching seems to be wrong.
There are so many different ways to insert data into that textbox. What about copy & paste for instance?
Just perform a .Text.ToUpper() when accessing the value of the Textbox
Save the SelectionStart and SelectionLength before changing the text. The ToUpper should make no changes to the length, so you can simply set the SelctionStart and SelectionLength back to what they had been.
Also, I would expect to get a changed event again when you set it ToUpper. I'm not sure if you also need to check that the ToUpper actually changed anything before you set Text again. It may be smart enough to check that for you when you assign the text and avoiding giving you an infinite recursive loop of change events. However, you probably don't want to alter the selection in the event handler call for the case where you aren't making a further change, only in the outer call where you are assigning back to Text. So you might as well guard recursion directly.
Something like:
bool m_InMyTextChanged = false;
private void txtMyText_TextChanged(object sender, EventArgs e)
{
if (m_InMyTextChanged)
return; // Recursive! We can bail quickly.
m_InMyTextChanged = true; // Prevent recursion when we change it.
int selectionStart = txtMyText.SelectionStart;
int selectionLength = txtMyText.SelectionLength;
string originalText = txtMyText.Text;
string newText = originalText.ToUpper();
if (newText != originalText)
{
txtMyText.Text = newText; // Will cause a new TextChanged event.
// Set the selection back *after* the assignment, which has reset them.
txtMyText.SelectionStart = selectionStart;
txtMyText.SelectionLength = selectionLength;
}
m_InMyTextChanged = false; // Allow it for next time.
}
could work. I haven't worked in Windows Mobile, but I would think this would work the same as in general for .NET.
I figured it out. So on the textChanged event, I replace the entered text with the ToUpper version. Then I set the SelectionStart property to the Text.Length to move the cursor to the end.