Suppress a wxWidgets event at specific point to prevent mutual handling - events

I have a WxWidget Panel with two TextControls for user input. One TextControl input changes the value of the other input field. I used an EVT_COMMAND_TEXT_UPDATE event and bound it to a function like "OnValueChanged" ...
mTextCtrl1->Connect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(MyClass::OnTextCtrlChanged1), NULL, this);
mTextCtrl2->Connect(wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler(MyClass::OnTextCtrlChanged2), NULL, this);
void MyClass::OnTextCtrlChanged1(wxCommandEvent &event) {
// ...
mTextCtrl2->SetValue(...); // Set a Hex value of entered Value in text ctrl 1
}
void MyClass::OnTextCtrlChanged2(wxCommandEvent &event) {
// ...
mTextCtrl1->SetValue(...); // Set a Integer value of entered Value in text ctrl 2
// at this point, MyClass::OnTextCtrl1 is handled,
// but should not, only if user himself enters something
}
Problem is, when Text in one TextControl is changed, it changes the value of the other correctly. But, as soon as text is changed in other input, it rises its own TEXT_UPDATE event and updates the current users inputs, resulting in funny curser jumping etc.
Is it possible to provide the execution of these events while changing the value of the other TextControl, so that it does not rise its TEXT_UPDATE event? If the user makes some input for himself to this text control, it should work as usual.

Maybe you can use wxTextCtrl::ChangeValue
virtual void ChangeValue(const wxString& value)
Sets the text value and marks the control as not-modified (which means that IsModified would return false immediately after the call to SetValue).
Note that this function will not generate the wxEVT_COMMAND_TEXT_UPDATED event. This is the only difference with SetValue. See this topic for more information.

Related

Validation in a Datagridview

I have an application with a datagrid view
This datagrid is filled by code, not binded to a datasource.
All cells are editable
when a user edits a value in a cell, then i need to perform an action
Private Sub dgView_CellValidated(sender As Object, e As DataGridViewCellEventArgs)
Handles dgView.CellValidated
Dim nColumnIndex As Integer
DIm nRowIndex as Integer
If sender.iscurrentRowDirty Then
nRowIndex = e.RowIndex
nColumnIndex = e.ColumnIndex
Call UpdateRowData(nRowIndex, nColumnIndex)
End If
End Sub
The Function UpdateRowData is called when user edits en then leave the cell, but if he goes to a cell in the same row. Then this routine will be called again when the user again goes to another cell without any editing
I want this function only called once
How are you going to know if the cell’s value had “actually” changed from its original value? And what is this “action” that is taken if the cell “was” changed? Is what I am getting at… is that it is unclear “what” this “action” is doing? If it is an extensive process that may take some time and you want to avoid this “action” if the cells value doesn’t “actually” change. Then you will need to know if a cells value actually DID change. And this will require a different event and some additional code implementation on your part.
On the other hand, if the “action” is simply to update something with the new value, then it may take MORE execution steps to actually “check” if the value has changed as opposed to simply overwriting the same values. So, if the “action” is trivial, I wouldn’t bother “checking” if the values have changed.
However, if you DID need to make sure that the user “actually” did change the cells value… Then below is a crude, yet simple, solution for this.
One possible issue is if the user clicked into a cell and edited it by “actually” typing some characters, but, after the user finishes typing characters they had ended up simply re-typing what the original value was… then “technically” from the grids perspective… THAT cells value DID change. In other words, We will need to somehow get the original value of the cell “BEFORE” the user starts to edit the cell. Then we could “compare” the new value with the original value when the user tries to leave the cell.
Fortunately, there is a grid event that should make this fairly trivial to implement. The event I suggest using is the grids CurrentCellDirtyStateChanged event. This event will actually fire TWICE. The event will fire once when the cell goes into “edit” mode. And it will fire again when the user ends the same cells edit mode.
Therefore, we can take advantage of this, however, we will need to know that WHEN the event fires… is it fired when the user “enters” the cells edit mode… OR … is the event fired because the user is “ending” the cells edit mode. I do not think the event keeps track of this internally so we need to create our own “mechanism” to be able to distinguish when the event is fired on the “begin” edit and the “end” edit.
One possible solution to make this work is to create two global variables… one a bool variable called FirstIn and a string variable called OriginalValue… When the event fires the first time to “begin” the cells edit mode, we can check the FirstIn variable. If it is true then we know the cell is “beginning” the edit of the cell… this is where we would capture the cells current value and set the OrignalValue string variable so we can “check” it against the final cells value when the user ends the cells edit mode.
Capturing the current cells value and setting FirstIn to false is all we need to do. We now wait until the user ends the cells edit mode and the event fires for the second time. When it fires the second time… again we check the FirstIn variable and in this case it is false which means the cells is ending its edit mode. Here we could check the OriginalValue with the cells current value and do your “action” if they are different. And finally setting FirstIn to true to start the whole process over.
Below is an example of what is described above.
bool FirstIn = true;
string OriginalValue;
private void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e) {
if (FirstIn) {
FirstIn = false;
OriginalValue = dataGridView1.CurrentCell.Value.ToString();
}
else {
string NewValue = dataGridView1.CurrentCell.Value.ToString();
FirstIn = true;
if (OriginalValue.Equals(NewValue)) {
MessageBox.Show("NO changes were made in the cell edit");
}
else {
MessageBox.Show("YES changes were made in the cell edit");
}
}
}
Sorry but I wrote this in C#, below is a VB version.
Dim FirstIn As Boolean = True
Dim OriginalValue As String
Private Sub DataGridView1_CurrentCellDirtyStateChanged(sender As Object, e As EventArgs) Handles DataGridView1.CurrentCellDirtyStateChanged
If (FirstIn) Then
FirstIn = False
OriginalValue = DataGridView1.CurrentCell.Value.ToString()
Else
Dim NewValue As String = DataGridView1.CurrentCell.Value.ToString()
FirstIn = True
If (OriginalValue.Equals(NewValue)) Then
MessageBox.Show("NO changes were made in the cell edit")
Else
MessageBox.Show("YES changes were made in the cell edit")
End If
End If
End Sub

