modify Text in TextBox before Text is displayed [duplicate] - c#

This question already has answers here:
Automatically capitalize all input in WPF
(5 answers)
Closed 5 years ago.
I want to modify the Text, that is typed in the TextBox, before it is displayed, without looking on the Text, that is already typed.
Example: xaml:
<TextBox x:Name="tb" TextChanged="tb_TextChanged">
my long text
</TextBox>
c#:
private void tb_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox tb = sender as TextBox;
if (tb != null)
{
//save caretIndex
int caretIndex = tb.CaretIndex;
//modify Text //can be any modification
//vvvvvvvvvvv Lines that I'm talking about
tb.Text.ToLower(); //or: tb.Text.ToLower(); etc.
char[] notAllowedChars = new char[] {'/t', '~', '#'}; //any other
foreach (char c in notAllowedChars)
{
tb.Text = tb.Text.Replace(c, '_'); //replace unwanted characters
}
//^^^^^^^^^^^^ these lines modify the whole text
//restore caretIndex
tb.CaretIndex = caretIndex;
}
}
In that example, the whole Text is going to be modified. But all of the other characters are already modified. So I don't want to go through them again. I want only to modify the changed text before it gets inserted.
I'm talking of 100.000+ Characters. That means, that the looking up all characters causes an unwanted performance issue.
Is there any solution or is it an impossible demand.

You can look at the TextChangedEventArgs to see the changes, and then modify the text again...here is some starting code:
private bool m_bInTextChanged;
private void tb_TextChanged(object sender, TextChangedEventArgs e)
{
if (m_bInTextChanged)
return;
m_bInTextChanged = true;
TextBox tb = sender as TextBox;
if (tb != null)
{
//save caretIndex
int caretIndex = tb.CaretIndex;
if (e.Changes.Any())
{
var addedchanges = e.Changes.Where(tc => tc.AddedLength > 0);
foreach (var added in addedchanges)
{
string stringchanged = tb.Text.Substring(added.Offset, added.AddedLength);
tb.Select(added.Offset, added.AddedLength);
tb.SelectedText = stringchanged.ToLower();
}
}
//restore caretIndex
tb.CaretIndex = caretIndex;
}
m_bInTextChanged = false;
}

Related

Guidance text when typing in a text box in c#

I'm a starter at this so not sure if this is simple or crazly hard but what i would like is after 6 characters in a text box a - to appear and then they continue typing abit like when you do a license number in windows and it autopolutes the field as you go.
Add an event KeyPress to your textbox, so every time when you change text it will add '-' if needed. Something like this:
private void textBox_KeyPress(object sender, KeyPressEventArgs e)
{
if (Char.IsControl(e.KeyChar) == false)
{
var tb = (sender as TextBox);
var text = tb.Text;
var blocks = text.Split('-');
var lastBlock = blocks.Last();
if (lastBlock.Length == 6)
{
tb.Text += "-";
tb.SelectionStart = tb.Text.Length;
}
}
}
There is a bug although with entering '-', as it is used as a separator.

Textbox string matching

