How to disable / cancel undo button in xamarin - c#

I am creating an application that has an entry. I am trying to restrict the entry to only allow for numeric input. I have already tried using Keyboard = "Numeric". For the iPad, however, the keyboard has more characters than just numbers. So I had to restrict what is entered. When I do this however, if the user types in a parenthesis, for example, it does stop the character from being entered. But then if the user presses undo, it crashes. I assume this is because the software keyboard is separate from the app, so it is looking for that parenthesis character, but it isn't there. Here is my code:
private void Entry_TextChanged(object sender, TextChangedEventArgs e)
{
Entry theEntry = (Entry)sender;
string entryText = theEntry.Text;
if(entryText != null && entryText.Length != 0)
{
char theMostRecentInput = entryText[entryText.Length - 1];
if(!Extension.IsNumeric(theMostRecentInput))
{
theEntry.TextChanged -= Entry_TextChanged;
theEntry.Text = e.OldTextValue;
theEntry.TextChanged += Entry_TextChanged;
}
}
}
Thanks for the help!

This issue will occur when Validation like special character, Max Limit, etc... for Input field are implemented.
By that time undo action will have extra character count than current input field text characters length in iOS ShouldChangeCharacters Delegate. This leads to app crash.
One of the solution is to return as false in such scenario instead of disabling undo buttons. Below solution worked for me.
public class ExtEntryRenderer : EntryRenderer
{
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (Control != null)
{
Control.ShouldChangeCharacters += (UITextField textField, NSRange range, string replacementString) =>
{
if (range.Location + range.Length > ((UITextField)textField).Text.Length)
return false;
return true;
};
}
}
}

I would do this in a Entry custom renderer that way you you can control the input via ShouldChangeCharacters and not have to kludge it by allowing the input and then having to remove the handler and change the text back to the old value...
Here is a quick example that allows numeric, It also automatically handles clipboard pasting non-numeric strings as those would be disallowed. I am using the NSCharacterSet.DecimalDigits character set as that would be internationalized by the OS, but you could allow/disallow any chars of your choosing.
You could also include haptic, visual or audio feedback on the disallowed/rejected entries...
[assembly: ExportRenderer(typeof(NumericEntry), typeof(NumericEntryRenderer))]
namespace Forms_PCL_Tester.iOS
{
public class NumericEntryRenderer : EntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Entry> e)
{
base.OnElementChanged(e);
if (e.NewElement != e.OldElement)
if (Control != null)
{
Control.KeyboardType = UIKeyboardType.NumbersAndPunctuation;
Control.ShouldChangeCharacters += (UITextField textField, NSRange range, string replacementString) =>
{
foreach (var aChar in replacementString)
if (!NSCharacterSet.DecimalDigits.Contains(aChar))
return false;
return true;
};
}
}
}
}

Related

How do I get my StringBuilder to append correctly?

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;
}
}
}

WPF Textbox - Append only

I need a textbox which can contain a maximum of 8 chars which can be achieved by setting
this.textBox1.MaxLength = 8;
However, sometimes my program will pre-enter a few characters (i.e. 6). When presented to the user, I want him to be able to append the missing 2, but not be able to delete the pre-entered 6 characters.
I do not want to have something like
if(!textBox1.Text.Equals(strPreEntered)) throw new Exception(); // Or show a messagebox, or ..
Ideally I would like the Textbox to not accept any backspaces in case textBox1.Length == strPreEntered.Length
What you really want is a flag indicating whether the TextBox has had pre-entered characters sent to it or not. Set it to true whenever you send the pre-entered characters to the TextBox.
Then in the TextBox's OnKeyPress event, ignore the keypress if the KeyCode is a backspace if the flag is set.
public class FormTest : Form
{
public FormTest() : base()
{
LimitedTextBox tb = new LimitedTextBox();
this.Controls.Add(tb);
tb.Text = "123456";
tb.MaxLength = 8;
tb.HasPreenteredText = true;
}
}
public class LimitedTextBox : TextBox
{
private int preenteredTextLength = -1;
private bool hasPreenteredText = false;
public bool HasPreenteredText
{
get { return hasPreenteredText; }
set
{
if (value == true)
{ preenteredTextLength = this.TextLength; }
else
{ preenteredTextLength = -1; }
hasPreenteredText = value;
}
}
protected override void OnKeyPress(KeyPressEventArgs e)
{
if (this.TextLength <= preenteredTextLength && e.KeyChar == '\b')
{ e.Handled = true; } // Causes the KeyPress to be skipped as it was already 'handled'
if (this.SelectionStart < preenteredTextLength) // Prevent user from overwriting/deleting selected text beyond the minimum text length
{ e.Handled = true; }
base.OnKeyPress(e);
}
}
The above code's a little sloppy but you would of course want to clean it up. Maybe have a SetPreenteredText() method that handles setting the boolean value within the LimitedTextBox control and make the boolean private, you don't want to trouble the caller with that sort of thing. The code works as expected though and accomplishes what you seem to want.