LiveCycle drop-down list change event only works on second change

I want a Text Field to appear when a certain item is chosen from a drop-down list. I'm using a change event.
if(this.rawValue == 1){
Tolerance.presence = "visible";
}
else{
Tolerance.presence = "hidden";
}
The problem is that the Text Field presence does not change immediately when a selection is made, but only after I go back to the list box and select again (any value, not just the same one).
The new value of the dropdown only registers after the change event. This means this.rawValue points to the old value of the dropdown in a change event.
Either move your script dropdown exit event or make use of the event.newText in the if conditional in the change event.

Edit Control not updating with Spin Control MFC

I am trying to use an edit control along with a spin control using MFC visual studio .net 2003. I have carried out the basic settings for the spin control like setting the "AutoBuddy" property and "SetBuddyInteger" property to True so that the Spin control works in coordination with the edit control next to it. In my Spin control's event handler, I am facing a problem when I am trying to call my Invalidate() function. The float value in my edit control does not update and stays zero. If I remove the Invalidate(), then the value increments but my paint function is not updated obviously. A code of the following is given below:
void CMyDlg::OnSpinA(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
// TODO: Add your control notification handler code here
UpdateData();
m_A = m_ASpinCtrl.GetPos(); // m_A is my edit control float value variable
Invalidate(); // Invalidate is to be called to update my paint function to redraw the drawing
UpdateData(false);
*pResult = 0;
}
I have carried out the tab order correctly as well for the two controls.
Any suggestions on where I am going wrong?
Thanks in advance.
If you just want to have a spinning integer, you don't have to override anything.
The spin control has to be right next to the edit control in the tab order. With AutoBuddy that's all you have to do.
m_A when getting the position back would do something weird and would not return you the correct value. Try using the pointer to get your position and value and then carry out the invalidate().
{
LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
// TODO: Add your control notification handler code here
UpdateData();
CString tempStr;
m_A += pNMUpDown->iDelta;
tempStr.Format("%f",m_A);
m_ACtrl.SetWindowText(tempStr); // Like a CEdit m_ACtrl to display your string
Invalidate();
UpdateData(false);
*pResult = 0;
}
This should work perfectly well. Let me know if you still get any problems.

How to redirect a WM_KEYDOWN message to another control in MFC?

