RepositoryItemLookupEdit new value disappears before being processed - c#

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
}

Related

c# lock a windows forms control

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.

datagridview CellEndEdit fires twice

I am having a bit of an issue with the datagridview's CellEndEdit event. While I understand the concept of what the issue actually is, any attempt at circumventing it seems to fail.
Basically, I have a datagridview, in the CellEndEdit event, I make a check against the database to make sure the entry is not a duplicate. If it is, I prompt the user with a messagebox to tell them they can't enter duplicates, I then change the value back to its original state/value programmatically, and return the cell to an "Edit" state.
My understanding is that the fact that i'm changing the value programatically is why the event fires twice. To circumvent, I set a flag upon first entering the event, then prompt + set + re-edit, then set the flag to false. This does not work... can anyone tell me why or how I can make this happen?
Here's the event code:
private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
if(e.ColumnIndex == this.dataGridView1.Columns["Name"].ColumnIndex)
{
if(!this.CellBeingEdited)
{
string NewName = this.dataGridView1.Rows[e.RowIndex].Cells["Name"].Value.ToString();
//-== DATABASE CODE REMOVED ==-
bool IsDuplicate = ...;
if(IsDuplicate)
{
MessageBox.Show("Cannot have duplicate item names at this level!");
this.dataGridView1.CurrentCell = this.dataGridView1.Rows[e.RowIndex].Cells["Name"];
this.CellBeingEdited = true;
this.dataGridView1.CurrentCell.Value = this.LastEditedRowName;
this.CellBeingEdited = false;
this.dataGridView1.BeginEdit(false);
return;
}
}
}
}
This bit of code does not fire twice when I edit a value in a row :
private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
string test = "test";
if (this.dataGridView1.Rows[e.RowIndex].Cells[0].Value.ToString() == test)
{
this.dataGridView1.CurrentCell = this.dataGridView1.Rows[e.RowIndex].Cells[0];
this.dataGridView1.CurrentCell.Value = "not test";
this.dataGridView1.BeginEdit(false);
return;
}
}
Perhaps are you calling the event elsewhere?

Disable repositoryItemCheckEdit after it's checked

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

How to determine whether TextChanged was triggered by keyboard in C#?

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

Cancelling ListBox SelectedIndexChange Event

Is it possible to cancel the SelectedIndexChange event for a listbox on a winforms application? This seems like such a logical thing to have that I must be overlooking some easy feature. Basically, I have been popping up a message box asking if the user really wants to move to another item, as this will change the UI and I don't want their changes to be lost. I'd like to be able to cancel the event in case the user has not saved what they are working on. Is there a better way of doing this?
You cannot cancel it.
What I did just a couple of days ago was to have a variable with the latest selected index. Then when the event fires, you ask the user if he wants to save, this is done in the eventhandler. If the user selected "Cancel" you change the id again.
The problem is that this will make the event fire once again. So what i've used is a bool just saying "Inhibit". And at the top of the eventhandler I have:
if(Inhibit)
return;
Then below this where you ask the question you do something like this:
DialogResult result = MessageBox.Show("yadadadad", yadada cancel etc);
if(result == DialogResult.Cancel){
Inhibit = true; //Make sure that the event does not fire again
list.SelectedIndex = LastSelectedIndex; //your variable
Inhibit = false; //Enable the event again
}
LastSelectedIndex = list.SelectedIndex; // Save latest index.
This is exactly #Oskar Kjellin 's method, but with a twist. That is, one variable less and with a selected index changed event that really behaves like a selected index changed event. I often wonder why is selected index changed event getting fired even if I click on the exact same selected item. Here it doesn't. Yes it's a deviation, so be doubly sure if you want this to be there.
int _selIndex = -1;
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (listBox1.SelectedIndex == _selIndex)
return;
if (MessageBox.Show("") == DialogResult.Cancel)
{
listBox1.SelectedIndex = _selIndex;
return;
}
_selIndex = listBox1.SelectedIndex;
// and the remaining part of the code, what needs to happen when selected index changed happens
}
I just ran into this exact problem. What I did is when the user makes changes, I set ListBox.Enabled = false; This disallows them to select a different index. Once they either save or discard their changes, I set ListBox.Enabled = true; Probably not as slick as a prompt, but it gets the job done.
More elegant, use the Tag property:
if ((int)comboBox.Tag == comboBox.SelectedIndex)
{
// Do Nothing
}
else
{
if (MessageBox.Show("") == DialogResult.Cancel)
{
comboBox.SelectedIndex = (int)comboBox.Tag;
}
else
{
// Reset the Tag
comboBox.Tag = comboBox.SelectedIndex;
// Do what you have to
}
}
The SelectedIndexChanged cannot be cancelled. So you only have one real option:
private int? currentIndex;
public void ListBox_SelectedIndexChanged(sender, EventArgs args) {
if (currentIndex.HasValue && currentIndex.Value != listBox1.SelectedIndex) {
var res = MessageBox.Show("Do you want to cancel edits?", "Cancel Edits", MessageBoxButtons.YesNo);
if (res == DialogResult.Yes) {
currentIndex = (listBox1.SelectedIndex == -1 ? null : (int?) listBox1.SelectedIndex);
} else {
listBox1.SelectedIndex = currentIndex.Value;
}
}
}
This is my way to cancel SelectionChange for ComboBox. I think it could also fit to ListBox.
private bool comboBox_CancelSelection = false;
private int comboBox_LastSelectedIndex = -1;
private void comboBox_SelectedIndexChanged(object sender, EventArgs e) {
if (comboBox_CancelSelection) {
comboBox_CancelSelection = false;
return ;
}
// Handle Event
if (!comoBox_CancelSelection) {
comboBox_LastSelectedIndex = comboBox.SelectedIndex;
} else {
comboBox.SelectedIndex = comboBox_LastSelectedIndex;
}
}

Categories