I'm trying to catch the input of the swiped card but I'm stuck using C# Windows Forms

I am using a test card and this is the output after I swiped the card and it is ok
But when I'm trying to get the data of the swiped through prompting it to messagebox this will be the output
How can I fix this? I am expecting the output same as the first image, and it will also be the message of the messagebox
Here is my code:
private void CreditCardProcessor_Load(object sender, EventArgs e)
{
KeyPreview = true;
KeyPress += CreditCardProcessor_KeyPress;
}
private bool inputToLabel = true;
private void CreditCardProcessor_KeyPress(object sender, KeyPressEventArgs e)
{
if (inputToLabel)
{
label13.Text = label13.Text + e.KeyChar;
e.Handled = true;
}
else
{
e.Handled = false;
}
MessageBox.Show(label13.Text);
}
In short I want to run a function after swiping the card, and use its data to be use in my function. :)
You'll need to be more specific with your question. From the looks of things your card scanner is operating through the keyboard buffer. (Many card scanners operate this way) This means that every character of the strip is received as a character which is why you can capture this OnKeyPress.
If you're wondering why you're only seeing one character at a time it is exactly because you're raising a message box with each character received. If you want to know when you can call a function with the whole card info using that code what you'll need is something like:
private bool inputToLabel = true;
private StringBuilder cardData = new StringBuilder();
private void CreditCardProcessor_KeyPress(object sender, KeyPressEventArgs e)
{
if (!inputToLabel)
return;
if (e.KeyChar == '\r')
{
MessageBox.Show(cardData.ToString()); // Call your method here.
}
else
{
cardData.Append(e.KeyChar);
//label13.Text = label13.Text + e.KeyChar;
}
e.Handled = true;
}
Caveat: This is assuming that the card reader library is configured to terminate a card read with carriage return. (\r) You'll need to read up, or experiment with it for settings as to whether it can/does send a terminating character to know when the card read is complete. Failing that you can watch the output string for patterns. (I.e. when the captured string ends with "??") Though this is less optimal.

Winforms MaskedTextBox - Reformatting pasted text to match mask

I have a MaskedTextBox control that, in our case, is collecting social insurance (tax) numbers (without a ValidatingType though since the string representation including the mask literals). A social insurance number is 3 groups of 3 digits separated by dashes. Sometimes spaces may be typed or entered instead of the dashes.
The configuration of the textbox is:
Mask: 999-999-999
ValidationType: null / not required
SkipLiterals: true
CutCopyMaskFormat: IncludeLiterals (only relevant when cut/copy FROM textbox)
TextMaskFormat: IncludeLiterals
-- Let me know if there other properties that you think could be important!
Problem
When pasting the following tax number "450 622 097" because of the spaces it doesn't match the mask. So I end up with "450- 62-2 9" in the text box. Pasting "450-622-097" will successfully paste into the box.
I want to be able to intercept the paste event in order to possibly fix it up to replace the spaces with dashes.
Alternatively, could we make the mask accept dashes OR spaces (but always output dashes)?
Non-solutions
MaskInputRejected event - I can't seem to get a handle on what was originally input (i.e. what's being rejected) so as to compare it with what's sitting at the top of the Clipboard. It merely returns how it was rejected
Validating event - Already occurs after the mask has been applied. I.e. the value of "450- 62-2 9" is in the textbox now.
Use custom ValidatingType with static Parse function - Again, occurs after the mask has been applied.
Detecting Key-Down event - Then if key series is Ctrl-V then manually handle and pass in a cleaned up version of the clipboard text. Could work, but then what about paste via the right click context menu?
Any other ideas?
While this is a hammer solution, there are limitations to the mask string and i don't see another way around it. What you need is to capture the paste event and process the text before it gets in the textbox. See below a simplistic example
class MyMaskedTextbox : MaskedTextBox
{
const int WM_PASTE = 0x0302;
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_PASTE:
if (Clipboard.ContainsText())
{
string text = Clipboard.GetText();
text = text.Replace(' ', '-');
//put your processing here
Clipboard.SetText(text);
}
break;
}
base.WndProc(ref m);
}
}
As per #anchandra's response and subsequent comments here is the class to enable processing of the text on a per-control basis.
public class MyMaskedTextBox : MaskedTextBox
{
private const int WM_PASTE = 0x0302;
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_PASTE:
if (Clipboard.ContainsText())
{
string text = Clipboard.GetText();
var args = OnPasting(text);
if (args.Cancel)
{
// Swallow it up!
return;
}
// If value changed, then change what we'll paste from the top of the clipboard
if (!args.Value.Equals(text, StringComparison.CurrentCulture))
{
Clipboard.SetText(args.Value);
}
}
break;
}
base.WndProc(ref m);
}
public event EventHandler<PastingEventArgs> Pasting;
protected virtual PastingEventArgs OnPasting(string value)
{
var handler = Pasting;
var args = new PastingEventArgs(value);
if (handler != null)
{
handler(this, args);
}
return args;
}
}
public class PastingEventArgs : CancelEventArgs
{
public string Value { get; set; }
public PastingEventArgs(string value)
{
Value = value;
}
}
And simple usage of the Pasting event to strip out spaces as per:
private void sinTextBox_Pasting(object sender, PastingEventArgs e)
{
e.Value = e.Value.Replace(" ", String.Empty);
}

