I'm programming a winforms app, and I have encountered a problem:
I have, for example, a numeric UpDown control, and when pressing the up/down button, I don't want it to change, but I want access to the new value, without changing the number on the control itself.
I need as well to be able to unlock it under some condition, so it would look like that:
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
if (!canChange)
{
int newValue = get_expected_new_value();
doSomeStuff(newValue);
//some_code_to_cancel_the_value_change;
}
else
{
//allow the change
doSomeOtherStuff();
}
}
How can I do thins thing?
You can use the Tag property of the numericUpDown1 to store the last value.
Although it's not a particulary elegant solution.
Credit to: C# NumericUpDown.OnValueChanged, how it was changed?
In your case it can look something like this:
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
var o = (NumericUpDown) sender;
int thisValue = (int) o.Value;
int lastValue = (o.Tag == null) ? 0 : (int)o.Tag;
o.Tag = thisValue;
if (checkBox1.Checked) //some custom logic probably
{
//remove this event handler so it's not fired when you change the value in the code.
o.ValueChanged -= numericUpDown1_ValueChanged;
o.Value = lastValue;
o.Tag = lastValue;
//now add it back
o.ValueChanged += numericUpDown1_ValueChanged;
}
//otherwise allow as normal
}
Basicaly you store the last known good value in the Tag property.
Then you check you condition and set the value back to the last good value.
Related
I have a repositoryItemCheckEdit in a column of my grid. The task I want to do is :
Once the user pressed the CheckEdit , this cell become disable so that the user can not make click again.
To do this task I'm using the CheckedChanged event, in the following way :
private void repositoryItemCheckEdit1_CheckedChanged(object sender, EventArgs e)
{
var obj = sender as CheckEdit;
if (obj.Checked)
{
repositoryItemCheckEdit1.Enabled = false;
}
}
With the above event the only thing I get is that the cell becomes clearer , but not is disabled. Even if I make click again it allows me to do it.
Any help is appreciated.
You will probably have more luck/an easier time dealing with this using the brute force method... at least i find this a lot easier than dealing with the crazyness of DataGridView controls scheme.
Use the Tag attribute of your control to set a flag on it, and then when someone tries to un-check it/change it, force it back to checked. Like so:
private void repositoryItemCheckEdit1_CheckedChanged(object sender, EventArgs e)
{
var obj = sender as CheckEdit;
if(obj.Tag != null)
{
obj.Checked = true;
repositoryItemCheckEdit1.Enabled = false;
}
else
{
if (obj.Checked)
{
obj.Tag = true;
repositoryItemCheckEdit1.Enabled = false;
}
}
}
I have a LookupEdit in the grid that needs to be able to accept new values. When I enter the new value and press "Enter" or "Tab", it saves normally via the ProcessNewValue event, however when I enter a value and click elsewhere in the grid, on another cell or just in white space, the value vanishes completely. By implementing several other events and setting breakpoints all I figured out was that the value disappears before the "CloseUp" event fires. Validating, EditValueChanged, EditValueChanging, ProcessNewValue, Closed, Leave, and even GetNotInListValue never even get called because of the empty value.
Can anyone think of some setting I haven't found yet, or any other reason why this value would disappear...And how I might stop it from happening?
Found a valid Workaround
I implemented the following 3 events, in sequence, to solve this issue. I still have no idea what caused it, or how to go about preventing it. This is a Workaround, not a solution, and should be treated as such. I end up having to manually call the ProcessNewValue method, as well as forcing the value to equal the text field, and the text field back into the value later on. Not the smoothest of operations, but it does work.
private void repPatchNum1_EditValueChanging(object sender, DevExpress.XtraEditors.Controls.ChangingEventArgs e)
{
string surfaceSoftware = vwSurfaceSoftware.GetRowCellDisplayText(vwSurfaceSoftware.FocusedRowHandle, "SurfaceSoftware");
if (string.Compare(surfaceSoftware, SOFTWARE_CHECK, true) == 0)
{
string version = vwSurfaceSoftware.GetRowCellDisplayText(vwSurfaceSoftware.FocusedRowHandle, "Version");
if (version.ToLower().Contains(VERSION_CHECK))
{//now we are certain we are in the right place
LookUpEdit editor = sender as LookUpEdit;
if (!((RPickListCollection)((BindingSource)editor.Properties.DataSource).DataSource).OfType<RPickList>().Any(a => a.RValue.Equals(e.NewValue)))
{
repPatchNum1_ProcessNewValue(sender, new DevExpress.XtraEditors.Controls.ProcessNewValueEventArgs(e.NewValue));
vwSurfaceSoftware.SetRowCellValue(vwSurfaceSoftware.FocusedRowHandle, colPatchNum, e.NewValue);
}
}
}
}
private void repPatchNum1_CloseUp(object sender, DevExpress.XtraEditors.Controls.CloseUpEventArgs e)
{
string surfaceSoftware = vwSurfaceSoftware.GetRowCellDisplayText(vwSurfaceSoftware.FocusedRowHandle, "SurfaceSoftware");
if (string.Compare(surfaceSoftware, SOFTWARE_CHECK, true) == 0)
{
string version = vwSurfaceSoftware.GetRowCellDisplayText(vwSurfaceSoftware.FocusedRowHandle, "Version");
if (version.ToLower().Contains(VERSION_CHECK))
{//now we are certain we are in the right place
LookUpEdit editor = sender as LookUpEdit;
if (!((RPickListCollection)((BindingSource)editor.Properties.DataSource).DataSource).OfType<RPickList>().Any(a => a.RValue.Equals(e.Value)))
{
e.Value = ((LookUpEdit)sender).Text;
}
}
}
}
private void repPatchNum1_Closed(object sender, DevExpress.XtraEditors.Controls.ClosedEventArgs e)
{
string surfaceSoftware = vwSurfaceSoftware.GetRowCellDisplayText(vwSurfaceSoftware.FocusedRowHandle, "SurfaceSoftware");
if (string.Compare(surfaceSoftware, SOFTWARE_CHECK, true) == 0)
{
string version = vwSurfaceSoftware.GetRowCellDisplayText(vwSurfaceSoftware.FocusedRowHandle, "Version");
if (version.ToLower().Contains(VERSION_CHECK))
{//now we are certain we are in the right place
LookUpEdit editor = sender as LookUpEdit;
string patch = vwSurfaceSoftware.GetRowCellValue(vwSurfaceSoftware.FocusedRowHandle, colPatchNum).ToString();
if (String.IsNullOrEmpty(editor.Text) && !String.IsNullOrEmpty(patch))
{
editor.Text = vwSurfaceSoftware.GetRowCellValue(vwSurfaceSoftware.FocusedRowHandle, colPatchNum).ToString();
vwSurfaceSoftware.UpdateCurrentRow();
}
}
}
}
As to the original question: Please post an answer if you know why this might be happening or how to prevent it.
Thanks all :-)
I think I found a simpler workaround, tested on DevExpress 13.
When user presses Tab/Enter, event sequence is ProcessNewValue -> CloseUp
However if user finishes lookup by clicking somewhere else, events are reversed: CloseUp -> ProcessNewValue and entered value is lost. We can use PopupCloseMode.Immediate (specifies that the dropdown window was closed because an end-user clicked outside the editor) to detect this case, manually take entered value from editor, set it to event Value field and manually call ProcessNewValue. No need for other events.
private void myLookUp_CloseUp( object sender, CloseUpEventArgs e )
{
var lookUpEdit = sender as LookUpEdit;
if( lookUpEdit != null )
{
var enteredLookUpText = lookUpEdit.Text;
if( e.CloseMode == PopupCloseMode.Immediate )
{
e.Value = enteredLookUpText;
myLookUp_ProcessNewValue( sender, new ProcessNewValueEventArgs( enteredLookUpText ) );
}
}
// Rest of event handler
}
how to do validations for numeric,double and empty values for datagridview in c# windows application.Text values should not be allowed to enter the cells which are numeric or double.how to do it?
You Can validate datagrid view cell like this...
private void dataGridView1_CellValidating(object sender,DataGridViewCellValidatingEventArgs e)
{
// Validate the CompanyName entry by disallowing empty strings.
if (dataGridView1.Columns[e.ColumnIndex].Name == "CompanyName")
{
if (e.FormattedValue == null && String.IsNullOrEmpty(e.FormattedValue.ToString()))
{
dataGridView1.Rows[e.RowIndex].ErrorText =
"Company Name must not be empty";
e.Cancel = true;
}
}
}
void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
// Clear the row error in case the user presses ESC.
dataGridView1.Rows[e.RowIndex].ErrorText = String.Empty;
}
This validates only empty values , if you want validate numericals you can do like this...
I hope it will helps you...
If you'd like to restrict anything but numbers from being entered, you need to handle the EditingControlShowing event on the DataGridView. It can be done like this:
dataGridView.EditingControlShowing = new DataGridViewEditingControlShowingEventHandler (dataGridView_EditingControlShowing);
Then, define your handler:
void dataGridView_EditingControlShowing (object sender, DataGridViewEditingControlShowingEventArgs e)
{
TextBox tx = e.Control as TextBox;
tx.KeyPress += new KeyPressEventHandler (tx_KeyPress_int);
}
Then, define your KeyPress handler, and only handle numeric characters:
void tx_KeyPress_int (object sender, KeyPressEventArgs e)
{
if (!(char.IsNumber (e.KeyChar) || e.KeyChar == '\b'))
{
//is NOT number or is Backspace key
e.Handled = true;
}
}
Adjust to suit your exact needs accordingly (i.e. handle only input on a certain column, etc.)
If you want to validate cell values when user trying to leave cell, you should handle DataGridView.CellValidating event and process cell value there.
If you want to validate value when user typing it, you may handle KeyPress event.
To validate numeric values you may use code like this:
int number = 0;
if(!int.TryParce(cell.Value.ToString(), out number))
{
//report about error input
}
Please read this link.
http://msdn.microsoft.com/en-us/library/aa730881(v=vs.80).aspx
##Edit ,
if you try to use your own custom numeric control,
1. you no need to check any additional validation.
2. It is reusable.
I have a method
private void textBoxPilot_TextChanged(object sender, TextChangedEventArgs e)
{ ... }
where the textbox in question takes a search string from the user and populates a ListBox with the results on every keystroke.
Subsequently, when an item is picked from the ListBox, I would like the choice reflected in the same Textbox. However, I don't want to trigger the search mechanism, which would cause the Listbox to forget its selection.
How can I determine whether the TextChanged event was triggered by the user (via they keyboard or maybe copy/paste) or by another method using textBoxPilot.Text = "Pilot name";?
Thanks.
bit of a hack, but....
public class MyForm : Form
{
private bool _ignoreTextChanged;
private void listView1_SelectionChanged( object sender, EventArgs e )
{
_ingnoreTextChanged = true;
textBoxPilot.Text = listView1.SelectedValue.ToString(); // or whatever
}
private void textBoxPilot_TextChanged( object sender, TextChangedEventArgs e )
{
if( _ignoreTextChanged )
{
_ignoreTextChanged = false;
return;
}
// Do what you would normally do.
}
}
A disabled control will not fire a event. So two options are either always disable update the text then re-enable or create a derived class wrapper (using this method you could still do data binding)
class myClass : TextBox
{
public virtual string TextWithoutEvents
{
get
{
return base.Text;
}
set
{
bool oldState = Enabled;
Enabled = false;
base.Text = value;
Enabled = oldState;
}
}
}
If the user selects "Pilot name" from the list, you set the text box to "Pilot name". This will cause the list box to select "Pilot name". So the selection should be kept. You just have to break the recursion.
In my scenario where user has to type in text to trigger auto-complete and we didn't want a re-trigger when the auto-complete changes the text again, I used the text lengths. This won't work if user copy/pastes and therefore adds more than 1 character at a time with the keyboard.
private void HandleTextChanged(object sender, TextChangedEventArgs e){
var oldText = e.OldTextValue;
var newText = e.NewTextValue;
// Assuming text changed from keyboard is always 1 character longer,
// ignore this text changed event if new text > 1 character longer.
if (newText.Length > oldText.Length + 1) {
return;
}
...
}
In your scenario, if you always know the values you want to skip, then you could check for them instead:
if (newText == "Pilot name") {
return;
}
or
if (myListOfNamesToIgnore.Contains(newText)) {
return;
}
I come from the VBA world, and remember there was a BeforeUpdate call I could make on a combobox. Now I am in C# (and loving it) and I was wondering is there a BeforeUpdate call for a ComboBox on a Winform?
I can make an invisible textbox and store the info I need there and after the update, look at that box for what I need, but I was hoping there was a simplier solution.
One of the goodies of WF is that you can easily make your own. Add a new class to your project and paste the code below. Compile. Drop the new control from the top of the toolbox onto your form. Implement the BeforeUpdate event.
using System;
using System.ComponentModel;
using System.Windows.Forms;
public class MyComboBox : ComboBox {
public event CancelEventHandler BeforeUpdate;
public MyComboBox() {
this.DropDownStyle = ComboBoxStyle.DropDownList;
}
private bool mBusy;
private int mPrevIndex = -1;
protected virtual void OnBeforeUpdate(CancelEventArgs cea) {
if (BeforeUpdate != null) BeforeUpdate(this, cea);
}
protected override void OnSelectedIndexChanged(EventArgs e) {
if (mBusy) return;
mBusy = true;
try {
CancelEventArgs cea = new CancelEventArgs();
OnBeforeUpdate(cea);
if (cea.Cancel) {
// Restore previous index
this.SelectedIndex = mPrevIndex;
return;
}
mPrevIndex = this.SelectedIndex;
base.OnSelectedIndexChanged(e);
}
finally {
mBusy = false;
}
}
}
You may consider SelectionChangeCommited.
From MSDN:
SelectionChangeCommitted is raised
only when the user changes the combo
box selection. Do not use
SelectedIndexChanged or
SelectedValueChanged to capture user
changes, because those events are also
raised when the selection changes
programmatically.
This won't work when you have set your combobox to allow the user to type in the textbox though. Also, it won't tell you what the 'last' selected item was. You will have to cache this information. However, you don't need to store your information in a textbox. You can use a string.
You could try ValueMemberChanged, Validating, SelectedIndexChanged, or TextChanged. They don't fire like the BeforeUpdate, but you can look at what will be updated and handle the updated, or refuse it.
Out of the box, there's nothing like that. All of the events that deal with change in the combo box happen after the new value is already selected. At that point there's no way to tell what the value USED to be. You're best bet is what you eluded to. As soon as your ComboBox is populated save the SelectedItem to a temporary variable. Then, hook into the SelectedValueChanged event. At that point, your temporary variable will be your old value, and the SelectedItem will be your current value.
private object oldItem = new object();
private void button3_Click(object sender, EventArgs e)
{
DateTime date = DateTime.Now;
for (int i = 1; i <= 10; i++)
{
this.comboBox1.Items.Add(date.AddDays(i));
}
oldItem = this.comboBox1.SelectedItem;
}
private void comboBox1_SelectedValueChanged(object sender, EventArgs e)
{
//do what you need with the oldItem variable
if (oldItem != null)
{
MessageBox.Show(oldItem.ToString());
}
this.oldItem = this.comboBox1.SelectedItem;
}
I think what you wanted was the DropDown event. It will tell you what the value is before the user changes it. However, the user may end up not changing anything, so it's not exactly the same as BeforeUpdate.