I'm on a roll today with MFC! :D
I have a text box and a list view control.
When the user presses the VK_UP and VK_DOWN keys in the text box, I would like this to happen:
Do some stuff.
Have the list view control also process the message (to highlight the previous/next item).
I want the list view to wrap around the current selection, if the key press is the first in its sequence.
Do some more stuff.
I tried subclassing my edit box in my dialog:
class MyEditBox : public CWnd
{
bool allowWrap;
afx_msg void OnKeyUp(UINT, UINT, UINT) { this->allowWrap = true; }
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
CListCtrl &listView = static_cast<CListView *>(
this->GetParent()->GetDlgItem(IDC_LIST_VIEW))->GetListCtrl();
if (nChar == VK_UP || nChar == VK_DOWN)
{
int iSelBefore = listView.GetNextItem(-1, LVNI_SELECTED);
this->GetParent()->GetDlgItem(IDC_LIST_VIEW)
->OnKeyDown(nChar, nRepCnt, nFlags); //Oops! Protected member :(
int iSelAfter = listView.GetNextItem(-1, LVNI_SELECTED);
if (iSelBefore == iSelAfter && // Did the selection reach an end?
this->allowWrap) // If so, can we wrap it around?
{
int i = a == 0 ? listView.GetItemCount() - 1 : 0;
listView.SetItemState(i, LVIS_SELECTED | LVIS_FOCUSED,
LVIS_SELECTED | LVIS_FOCUSED);
}
}
this->allowWrap = false;
}
}
but OnKeyDown() is a protected member, so I can't just call it on another control.
Is there a better way to solve this than manually sending the command with SendMessage? Should I change my design, e.g. subclass something else, etc.?
Your intention is to select previous or next item in list control, right? Then you should call the method to do that directly instaed of asking the CListCtrl to "process" your message.
You may call CListCtrl::SetSelectionMark and CListCtrl::SetItemState to select next or previous keystroke. Example:
cListCtrl.SetSelectionMark(nIndex);
cListCtrl.SetItemState(nIndex, LVIS_SELECTED | LVIS_FOCUSED, 0xFF);
You can handle Key Down, Key Up as well as Page Down, Page Up, End, Home or any any key from edit box. You need to do calculation, though.
Or you can just SendMessage. There is no need to call OnKeyDown directly. Let the framework call it for you when you send the message.
I am seeing also other ways to solve this:
Derive a class from CListCtrl called MyListCtrl and choose one of two things:
1.1 Declare MyEditBox as a friend and now you can call the protected methods on MyEditBox
1.2 Add public methods CallOnKeyDown(...) and CallOnKeyup(...) to it that only do what is needed.
And when creating the control, instance a MyListCtrl instead of a CListCtrl. Also replace the listView variable you have shown here to be a MyListCtrl and use the methods you have now available
Use PreTranslateMessage(...). I think this "hammer" solution is worse than sending a message.

Is there a way to break out of event recursion in jQuery without using a global variable?

I have a form with 2 text inputs and 2 span controls. Normally, when textbox A is changed an event is fired to change span A, and when textbox B is changed, an event is fired to change span B.
However, in one particualar case I would like a change either textbox A or textbox B to update both span A and B. I tried wiring the events up to the corresponding controls in this case, but it didn't work because there is much state that is set up in the event building code (not to mention each event calls 'this', which would make the logic use the wrong control if it were fired from a different one than it was intended).
To make things easy, it would be best to pass a string (representing the other text input id) to the event handler at the time it is created, and then calling the change() event manually on the second control. However, this puts things in an infinite loop of recursion. I thought of a way to get around the recursion, but it reqires a global variable.
Is there a better way than this, preferably one that doesn't require a global variable?
ml_inEvent = false;
$ctlToVal.bind('keyup change', {feedbackCtl: ml_feedback, controlsToMonitor: ary, validationGroup: this.validationGroup, controlToFire: ctlToFire}, function(event) {
// Other processing happens here...
if (event.data.controlToFire != '') {
var $controlToFire = $('#' + event.data.controlToFire);
if ($controlToFire.length) {
// Use a global variable to ensure this event is not fired again
// as a result of calling the other one
if (!ml_inEvent) {
ml_inEvent = true;
$controlToFire.change();
ml_inEvent = false;
}
}
}
});
You can use the extraParameters argument on .trigger() to break out, for example:
$ctlToVal.bind('keyup change', {feedbackCtl: ml_feedback, controlsToMonitor: ary, validationGroup: this.validationGroup, controlToFire: ctlToFire}, function(event, fire) {
// Other processing happens here...
if(fire !== false) $('#' + event.data.controlToFire).trigger('change', false);
});
You can give it a try here. What this does is the event handler callback not only receives the event object but also any other arguments you pass in, in this case we're just using a simple false and a !=== check this in important so undefined (the parameter not passed at all) still changes both controls. So for instance $("#control").change() would change both controls, but still not loop...you can test that here.

Resources