I am trying to make a suggestion in a textbox based on the input. I am trying to input a number (50 for example) and make the textbox suggest what I am trying to type. I have the following data contained in vars.str variable:
AU-60-ST-F
AU-60-CA-F
AU-61-ST-F
Here is the code that I am using
private void textBox2_TextChanged(object sender, EventArgs e)
{
AutoCompleteStringCollection col2 = new AutoCompleteStringCollection();
//
for (int i = 0; i < vars.str.Count; i++)
{
if(vars.str[i].IndexOf(textBox2.Text) != -1)
{
label1.Text = "TRUE";
string match = Regex.Replace(vars.str[i], #"-[a-zA-Z][a-zA-Z]-[MF]", "");
col2.Add(match);
}
}
textBox2.AutoCompleteCustomSource = col2;
}
The suggestion should be AU-60 if I put 60 in the textbox, AU-61 if I put 61 in the textbox
The code seems to work - my testing label changes to TRUE but I do not get any suggestions below the textbox. Any ideas?

Limit number of lines entered in WPF textbox

I am trying to limit the number of lines a user can enter in a textbox.
I have been researching - the closest I can find is this:
Limit the max number of chars per line in a textbox .
And Limit the max number of chars per line in a textbox which turns out to be for winforms.
This isn't quite what I'm after... also worth mentioning that there is a misleading maxlines property which I have discovered only limits what is shown in the text box.
My requirements:
Do not require the use of a mono-spaced font
Limit the textbox to having a maximum of 5 lines
Accepts carriage return
Do not allow extra carriage returns
Stops text input when max length has been reached
Wraps text (don't particularly care if it does this in-between words or breaks up whole words)
Handles text being pasted into the control and will only paste in what will fit.
No scroll bars
Also - and this would nice - having the option of limiting the number of characters per line
These requirements are for creating a WYSIWYG textbox which will be used for capturing data that will eventually be printed, and the fonts need to be changeable - if the text gets cut off or is too big for a fixed size line - then it will come out that way in print (even if it does not look right).
I've had a stab at doing this myself by handling events - but am having a great deal of trouble getting this right. Here is my code so far.
XAML
<TextBox TextWrapping="Wrap" AcceptsReturn="True"
PreviewTextInput="UIElement_OnPreviewTextInput"
TextChanged="TextBoxBase_OnTextChanged" />
Code Behind
public int TextBoxMaxAllowedLines { get; set; }
public int TextBoxMaxAllowedCharactersPerLine { get; set; }
public MainWindow()
{
InitializeComponent();
TextBoxMaxAllowedLines = 5;
TextBoxMaxAllowedCharactersPerLine = 50;
}
private void TextBoxBase_OnTextChanged(object sender, TextChangedEventArgs e)
{
TextBox textBox = (TextBox)sender;
int textLineCount = textBox.LineCount;
if (textLineCount > TextBoxMaxAllowedLines)
{
StringBuilder text = new StringBuilder();
for (int i = 0; i < TextBoxMaxAllowedLines; i++)
text.Append(textBox.GetLineText(i));
textBox.Text = text.ToString();
}
}
private void UIElement_OnPreviewTextInput(object sender, TextCompositionEventArgs e)
{
TextBox textBox = (TextBox)sender;
int textLineCount = textBox.LineCount;
for (int i = 0; i < textLineCount; i++)
{
var line = textBox.GetLineText(i);
if (i == TextBoxMaxAllowedLines-1)
{
int selectStart = textBox.SelectionStart;
textBox.Text = textBox.Text.TrimEnd('\r', '\n');
textBox.SelectionStart = selectStart;
//Last line
if (line.Length > TextBoxMaxAllowedCharactersPerLine)
e.Handled = true;
}
else
{
if (line.Length > TextBoxMaxAllowedCharactersPerLine-1 && !line.EndsWith("\r\n"))
e.Handled = true;
}
}
}
This doesn't quite work right - I am getting strange behaviour on the last line and the selected position within the textbox keeps jumping about.
As an aside, maybe I am going down the wrong track... I was also wondering if this could be achieved by using a regular expression using something like this: https://stackoverflow.com/a/1103822/685341
I am open to any ideas as I have been struggling with this for a while. The requirements listed above are immutable - I am unable to change them.
Here is my final solution - I'd still like to hear if anyone can come up with a better way of doing this...
This just handles max number of lines - I haven't done anything with max characters yet - but it's logically a simple extension to what I have already done.
As I'm handling the textChanged event of the textbox - this also covers pasing into the control too - I haven't found a clean way to truncate text in this event (unless I handle the key_preview separately) - so I'm just not allowing invalid input by undoing.
XAML
<TextBox TextWrapping="Wrap" AcceptsReturn="True"
HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Disabled">
<i:Interaction.Behaviors>
<lineLimitingTextBoxWpfTest:LineLimitingBehavior TextBoxMaxAllowedLines="5" />
</i:Interaction.Behaviors>
</TextBox>
Code (for behaviour)
/// <summary> limits the number of lines the textbox will accept </summary>
public class LineLimitingBehavior : Behavior<TextBox>
{
/// <summary> The maximum number of lines the textbox will allow </summary>
public int? TextBoxMaxAllowedLines { get; set; }
/// <summary>
/// Called after the behavior is attached to an AssociatedObject.
/// </summary>
/// <remarks>
/// Override this to hook up functionality to the AssociatedObject.
/// </remarks>
protected override void OnAttached()
{
if (TextBoxMaxAllowedLines != null && TextBoxMaxAllowedLines > 0)
AssociatedObject.TextChanged += OnTextBoxTextChanged;
}
/// <summary>
/// Called when the behavior is being detached from its AssociatedObject, but before it has actually occurred.
/// </summary>
/// <remarks>
/// Override this to unhook functionality from the AssociatedObject.
/// </remarks>
protected override void OnDetaching()
{
AssociatedObject.TextChanged -= OnTextBoxTextChanged;
}
private void OnTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
TextBox textBox = (TextBox)sender;
int textLineCount = textBox.LineCount;
//Use Dispatcher to undo - http://stackoverflow.com/a/25453051/685341
if (textLineCount > TextBoxMaxAllowedLines.Value)
Dispatcher.BeginInvoke(DispatcherPriority.Input, (Action) (() => textBox.Undo()));
}
}
This requires System.Windows.InterActivity to be added to the project and referenced in XAML thusly:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
That is my simple soultion to set the MaxLines for TextBox and it is working fine, I hope it matches your requirements.
My_Defined_MaxTextLength is a property to set the MaxLenght
My_MaxLines is a property to set Maximum lines
My_TextBox.TextChanged += (sender, e) =>
{
if(My_TextBox.LineCount > My_MaxLines)
{
My_TextBox.MaxLength = My_TextBox.Text.Length;
}
else
{
My_TextBox.MaxLength = My_Defined_MaxTextLength;
}
};
Best Regards
Ahmed Nour
I have been looking for answers to problems similar to this for awhile and every answer that I have found involves attaching event handlers and writing a lot of code. This didn't seem right to me, and seems to link the GUI much too tightly to the Codebehind for my tastes. Also, it doesn't seem to leverage the power of WPF.
Limiting the number of lines is actually part of the more generic question of: How do you limit anything in a textbox as it is being edited?
The answer is surprisingly simple: bind your textbox to a custom DependencyProperty, then use the CoerceCallback to limit/alter/change the content of the textbox.
Make sure to set your data context properly - the simplest (but not the best) way is to add the line: DataContext="{Binding RelativeSource={RelativeSource self}}" to the top of your Window or UserControl XAML code.
XAML
<TextBox TextWrapping="Wrap"
Text="{Binding NotesText, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
AcceptsReturn="True"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Disabled">
Codebehind (C#)
const int MaxLineCount = 10;
const int MaxLineLength = 200;
public static readonly DependencyProperty NotesTextProperty =
DependencyProperty.Register(
name: "NotesText",
propertyType: typeof( String ),
ownerType: typeof( SampleTextBoxEntryWindow ),
typeMetadata: new PropertyMetadata(
defaultValue: string.Empty,
propertyChangedCallback: OnNotesTextPropertyChanged,
coerceValueCallback: CoerceTextLineLimiter ) );
public string NotesText
{
get { return (String)GetValue( NotesTextProperty ); }
set { SetValue( NotesTextProperty, value ); }
}
private static void OnNotesTextPropertyChanged(DependencyObject source,
DependencyPropertyChangedEventArgs e)
{
// Whatever you want to do when the text changes, like
// set flags to allow buttons to light up, etc.
}
private static object CoerceTextLineLimiter(DependencyObject d, object value)
{
string result = null;
if (value != null)
{
string text = ((string)value);
string[] lines = text.Split( '\n' );
if (lines.Length <= MaxLineCount)
result = text;
else
{
StringBuilder obj = new StringBuilder();
for (int index = 0; index < MaxLineCount; index++)
if (lines[index].Length > 0)
obj.AppendLine( lines[index] > MaxLineLength ? lines[index].Substring(0, MaxLineLength) : lines[index] );
result = obj.ToString();
}
}
return result;
}
(The line-limiting code is crude - but you get the idea).
The cool thing is, is this provides an easy framework to do other things as well, like limiting to numbers or alpha or special stuff - for instance, here is a simple (non-Regx) Phone number coerce method:
private static object CoercePhoneNumber(DependencyObject d, object value)
{
StringBuilder result = new StringBuilder();
if (value != null)
{
string text = ((string)value).ToUpper();
foreach (char chr in text)
if ((chr >= '0' && chr <= '9') || (chr == ' ') || (chr == '-') || (chr == '(') || (chr == ')'))
result.Append( chr );
}
return result.ToString();
}
This seems like a much cleaner and maintainable solution to me that can easily be refactored - while keeping the data and the presentation as separate as possible. The Coerce methods don't need to know anything about where the data came from or is going - it is just data.
Thanks to Jay's answer I was able to find the best solution for me.
It will undo paste and block typing.
public class LineLimitingBehavior : Behavior<TextBox>
{
public int? TextBoxMaxAllowedLines { get; set; }
protected override void OnAttached()
{
if (TextBoxMaxAllowedLines == null || !(TextBoxMaxAllowedLines > 0)) return;
AssociatedObject.PreviewTextInput += OnTextBoxPreviewTextInput;
AssociatedObject.TextChanged += OnTextBoxTextChanged;
}
private void OnTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
var textBox = (TextBox)sender;
if (textBox.LineCount > TextBoxMaxAllowedLines.Value)
Dispatcher.BeginInvoke(DispatcherPriority.Input, (Action)(() => textBox.Undo()));
}
private void OnTextBoxPreviewTextInput(object sender, TextCompositionEventArgs e)
{
var textBox = (TextBox)sender;
var currentText = textBox.Text;
textBox.Text += e.Text;
if (textBox.LineCount > TextBoxMaxAllowedLines.Value)
e.Handled = true;
textBox.Text = currentText;
textBox.CaretIndex = textBox.Text.Length;
}
protected override void OnDetaching()
{
AssociatedObject.PreviewTextInput -= OnTextBoxPreviewTextInput;
AssociatedObject.TextChanged -= OnTextBoxTextChanged;
}
}
This should limit the lines and will show the last added lines, as opposed to showing the first lines:
XAML
<TextBox TextWrapping="Wrap" AcceptsReturn="True"
PreviewTextInput="UIElement_OnPreviewTextInput"
TextChanged="TextBoxBase_OnTextChanged" />
Code
const int MaxLineCount = 10;
private void TextBoxBase_OnTextChanged(object sender, TextChangedEventArgs e)
{
TextBox textBox = (TextBox)sender;
int textLineCount = textBox.LineCount;
if (textLineCount > MaxLineCount)
{
StringBuilder text = new StringBuilder();
for (int i = 0; i < MaxLineCount; i++)
{
text.Append(textBox.GetLineText((textLineCount - MaxLineCount) + i - 1));
}
textBox.Text = text.ToString();
}
}
string prev_text = string.Empty;
private void textBox1_TextChanged(object sender, TextChangedEventArgs e)
{
int MaxLineCount = 5;
if (textBox1.LineCount > MaxLineCount)
{
int index = textBox1.CaretIndex;
textBox1.Text = prev_text;
textBox1.CaretIndex = index;
}
else
{
prev_text = textBox1.Text;
}
}

Allow only Numbers and Hyphen declaring a negative number in a textbox

I tried to get textboxes to only allow numbers and a hyphen declaring a negative number however i researched and tried to replicate what i found and it doesn't work. maybe im missing something but i have no clue about Regex so maybe its wrong. but it doesn't update and accepts letters and other characters
Regex reg = new Regex(#"^[0-9-]*$");
bool textChangedByKey;
string lastText;
private void Team1Q1_KeyPress(object sender, KeyPressEventArgs e)
{
TextBox senderTB = sender as TextBox;
if (char.IsControl(e.KeyChar)) return;
if (!reg.IsMatch(senderTB.Text.Insert(senderTB.SelectionStart, e.KeyChar.ToString()) + "1"))
{
e.Handled = true;
return;
}
textChangedByKey = true;
}
private void Team1Q1_TextChanged(object sender, EventArgs e)
{
OnTeamInfoChanged();
TextBox senderTB = sender as TextBox;
if (!textChangedByKey)
{
if (!reg.IsMatch(senderTB.Text))
{
return;
}
}
else
{
textChangedByKey = false;
lastText = senderTB.Text;
}
}
Changing the Regex pattern to ^-?[0-9]*$ would only allow a single hyphen, and only at the start of the input.
Wiring up the KeyPress event handler in the designer class like so should cause disallowed characters to get filtered out:
this.Team1Q1.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.Team1Q1_KeyPress);`

Replacing special characters in a list so you can search them easily

I am downloading an xml file and saving it to the phone (Windows Phone 8 app), then I open it as a list. This list has greek words in it with upper, lower and special characters. I want the user to be able to search thru this list without making him write the same text as is in the list, so, so far I made the search bar and the list to turn into Upper characters so if the user types "to" the list highlights "to","To","TO" erasing temporarily the words not matching bringing up the ones matching. But if the user tries to write a letter without intonation the words in the list that have intonation will all be erased until the user presses backspace and type the letter with intonation.
example: list got 2 words. Σπύρος , Μάρα.
User presses letter 'Μ', in the list, Σπύρος disappears and Μάρα becomes Μάρα. Now the user types 'α' and Mάρα disappears as well because he had to type 'ά'. If he had it would be Μάρα and so on.
So my question is, is it possible to replace a character with intonation to the same character without intonation? For example. ά to a, Ά to A, etc. On the Unicode character table they have different codes.
The code so far:
private void businessFilterString_TextChanged(object sender, TextChangedEventArgs e)
{
sI.ItemsSource = businesses.Collection.Where(b => b.name.ToUpper().Contains(searchBox.Text.ToUpper()) || b.address.ToUpper().Contains(searchBox.Text.ToUpper())).ToList();
LayoutUpdateFlag = true;
}
private void sI_LayoutUpdated(object sender, EventArgs e)
{
if (LayoutUpdateFlag)
{
SearchVisualTree(sI);
}
LayoutUpdateFlag = false;
}
private void SearchVisualTree(DependencyObject targetElement)
{
var count = VisualTreeHelper.GetChildrenCount(targetElement);
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(targetElement, i);
if (child is TextBlock)
{
textBlock1 = (TextBlock)child;
prevText = textBlock1.Text.ToUpper();
HighlightText();
}
else
{
SearchVisualTree(child);
}
}
}
private void HighlightText()
{
if (textBlock1 != null)
{
string text = textBlock1.Text.ToUpper();
textBlock1.Text = text;
textBlock1.Inlines.Clear();
int index = text.IndexOf(searchBox.Text.ToUpper());
int lenth = searchBox.Text.Length;
if (!(index < 0))
{
Run run = new Run() { Text = text.Substring(index, lenth), FontWeight = FontWeights.ExtraBold};
run.Foreground = new SolidColorBrush(Colors.Orange);
textBlock1.Inlines.Add(new Run() { Text = text.Substring(0, index), FontWeight = FontWeights.Normal});
textBlock1.Inlines.Add(run);
textBlock1.Inlines.Add(new Run() { Text = text.Substring(index + lenth), FontWeight = FontWeights.Normal });
}
else
{
//textBlock1.Text = "No Match";
}
}
}
So I added
private static string DeleteAccentAndSpecialsChar(string OriginalText)
{
string strTemp = OriginalText;
Regex rega = new Regex("[Ά]");
Regex rege = new Regex("[Έ]");
Regex regi = new Regex("[Ί]");
Regex regu = new Regex("[Ύ]");
Regex regh = new Regex("[Ή]");
Regex rego = new Regex("[Ό]");
Regex regw = new Regex("[Ώ]");
strTemp = rega.Replace(strTemp, "Α");
strTemp = rege.Replace(strTemp, "Ε");
strTemp = regi.Replace(strTemp, "Ι");
strTemp = regu.Replace(strTemp, "Υ");
strTemp = regh.Replace(strTemp, "Η");
strTemp = rego.Replace(strTemp, "Ο");
strTemp = regw.Replace(strTemp, "Ω");
return strTemp;
}
and changed
private void businessFilterString_TextChanged(object sender, TextChangedEventArgs e)
{
sI.ItemsSource = businesses.Collection.Where(b => b.name.ToUpper().Contains(searchBox.Text.ToUpper()) || b.address.ToUpper().Contains(searchBox.Text.ToUpper())).ToList();
LayoutUpdateFlag = true;
}
to
private void businessFilterString_TextChanged(object sender, TextChangedEventArgs e)
{
sI.ItemsSource = businesses.Collection.Where(b => DeleteAccentAndSpecialsChar(b.name.ToUpper()).Contains(DeleteAccentAndSpecialsChar(searchBox.Text.ToUpper()))).ToList();
LayoutUpdateFlag = true;
}
This way all text is shown on upper and the intonation is vanished. Still if the user uses intonation on his text, nothing will be displayed tho but suits me well since intonation is not required on upper text.

Categories

Resources