Need help figuring out repeatable equals and addition operator events - c#

I am building a UWP calculator app using the MVVM pattern and I have just about everything figured out except how to get the equals button to calculate repeatedly and have my operator buttons work correctly. I can do one or the other, but I can't figure out how to do both. The code below works for a repeatable equals button, but the plus operator will yield the wrong answer when adding multiple values.
If I take out the second switch statement and change the variables back to using one Output variable then I can get the plus operator to work, but the equals button will fail when it repeats.
I am sure it is just a matter of adding a nested if statement or the addition of a bool somewhere, but I'm stumped! Please help. I've removed the non essential buttons and operators. Any other helpful suggestions on my code would be appreciated too.
Thank you!
public class ComputareViewModel : ViewModelBase
{
#region Private Fields
private double resultNumber = 0;
private double xResultNumber;
private double yResultNumber;
private string _outputValue = "0";
bool isOperationPerformed = false;
bool EqualsRepeated = false;
public ComputareViewModel()
{
}
public string OutputValue
{
get
{
return _outputValue;
}
set
{
_outputValue = value;
OnPropertyChanged("OutputValue");
}
}
public void OnNumberBtnClick(object sender, RoutedEventArgs args)
{
if ((_outputValue) == "0" || (isOperationPerformed))
_outputValue = "";
isOperationPerformed = false;
Button numberBtnClick = (Button)sender;
if ((string)numberBtnClick.Content == ".")
{
if (!_outputValue.Contains("."))
{
OutputValue = OutputValue + numberBtnClick.Content;
}
}
else
{
OutputValue = OutputValue + numberBtnClick.Content;
}
}
public void OnOperatorBtnClick(object sender, RoutedEventArgs args)
{
Button operatorBtnClick = (Button)sender;
if(isOperationPerformed == false)
{
if (xResultNumber != 0)
{
OnEqualsBtnClick(this, new RoutedEventArgs());
operationPerformed = (string)operatorBtnClick.Content;
isOperationPerformed = true;
EqualsRepeated = false;
}
else
{
operationPerformed = (string)operatorBtnClick.Content;
xResultNumber = Double.Parse(OutputValue);
isOperationPerformed = true;
}
}
else
{
//Do nothing.
}
}
public void OnEqualsBtnClick(object sender, RoutedEventArgs args)
{
if (EqualsRepeated == false)
{
if (double.TryParse(OutputValue, out yResultNumber))
switch (operationPerformed)
{
case "+":
{
OutputValue = (xResultNumber + yResultNumber).ToString();
break;
}
}
isOperationPerformed = true;
EqualsRepeated = true;
}
else
{
// If equals has already been clicked
if (EqualsRepeated == true)
if (double.TryParse(OutputValue, out xResultNumber))
switch (operationPerformed)
{
case "+":
{
OutputValue = (xResultNumber + yResultNumber).ToString();
break;
}
}
}
isOperationPerformed = true;
}
}

There are some logical problems in your code. First, after OnEqualsBtnClick is called, isOperationPerformed is true, if you want to call the OnOperatorBtnClick, it will always go to the else part and do nothing. Second, if you repeat click euqal button, xResultNumber is not 0, if you want to call OnOperatorBtnClick, it will then call the OnEqualsBtnClick, but EqualsRepeated is true now, so you should change EqualsRepeated to false. At last, xResultNumber is the value of last calculation, you should set it to 0 in OnEqualsBtnClick method.
So you can change your code like this:
public void OnOperatorBtnClick(object sender, RoutedEventArgs args)
{
Button operatorBtnClick = (Button)sender;
EqualsRepeated = false;
if (isOperationPerformed == false)
{
if (xResultNumber != 0)
{
OnEqualsBtnClick(this, new RoutedEventArgs());
operationPerformed = (string)operatorBtnClick.Content;
isOperationPerformed = true;
EqualsRepeated = false;
}
else
{
operationPerformed = (string)operatorBtnClick.Content;
xResultNumber = Double.Parse(OutputValue);
isOperationPerformed = true;
}
}
else
{
//Do nothing.
}
}
public void OnEqualsBtnClick(object sender, RoutedEventArgs args)
{
if (EqualsRepeated == false)
{
if (double.TryParse(OutputValue, out yResultNumber))
switch (operationPerformed)
{
case "+":
{
OutputValue = (xResultNumber + yResultNumber).ToString();
break;
}
}
isOperationPerformed = true;
EqualsRepeated = true;
}
else
{
// If equals has already been clicked
if (EqualsRepeated == true)
if (double.TryParse(OutputValue, out xResultNumber))
switch (operationPerformed)
{
case "+":
{
OutputValue = (xResultNumber + yResultNumber).ToString();
break;
}
}
}
isOperationPerformed = false;
xResultNumber = 0;
}