Looking for a simple C# numeric edit control

I am a MFC programmer who is new to C# and am looking for a simple control that will allow number entry and range validation.
Look at the "NumericUpDown" control. It has range validation, the input will always be numeric, and it has those nifty increment/decrement buttons.
I had to implement a Control which only accepted numbers, integers or reals.
I build the control as a specialization of (read: derived from) TextBox control, and using input control and a regular expresión for the validation.
Adding range validation is terribly easy.
This is the code for building the regex. _numericSeparation is a string with characters accepted as decimal comma values
(for example, a '.' or a ',': $10.50 10,50€
private string ComputeRegexPattern()
{
StringBuilder builder = new StringBuilder();
if (this._forcePositives)
{
builder.Append("([+]|[-])?");
}
builder.Append(#"[\d]*((");
if (!this._useIntegers)
{
for (int i = 0; i < this._numericSeparator.Length; i++)
{
builder.Append("[").Append(this._numericSeparator[i]).Append("]");
if ((this._numericSeparator.Length > 0) && (i != (this._numericSeparator.Length - 1)))
{
builder.Append("|");
}
}
}
builder.Append(#")[\d]*)?");
return builder.ToString();
}
The regular expression matches any number (i.e. any string with numeric characters) with only one character as a numeric separation, and a '+' or a '-' optional character at the beginning of the string.
Once you create the regex (when instanciating the Control), you check if the value is correct overriding the OnValidating method.
CheckValidNumber() just applies the Regex to the introduced text. If the regex match fails, activates an error provider with an specified error (set with ValidationError public property) and raises a ValidationError event.
Here you could do the verification to know if the number is in the requiered range.
private bool CheckValidNumber()
{
if (Regex.Match(this.Text, this.RegexPattern).Value != this.Text)
{
this._errorProvider.SetError(this, this.ValidationError);
return false;
}
this._errorProvider.Clear();
return true;
}
protected override void OnValidating(CancelEventArgs e)
{
bool flag = this.CheckValidNumber();
if (!flag)
{
e.Cancel = true;
this.Text = "0";
}
base.OnValidating(e);
if (!flag)
{
this.ValidationFail(this, EventArgs.Empty);
}
}
As I said, i also prevent the user from input data in the text box other than numeric characteres overriding the OnKeyPress methdod:
protected override void OnKeyPress(KeyPressEventArgs e)
{
if ((!char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar)) && (!this._numberSymbols.Contains(e.KeyChar.ToString()) && !this._numericSeparator.Contains(e.KeyChar.ToString())))
{
e.Handled = true;
}
if (this._numberSymbols.Contains(e.KeyChar.ToString()) && !this._forcePositives)
{
e.Handled = true;
}
if (this._numericSeparator.Contains(e.KeyChar.ToString()) && this._useIntegers)
{
e.Handled = true;
}
base.OnKeyPress(e);
}
The elegant touch: I check if the number valid every time the user releases a key, so the user can get feedback as he/she types. (But remember that you must be carefull with the ValidationFail event ;))
protected override void OnKeyUp(KeyEventArgs e)
{
this.CheckValidNumber();
base.OnKeyUp(e);
}
You can use a regular textbox and a Validator control to control input.
Try using an error provider control to validate the textbox. You can use int.TryParse() or double.TryParse() to check if it's numeric and then validate the range.
You can use a combination of the RequiredFieldValidator and CompareValidator (Set to DataTypeCheck for the operator and Type set to Integer)
That will get it with a normal textbox if you would like, otherwise the recommendation above is good.

Categories