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.
Related
so i have an adrduino that sends me data constantly, I made it Json data. so arduino sends me [0,0,0,0] constantly and the 0 changes depending on the condition in the arduino. My Issue is, in my c# im reading this arduino data and i'm using it, however my methods keeps on firing because the conditions are met. i want it to just fire once if the value is changed and that's all. for example if i get from arduino [0,2,0,0] i want it to update and if stays 2 i don't want my method to fire unless it's back to 0 again.
This is my c# code where i read the data
void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) {
var str = serial.ReadLine();
outputStr = str;
//intvalue = int.Parse(str);
try{
var json = JSON.Parse(str);
FirstSet = int.Parse(json[0].Value); // can be either 0 or 1
SecondSet = int.Parse(json[1].Value);// can be either 0 or 2
ThirdSet = int.Parse(json[2].Value);// can be either 0 or 3
ForthSet = int.Parse(json[3].Value);// can be either 0 or 4
}catch(Exception ex){
VLog.Info("EXCEPTION!: " + ex.Message);
}
// Set 1
if (FirstSet == 1)
On1();
else if (FirstSet == 0)
Off1();
// Set 2
if (SecondSet == 2)
On2();
else if (SecondSet == 0)
Off2();
// Set 3
if (ThirdSet == 3)
On3();
else if (ThirdSet == 0)
Off3();
// Set 4
if (ForthSet == 4)
On4();
else if (ForthSet == 0)
Off4();
changed = true;
}
some basic validation?
above the class:
private string lastParsed;
inside the class start with:
if (lastParsed != null && lastParsed == str)
{
return;
}
else
{
lastParsed = str;
}
EDIT: looks like you deleted your original code, but if you want to check only one of the int values for each sensor perhaps you could try another approach
above method:
private bool[] sensorState = new bool[4];
I don't remember exactly how you called the On & Off methods but in each IF and ELSE IF you could add
if (first == 0 && sensorState[0]) // it means you should switch OFF and it's currently ON
else if (first == 1 && !sensorState[0]) // it means you should switch ON and it's currently OFF
EDIT:
after each if/else block you update the sensorState
if (first != 0)
sensorState[0] = true;
else
sensorState[0] = false;
and so on..
You probably want to look into Event Handlers. Specifically when properties change. You're already using event handlers for when data is received as a SerialDataReceivedEvent, now you're adding another layer on top of it to handle if the value changes.
The basic concept is you build a class that is responsible for reading your Arduino inputs. Use Task.Run() to monitor on another thread so it doesn't lock your main process. That class will have an event handler in it that you will call from within your monitor class.
When your app instantiates this monitoring object, it will then register an action to that event handler.
This answer lays out a modern version of a class with a PropertyChangedEventHandler. That class should be the monitor that is checking the sensor. Then if the property changes, it will fire the event handler that your main program has registered with.
If I use the example from that answer, here's what your program might look like:
class Program
{
public ISensorMonitor Sensor1 { get; }
public ISensorMonitor Sensor2 { get; }
public ISensorMonitor Sensor3 { get; }
public ISensorMonitor Sensor4 { get; }
static void Main(string[] args)
{
// In this example Sensor1Monitor would implement ISensorMonitor
Sensor1 = new Sensor1Monitor();
Sensor1.PropertyChanged += DoSomethingWithSensor1;
Sensor1.StartMonitoring();
// ... implement the other 3 sensors.
}
void DoSomethingWithSensor1(object sender, PropertyChangedEventArgs e)
{
// This ensures that only the SensorValue property will handled.
if (e.PropertyName != nameof(ISensorMonitor.SensorValue))
return;
// ... Do something with Sensor1.SensorValue
}
// ... implement the other 3 sensors.
}
This will ensure DoSomething() only is called when the value of the monitor's sensor property reading changes.
Consider changing your model first... there is no need to store numbers for an On/Off state, just make your properties bool. Then, if you want to signal changes to the state, just pack it into the property setter instead of trying to be smart in some other area:
class MyContainerIsNotPartOfTheQuestion
{
private bool m_FirstSet;
public bool FirstSet
{
get { return m_FirstSet; }
set
{
if (value != m_FirstSet)
{
m_FirstSet = value;
// handle On/Off where it belongs
if (value) On1();
else Off1();
}
}
}
// same for SecondSet etc.
void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// ... your starting code
FirstSet = int.Parse(json[0].Value) != 0;
// set others
// Done, no handling of On/Off here
}
}
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;
};
}
}
}
}
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;
}
}
}
private void txtOctet1_TextChanged(object sender, EventArgs e)
{
double numCheck1;
if (txtOctet1.Text == "")
{
}
else
{
numCheck1 = Convert.ToDouble(txtOctet1.Text);
if (numCheck1 < 0 | numCheck1 > 255)
{
btnSubnetting.Enabled = false;
lblOctet1Error.Text = "Error";
lblOctet1Error.BackColor = Color.Red;
lblOctet1Error.ForeColor = Color.White;
}
else
{
btnSubnetting.Enabled = true;
lblOctet1Error.Text = "No Error";
lblOctet1Error.BackColor = Color.White;
lblOctet1Error.ForeColor = Color.Black;
}
}
}
I have made a decimal to binary converter in C#. This users a class made by me. The user enters their "IP address" in four textboxes (one for each Octet). The above code does work but I don't want to have to repeat the above code for the other third Octet input textboxes. How would I manage this (if it is possible)
Instead of showing you the solution, let's play with VS a bit :
1. Introduce two variables that hold the textbox and label references and replace all use below :
private void txtOctet1_TextChanged(object sender, EventArgs e)
{
double numCheck1;
TextBox txtToValidate = txtOctet1; // Variable 1
Label lblError = lblOctet1Error; // Variable 2
/* Select from here in the next step */
if (txtToValidate.Text == "") // Here, txtOctet1 replaced
{
}
else
{
numCheck1 = Convert.ToDouble(txtToValidate.Text); // Here, txtOctet1 replaced
if (numCheck1 < 0 | numCheck1 > 255)
{
btnSubnetting.Enabled = false;
lblError.Text = "Error"; // Here, lblOctet1Error replaced
lblError.BackColor = Color.Red; // Here, lblOctet1Error replaced
lblError.ForeColor = Color.White; // Here, lblOctet1Error replaced
}
else
{
btnSubnetting.Enabled = true;
lblError.Text = "No Error"; // Here, lblOctet1Error replaced
lblError.BackColor = Color.White; // Here, lblOctet1Error replaced
lblError.ForeColor = Color.Black; // Here, lblOctet1Error replaced
}
}
/* Select to here in the next step */
}
No progress for the moment
2. Select the code you want to reuse
with the mouse or the keyboard, select all code between the two comments place holder I've put. Basically, you should have wrapped up the outermost if/else statement.
3.a Extract to a method
Right click the select code, choose Refactor,Extract to a method.
Choose a name for your method, ValidateOctet for example.
Validate. You should have extracted the logic in a custom method :
private void txtOctet1_TextChanged(object sender, EventArgs e)
{
double numCheck1;
TextBox txtToValidate = txtOctet1; // Variable 1
Label lblError = lblOctet1Error; // Variable 2
ValidateOctet(txtToValidate, lblError);
}
private void ValidateOctet(TextBox txtToValidate, Label lblError)
{
if (txtToValidate.Text == "") // Here, txtOctet1 replaced
{
}
else
{
numCheck1 = Convert.ToDouble(txtToValidate.Text); // Here, txtOctet1 replaced
if (numCheck1 < 0 | numCheck1 > 255)
{
btnSubnetting.Enabled = false;
lblError.Text = "Error"; // Here, lblOctet1Error replaced
lblError.BackColor = Color.Red; // Here, lblOctet1Error replaced
lblError.ForeColor = Color.White; // Here, lblOctet1Error replaced
}
else
{
btnSubnetting.Enabled = true;
lblError.Text = "No Error"; // Here, lblOctet1Error replaced
lblError.BackColor = Color.White; // Here, lblOctet1Error replaced
lblError.ForeColor = Color.Black; // Here, lblOctet1Error replaced
}
}
}
Still no visible progress
3.b (Optionnal) remove useless variable
I choose to simplify the code by removing the variable we created before. I can call the method directly with the textbox and label reference. Choosing to keep the variable or not is a matter of code styling.
private void txtOctet1_TextChanged(object sender, EventArgs e)
{
double numCheck1;
ValidateOctet(txtOctet1, lblOctet1Error);
}
4. Reuse the method for all textboxes
Simply call ValidateOctet for all couple of Textbox/Label :
private void txtOctet1_TextChanged(object sender, EventArgs e)
{
double numCheck1;
ValidateOctet(txtOctet1, lblOctet1Error);
ValidateOctet(txtOctet2, lblOctet2Error);
ValidateOctet(txtOctet3, lblOctet3Error);
ValidateOctet(txtOctet4, lblOctet4Error);
}
Progress: you have now the logic defined in one place
5. Possible optimization
Take a look at other's answer. Some will probably help you.
There is a System.Net.IPAdress class. That provides a set of methods to play with IP Addresses (notably the TryParse method).
Don't know you business requirement, but your code won't support IP V6. We are at the door of (at least) deployment of IP V6. Maybe you should use only one textbox + the IPAddress class to avoid future limitations.
If you intend to have several IP Address fields, you should think about wrapping all the UI and the logic in a reusable UserControl.
Disclaimer
Please not that my answer was not targeted to find the better solution, but help you with using Visual Studio and it's refactoring features. It's quite common to start with simple things, to prototype, or playing a bit before the actual implementation. Refactoring tools allows to simply redesign some parts of your code.
Extract the code into a helper method that is passed the control.
Call this method for each control in turn.
Write a method / function and call it in every TextChange method; you have to pass the TextField as parameter to set color / error.
Create a method that takes in a TextBox and a Label, and call that method once for each set of Textbox/Labels.
(Example to follow)
You can attach the same TextChanged event to all the textboxes. The box which generated the event will be in the sender parameter, so you can get it like this TextBox txtOctet = (TextBox)sender; in the event.
Alternatively you could use just a single textbox which is what we do. This has two advantages. You can also type IPv6 addresses there and validation can be done simply by parsing the address using standard functions. We validate the address like this:
IPAddress address = null;
IPAddress.TryParse(txtIP.Text, out address);
if(address == null)
{
// Set error...
}
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.