Related

Music player freeze a bit when pressing next button too fast

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

Two simultaneous Textchanged Events firing in Winforms C#

When I type in the first textbox, it should run a conversion which appears in the second, and when I type in the second, it will appear in the first. However, when I type in the first textchanged event, it triggers the second, which disrupts entering in the first and vice versa. Is there a way I can disable firing the textchanged event when it is highlighted or something?
public void dB10_TextChanged(object sender, EventArgs e)
{
TextBox dB10 = sender as TextBox;
double dBV;
int i = dB10.Text.Trim().Length;
if (i > 0)
{
dBV = Convert.ToDouble(dB10.Text);
}
else
return;
UnitConverter dBConverter = new UnitConverter();
// Controls for if various radiobuttons were clicked
if (dBVRadio.Checked == true)
{
dBV = dBConverter.dBVToVolts(dBV);
voltage.Text = dBV.ToString();
}
else if (dBuRadio.Checked == true)
{
dBV = dBConverter.dBuToVolts(dBV);
voltage.Text = dBV.ToString();
}
}
public void voltage_TextChanged(object sender, EventArgs e)
{
TextBox voltage = sender as TextBox; //V >> dB10 (dBV/dBu)
int i = voltage.Text.Trim().Length;
double volts;
if (i > 0)
{
volts = Convert.ToDouble(voltage.Text);
}
else
return;
UnitConverter dBConverter = new UnitConverter();
if (dBVRadio.Checked == true)
{
dBuRadio.Checked = false;
volts = dBConverter.voltsTodBV(volts);
dB10.Text = volts.ToString();
}
else if (dBuRadio.Checked == true)
{
volts = dBConverter.voltsTodBu(volts);
dB10.Text = volts.ToString();
}
}
you can remove the handler of another textbox and then add it
public void dB10_TextChanged(object sender, EventArgs e)
{
voltage.TextChanged-= voltage_TextChanged;
TextBox dB10 = sender as TextBox;
double dBV;
int i = dB10.Text.Trim().Length;
if (i > 0)
{
dBV = Convert.ToDouble(dB10.Text);
}
else
return;
UnitConverter dBConverter = new UnitConverter();
// Controls for if various radiobuttons were clicked
if (dBVRadio.Checked == true)
{
dBV = dBConverter.dBVToVolts(dBV);
}
else if (dBuRadio.Checked == true)
{
dBV = dBConverter.dBuToVolts(dBV);
}
voltage.Text = dBV.ToString();
voltage.TextChanged+= voltage_TextChanged;
}
You can just use a bool variable:
bool escape = false;
public void dB10_TextChanged(object sender, EventArgs e)
{
if(escape)
return;
escape = true;
// your code
escape = false;
}
public void voltage_TextChanged(object sender, EventArgs e)
{
if(escape)
return;
escape = true;
// your code
escape = false;
}

BackgroundWorker for implementing "Search as you type" Combobox

I have created a code for my combobox, that can search addresses in a very large table on Sql Server with the help of stored procedure (i'm working with Entity framework). My stored procedure returns 10 hits and my code fills the combobox with search results. For doing this I'm using BackgroundWorker.
But here I'm now having big problems:
- although the combobox is filled with my search results, it always has the first item selected. Even if I type in only a letter, the whole text gets selected;
After that searching for the address doesn't work anymore. It searches only among these 10 results and I'm having no idea how to solve this. Here is my whole code, that causes me problems:
public String searchedItem = "";
public delegate void DelegateUpdateComboboxSelection(ComboBox myCombo,string value,int count);
BackgroundWorker m_bgworker = new BackgroundWorker();
static AutoResetEvent resetWorker = new AutoResetEvent(false);
m_bgworker.WorkerSupportsCancellation = true;
m_bgworker.DoWork += new DoWorkEventHandler(FillComboboxBindingList);
m_bgworker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(m_bgworker_RunWorkerCompleted);
BindingList<spIskalnikNaslovi_Result1> m_addresses = new BindingList<SP_Result1>();
void m_bgworker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
int count = (int)((object[])e.Result)[0];
string value = (string)((object[])e.Result)[1];
ComboBox myCombo = (ComboBox)((object[])e.Result)[2];
DelegateUpdateComboboxSelection ndelegate = new DelegateUpdateComboboxSelection(UpdateComboSelection);
if (this.InvokeRequired)
{
Invoke(ndelegate, new object[] {myCombo, value, count});
return;
}
else
{
UpdateComboSelection(myCombo, value, count);
return;
}
}
private void UpdateComboSelection(ComboBox myCombo, String value, int count)
{
myCombo = comboBox9;
myCombo.DataSource = m_addresses;
searchedItem = myCombo.Text;
if (count > 0)
{
myCombo.SelectionStart = value.Length;
myCombo.SelectionLength = searchedItem.Length - value.Length;
myCombo.DroppedDown = true;
}
else
{
myCombo.DroppedDown = false;
myCombo.SelectionStart = value.Length;
}
}
public void FillComboboxBindingList(object sender, DoWorkEventArgs e)
{
if (m_bgworker.CancellationPending)
{
resetWorker.Set();
e.Cancel = true;
return;
}
else
{
string value = (String)((Object[])e.Argument)[0];
List<SP_Result1> result;
result = _vsebina.SP_searcher(value).ToList<SP_Result1>();
m_addresses = new BindingList<SP_Result1>();
foreach (SP_Result1 rez in result)
{
if (m_addresses.Contains(rez))
{
continue;
}
else
{
m_addresses.Add(rez);
}
}
foreach (SP_Result1 r in m_addresses.ToArray())
{
if (!result.Contains(r))
{
m_addresses.Remove(r);
}
}
e.Result = new object[] { rezultat.Count, vrednost, null };
return;
}
}
private void comboBox9_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Back)
{
int searchStart = comboBox9.SelectionStart;
if (searchStart > 0)
{
searchStart--;
if (searchStart == 0)
{
comboBox9.Text = "";
}
else
{
comboBox9.Text = comboBox9.Text.Substring(0, searchStart + 1);
}
}
else
{
searchStart = 0;
}
e.Handled = true;
}
}
private void comboBox9_Enter(object sender, EventArgs e)
{
comboBox9.SelectionStart = 0;
comboBox9.SelectionLength = 0;
}
private void comboBox9_Click(object sender, EventArgs e)
{
comboBox9.Text = "";
}
private void comboBox9_KeyPress(object sender, KeyPressEventArgs e)
{
Search();
}
public void Search()
{
if (comboBox9.Text.Length < 4)
{
return;
}
else
{
if (m_bgworker.IsBusy)
{
m_bgworker.CancelAsync();
m_bgworker = new BackgroundWorker();
m_bgworker.WorkerSupportsCancellation = true;
m_bgworker.DoWork += new DoWorkEventHandler(FillComboboxBindingList);
m_bgworker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(m_bgworker_RunWorkerCompleted);
}
m_bgworker.RunWorkerAsync(new object[] { comboBox9.Text, comboBox9 });
}
}
Maybe can someone enlighten me, what I'm doing wrong. This is first time, that I'm using BackgroundWorker. I have no idea, how
to achieve "search as you type" with combobox in any other way, because my datatable with addresses is quite large (million records).
Thanks in advance for any kind of help or code example.
Vladimir
Edit 1:
Ok, here is my code, before I have used BackGroundWorker. It worked, but it searches very very slow (it can take up to 10 seconds):
private void comboBox9_TextChanged(object sender, EventArgs e)
{
if (comboBox9.Text.Length < 4)
{
return;
}
else
{
FillCombobox(comboBox9.Text, comboBox9);
}
}
public void FillCombobox(string value, ComboBox myCombo)
{
List<spIskalnikNaslovi_Result1> result;
result = _vsebina.spIskalnikNaslovi1(value).ToList();
if (result.Count() > 0)
{
myCombo.DataSource = result;
myCombo.ValueMember = "HS_MID";
myCombo.DisplayMember = "NASLOV1";
var searchedItem = myCombo.Items[0].ToString();
myCombo.SelectionStart = value.Length;
myCombo.SelectionLength = searchedItem.Length - value.Length;
myCombo.DroppedDown = true;
}
else
{
myCombo.DroppedDown = false;
myCombo.SelectionStart = value.Length;
}
return;
}
Is there a way to speed this up without having backgroundworker?
make a button you will call searchbutton
and in click_event of this button call your search() method that run your backgroundworker
that fill the combobox
clear you key_press event of your combobox and it will work
the mistake is you key_press event that call every key stroke happening your search method
so retrieve it
You should get your items in a list, use that list to populate your combobox.
then set AutoCompleteMode property value to Suggest or Append or SuggestAppend and set AutoCompleteSoucre property value to ListItems.
For "Search as you Type", which is actually "Filter as you Type" more than search, you need to implement the OnKeyDown or KeyPressed event.
What you would do is take the search string, which is the current text at the time of the event, then filter the master list using that string. Normally one would use "Starts With" for the filtering, but you could also simply use "Contains". Then you live update the contents of the box with the results from the filter. This is accomplished by changing and refreshing the Datasource.
Here is my final solution without BackGroundWorker. It works quick with my large table, and is upgraded for using a stored procedure on SQL Server (if you use Entity Framework). I use Timer to make sure the user can find a value, that he is searching.
Here you can see the original solution, that I found on this site (thanks to Max Lambertini and algreat for the idea and working concept):
C# winforms combobox dynamic autocomplete
My solution:
private bool _canUpdate = true;
private bool _needUpdate = false;
List<spIskalnikNaslovi_Result1> dataFound;
private void comboBox12_TextChanged(object sender, EventArgs e)
{
if (_needUpdate)
{
if (_canUpdate)
{
_canUpdate = false;
refreshData();
}
else
{
restartTimer();
}
}
}
private void comboBox12_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Back)
{
int searchStart = comboBox12.SelectionStart;
if (searchStart > 0)
{
searchStart--;
if (searchStart == 0)
{
comboBox12.Text = "";
}
else
{
comboBox12.Text = comboBox12.Text.Substring(0, searchStart + 1);
}
}
else
{
searchStart = 0;
}
e.Handled = true;
}
}
private void comboBox12_TextUpdate(object sender, EventArgs e)
{
_needUpdate = true;
}
private void timer1_Tick(object sender, EventArgs e)
{
_canUpdate = true;
timer1.Stop();
refreshData();
}
private void refreshData()
{
if (comboBox12.Text.Length > 1)
{
FillCombobox(comboBox12.Text, comboBox12);
}
}
private void restartTimer()
{
timer1.Stop();
_canUpdate = false;
timer1.Start();
}
private void FillCombobox(string value, ComboBox myCombo)
{
dataFound = _vsebina.spIskalnikNaslovi1(value).ToList();
if (dataFound.Count() > 0)
{
myCombo.DataSource = dataFound;
myCombo.ValueMember = "HS_MID";
myCombo.DisplayMember = "NASLOV1";
var searchedItem = myCombo.Items[0].ToString();
myCombo.SelectionStart = value.Length;
myCombo.SelectionLength = searchedItem.Length - value.Length;
myCombo.DroppedDown = true;
return;
}
else
{
myCombo.DroppedDown = false;
myCombo.SelectionStart = value.Length;
return;
}
}

Problem when summing selected cells in DataGridview

I using this code for sum selected cells. Its work good but when user selecte cell where is letter is throws exceptions : ) how can i secure when in selectet cells is letters dont make sum
private void dataGridView1_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
String filterStatus = DataGridViewAutoFilterColumnHeaderCell.GetFilterStatus(dataGridView1);
if (String.IsNullOrEmpty(filterStatus))
{
showAllLabel.Visible = false;
filterStatusLabel.Visible = false;
}
else
{
int result = -1;
Int32.TryParse(filterStatus, out result);
if (result != 0)
{
// it is a number
showAllLabel.Visible = true;
filterStatusLabel.Visible = true;
filterStatusLabel.Text = filterStatus;
}
else
{
// it can be a number yet won't help you with adding
}
}
}
this is my code
private void dataGridView1_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
String filterStatus = DataGridViewAutoFilterColumnHeaderCell.GetFilterStatus(dataGridView1);
if (String.IsNullOrEmpty(filterStatus))
{
showAllLabel.Visible = false;
filterStatusLabel.Visible = false;
}
else
{
showAllLabel.Visible = true;
filterStatusLabel.Visible = true;
filterStatusLabel.Text = filterStatus;
}
}

Best way to limit textbox decimal input in c#

How can I make a textbox in which can be only typed a number like 12.00 or 1231231.00 or 123123
I've done this in a very long way and I'm looking for the best and fastest way.
Also the decimal separator must be culture specific.:
Application.CurrentCulture.NumberFormat.NumberDecimalSeparator
The Validating event was made to do that. Drop an ErrorProvider control on your form so you can subtly remind the user that she did something wrong. The event also allows you to format the text box text the way that make sense. Like this:
private void textBox1_Validating(object sender, CancelEventArgs e) {
// Empty strings okay? Up to you.
if (textBox1.Text.Length > 0) {
decimal value;
if (decimal.TryParse(textBox1.Text, out value)) {
textBox1.Text = value.ToString("N2");
errorProvider1.SetError(textBox1, "");
}
else {
e.Cancel = true;
textBox1.SelectAll();
errorProvider1.SetError(textBox1, "Please enter a number");
}
}
}
That's a fairly straightforward operation. You'll need to filter the keys that you don't want out and then perform some additional checks.
Add the following code to the KeyDown event of the textbox:
private void TextBox1_KeyDown(object sender,
System.Windows.Forms.KeyEventArgs e)
{
switch (e.KeyCode) {
case Keys.D0:
case Keys.D1:
case Keys.D2:
case Keys.D3:
case Keys.D4:
case Keys.D5:
case Keys.D6:
case Keys.D7:
case Keys.D8:
case Keys.D9:
case Keys.NumPad0:
case Keys.NumPad1:
case Keys.NumPad2:
case Keys.NumPad3:
case Keys.NumPad4:
case Keys.NumPad5:
case Keys.NumPad6:
case Keys.NumPad7:
case Keys.NumPad8:
case Keys.NumPad9:
//allow numbers only when no modifiers are active
if (e.Control || e.Alt || e.Shift) {
//suppress numbers with modifiers
e.SuppressKeyPress = true;
e.Handled = true;
Interaction.Beep();
}
break;
case (Keys)110:
case Keys.OemPeriod:
if (!((TextBox)sender).Text.Contains(".")) {
//allow period key if there is no '.'
//in the text and no modifiers are active
if (e.Control || e.Alt || e.Shift) {
//suppress numbers with modifiers
e.SuppressKeyPress = true;
e.Handled = true;
Interaction.Beep();
}
} else {
e.SuppressKeyPress = true;
e.Handled = true;
Interaction.Beep();
}
break;
case Keys.Subtract:
case Keys.OemMinus:
if (((TextBox)sender).SelectionStart == 0 &&
!((TextBox)sender).Text.Contains("-")) {
//allow the negative key only when the cursor
//is at the start of the textbox
//and there are no minuses in the textbox
//and no modifiers are active
if (e.Control || e.Alt || e.Shift) {
//suppress numbers with modifiers
e.SuppressKeyPress = true;
e.Handled = true;
Interaction.Beep();
}
} else {
e.SuppressKeyPress = true;
e.Handled = true;
Interaction.Beep();
}
break;
case Keys.C:
case Keys.X:
case Keys.V:
case Keys.Z:
//allow copy, cut, paste & undo by checking for
//the CTRL state.
if (e.Control == false) {
e.SuppressKeyPress = true;
e.Handled = true;
Interaction.Beep();
}
break;
case Keys.Control:
case Keys.ControlKey:
case Keys.Alt:
case Keys.Shift:
case Keys.ShiftKey:
//allow control, alt & shift
break;
case Keys.Left:
case Keys.Right:
case Keys.Up:
case Keys.Down:
case Keys.PageUp:
case Keys.PageDown:
case Keys.Home:
case Keys.End:
//allow navigation keys
break;
case Keys.Back:
case Keys.Delete:
//allow backspace & delete
break;
default:
//suppress any other key
e.SuppressKeyPress = true;
e.Handled = true;
Interaction.Beep();
break;
}
}
And then, since a user may paste values into the textbox, you add the following to the TextBox's Validate event
private void TextBox1_Validating(object sender,
System.ComponentModel.CancelEventArgs e)
{
//just in case a value was pasted,
//we'll need to validate the value
if (!Information.IsNumeric(((TextBox)sender).Text))
{
e.Cancel = true;
}
}
I wrote a class to handle a variety of filters for you [which obviously includes the culture-specific decimal symbol].
Add this class to your project
using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
public class TextBoxFilter
{
[Flags()]
public enum Filters
{
None = 0,
Text = 1,
Numbers = 2,
AlphaNumeric = Filters.Text | Filters.Numbers,
Currency = 4,
All = Filters.Text | Filters.Numbers | Filters.Currency
}
Dictionary<TextBox, Filters> _keyFilter;
Dictionary<TextBox, string> _allowedKeys;
Dictionary<TextBox, string> _invalidKeys;
Dictionary<TextBox, Windows.Forms.KeyEventArgs> keyEventArgs;
private static string DecimalMark = Application.CurrentCulture.NumberFormat.NumberDecimalSeparator;
private static string NegativeMark = Application.CurrentCulture.NumberFormat.NegativeSign;
private static string CurrencySymb = Application.CurrentCulture.NumberFormat.CurrencySymbol;
private static string CurrencyDecimal = Application.CurrentCulture.NumberFormat.CurrencyDecimalSeparator;
public TextBoxFilter()
{
_keyFilter = new Dictionary<TextBox, Filters>();
_allowedKeys = new Dictionary<TextBox, string>();
_invalidKeys = new Dictionary<TextBox, string>();
keyEventArgs = new Dictionary<TextBox, KeyEventArgs>();
}
//set & remove filter
public void SetTextBoxFilter(TextBox textBox, Filters filter)
{
SetTextBoxFilter(textBox, filter, AllowedKeys(textBox), InvalidKeys(textBox));
}
public void SetTextBoxFilter(TextBox textBox, string allowedKeys)
{
SetTextBoxFilter(textBox, Strings.Filter(textBox), allowedKeys, InvalidKeys(textBox));
}
public void SetTextBoxFilter(TextBox textBox, string allowedKeys, string invalidKeys)
{
SetTextBoxFilter(textBox, Strings.Filter(textBox), allowedKeys, invalidKeys);
}
public void SetTextBoxFilter(TextBox textBox, Filters filter, string allowedKeys, string invalidKeys)
{
if (!_keyFilter.ContainsKey(textBox)) {
//add the textbox and its filter if it does not exist in
//the collection of registered textboxes
_keyFilter.Add(textBox, filter);
_allowedKeys.Add(textBox, allowedKeys);
_invalidKeys.Add(textBox, invalidKeys);
keyEventArgs.Add(textBox, new System.Windows.Forms.KeyEventArgs(Keys.None));
//add the event handlers
textBox.KeyDown += KeyDownUp;
textBox.KeyUp += KeyDownUp;
textBox.KeyPress += KeyPress;
textBox.Validating += Validating;
textBox.Disposed += Disposed;
} else {
//change the filter of the textbox if it exists in
//the collection of registered textboxes
_keyFilter(textBox) = filter;
_allowedKeys(textBox) = allowedKeys;
_invalidKeys(textBox) = invalidKeys;
}
}
public void RemoveTextBoxFilter(TextBox textBox)
{
if (_keyFilter.ContainsKey(textBox)) {
_keyFilter.Remove(textBox);
_allowedKeys.Remove(textBox);
_invalidKeys.Remove(textBox);
keyEventArgs.Remove(textBox);
textBox.KeyDown -= KeyDownUp;
textBox.KeyUp -= KeyDownUp;
textBox.KeyPress -= KeyPress;
textBox.Validating -= Validating;
textBox.Disposed -= Disposed;
}
}
public bool ContainsTextBox(TextBox textBox)
{
return _keyFilter.ContainsKey(textBox);
}
//properties
public Filters Filter {
get {
if (ContainsTextBox(textBox)) {
return _keyFilter.Item[textBox];
} else {
return Filters.None;
}
}
set { SetTextBoxFilter(textBox, value); }
}
public string AllowedKeys {
get {
if (ContainsTextBox(textBox)) {
return _allowedKeys(textBox);
} else {
return "";
}
}
set { SetTextBoxFilter(textBox, this.Filter(textBox), value, this.InvalidKeys(textBox)); }
}
public string InvalidKeys {
get {
if (ContainsTextBox(textBox)) {
return _invalidKeys(textBox);
} else {
return "";
}
}
set { SetTextBoxFilter(textBox, this.Filter(textBox), this.AllowedKeys(textBox), value); }
}
//event handlers
private void Disposed(object sender, System.EventArgs e)
{
RemoveTextBoxFilter((TextBox)sender);
}
private void KeyDownUp(object sender, System.Windows.Forms.KeyEventArgs e)
{
//assign the modifiers
keyEventArgs((TextBox)sender) = e;
}
private void KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
{
//ensure key pressed is in the allowed keys
object txt = (TextBox)sender;
object c = e.KeyChar;
bool allowKey = IsValidChar(txt, c, txt.SelectionStart);
//check for backspace & Ctrl combinations if the allowKey is still false
if (allowKey == false) {
if (keyEventArgs(txt).Control) {
//control modifier goes with A, X, C, V and Z for
//Select All, Cut, Copy, Paste and Undo respectively
object key = keyEventArgs(txt).KeyCode;
allowKey = (key == Keys.A || key == Keys.X || key == Keys.C || key == Keys.V || key == Keys.Z);
} else if (keyEventArgs(txt).KeyCode == Keys.Back) {
//allow the backspace key
allowKey = true;
}
}
//disable the key if it was not valid
if (!allowKey) {
e.Handled = true;
Interaction.Beep();
}
}
private void Validating(object sender, System.ComponentModel.CancelEventArgs e)
{
object box = (TextBox)sender;
object boxFlags = _keyFilter(box);
//skip validation if the textbox allows all entries or there is no text
if (boxFlags == Filters.All | string.IsNullOrEmpty(box.Text))
return;
//otherwise check the characters entered
object txtChars = box.Text.ToCharArray;
bool isValidEntry = false;
//check each caracter for an invalid entry
for (i = 0; i <= txtChars.Length - 1; i++) {
object c = txtChars(i);
isValidEntry = IsValidChar(box, txtChars(i), i);
if (!isValidEntry) {
box.Select(i, 1);
break; // TODO: might not be correct. Was : Exit For
}
}
if (!isValidEntry)
e.Cancel = true;
if (!isValidEntry) {
Interaction.MsgBox("The text entered is invalid for the format " + boxFlags.ToString + "." + !string.IsNullOrEmpty(_allowedKeys(box)) ? Constants.vbCrLf + "Additional Allowed Keys: " + _allowedKeys(box) : "" + !string.IsNullOrEmpty(_invalidKeys(box)) ? Constants.vbCrLf + "Additional Invalid Keys: " + _invalidKeys(box) : "", MsgBoxStyle.Critical, "Invalid Entry");
}
}
private bool IsValidChar(TextBox textBox, char c, int charIndex)
{
//ensure key pressed is in the allowed keys
object pF = _keyFilter(textBox);
object aK = _allowedKeys(textBox);
object iK = _invalidKeys(textBox);
bool shouldAllow = false;
//if filter is set to all, return true unconditionally
if (pF == Filters.All)
return true;
//check preset filters
//check for text
if (EnumHasFlag(pF, Filters.Text)) {
if (!char.IsDigit(c)) {
shouldAllow = true;
} else {
//if the character is a digit, check for the number flag (AlphaNumerics)
if (EnumHasFlag(pF, Filters.Numbers)) {
shouldAllow = true;
}
}
}
//check for nubers
if (shouldAllow == false && EnumHasFlag(pF, Filters.Numbers)) {
if (char.IsDigit(c)) {
shouldAllow = true;
} else if (DecimalMark.Contains(c)) {
//allow the decimal if there is no decimal in the textbox's
//text or the selected text contains the mark
if (!textBox.Text.Substring(0, charIndex).Contains(c) || textBox.SelectedText.Contains(c)) {
shouldAllow = true;
}
} else if (NegativeMark.Contains(c) && (charIndex <= NegativeMark.IndexOf(c))) {
//allow the negative mark if we are at the start of the
//textbox
shouldAllow = true;
}
}
//check for currency
if (shouldAllow == false && EnumHasFlag(pF, Filters.Currency)) {
if (char.IsDigit(c)) {
shouldAllow = true;
} else if (CurrencyDecimal.Contains(c)) {
//allow the currency decimal mark if it does not exist in the
//textbox's text or the selected text contains the mark
if (!textBox.Text.Substring(0, charIndex).Contains(c) || textBox.SelectedText.Contains(c)) {
shouldAllow = true;
}
} else if (CurrencySymb.Contains(c) && (charIndex <= CurrencySymb.IndexOf(c))) {
//allow the currency symbol if we are in a valid position
shouldAllow = true;
}
}
//now check for extra allowed keys
if (!shouldAllow) {
shouldAllow = aK.Contains(c);
}
//and then check for extra invalid keys
if (shouldAllow && iK.Contains(c)) {
shouldAllow = false;
}
return shouldAllow;
}
[System.Diagnostics.DebuggerStepThrough()]
private bool EnumHasFlag(Enum value, Enum flag)
{
return (Convert.ToInt64(value) & Convert.ToInt64(flag)) == Convert.ToInt64(flag);
}
}
and then use it in your form as follows
public class Form1
{
TextBoxFilter filter = new TextBoxFilter();
private void Form1_Load(object sender, System.EventArgs e)
{
filter.SetTextBoxFilter(TextBox1, TextBoxFilter.Filters.Numbers);
}
public Form1()
{
Load += Form1_Load;
}
}
Try the MaskedTextBox control.
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
if (!Char.IsNumber(e.KeyChar))
{
e.Handled = !(((TextBox)sender).SelectionStart != 0 && (e.KeyChar.ToString() == Application.CurrentCulture.NumberFormat.NumberDecimalSeparator && ((TextBox)sender).Text.IndexOf(Application.CurrentCulture.NumberFormat.NumberDecimalSeparator) == -1));
}
}
And you should check onLeave for Length == 0 I think...

Categories