How to prevent Checkbox Checkchanged event from firing in VB.Net - vb.net

I have this code in my one function
If DataServices.IsGroupByMe(test) = True Then
chkGroup.Visible = True
chkGroup.Checked = True
Else
chkGroup.Checked = False
chkGroup.Visible = False
End If
Upon going through this line
chkGroup.Checked = True
It automatically calls my checkchanged event for that checkbox
Private Sub chkGroupByMe_CheckedChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles chkGroupOper.CheckedChanged
LoadFilteredData()
End Sub
Which then distrupts the usual sequence of action to perform. Are there ways to prevent triggering the checkchanged event?
Please note that the first function in the base form. and the checkchanged event in a different child form inheriting the base.

You can simply use a variable to block the update:
private bool PreventUpdate;
// your function
If DataServices.IsGroupByMe(test) = True Then
PreventUpdate = true;
// ... all tasks
PreventUpdate = false;
// handler
Private Sub chkGroupByMe_CheckedChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles chkGroupOper.CheckedChanged
if not PreventUpdate Then
LoadFilteredData()
End Sub

A simple method would be to remove the Event Handler before settings its value and then re-assigning it.
Before setting the checked value (or at the start of the function):
RemoveHandler chkGroup.CheckedChanged, AddressOf chkGroupByMe_CheckedChanged
And after setting the checked value (or at the end of the function):
AddHandler chkGroup.CheckedChanged, AddressOf chkGroupByMe_CheckedChanged
Note: Anytime you add an EventHandler using the method above, always make sure there is a corresponding RemoveHandler called before it. Calling RemoveHandler too many times won't be a problem, but having your events handled too many times will cause you a few headaches wondering what is going on.

This is tough to answer since we don't really know the structure of your forms, in which form each piece of code reside nor how you interact with them.
However, one option is that you create a Shared property in the base form that indicates when LoadFilterData() should be bypassed.
Put this in your base form:
Protected Shared Property BypassDataLoad As Boolean = False
Then in your function do:
If DataServices.IsGroupByMe(test) = True Then
BaseForm.BypassDataLoad = True
chkGroup.Visible = True
chkGroup.Checked = True
BaseForm.BypassDataLoad = False
Else
BaseForm.BypassDataLoad = True
chkGroup.Checked = False
chkGroup.Visible = False
BaseForm.BypassDataLoad = False
End If
...and in your event handler:
Private Sub chkGroupByMe_CheckedChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles chkGroupOper.CheckedChanged
If Not BaseForm.BypassDataLoad Then
LoadFilteredData()
End If
End Sub
Replace BaseForm with the actual name of your base form class.

you can remove the event handler using RemoveHandler before setting the Checked property to true. Then in the else case where you set the Checked property to false you can add the eventhandler back using the AddHandler. so that whenever it is checked the actual event will fire.

You can use the "Tag" property of the checkbox in order to control the "CheckChanged" event instead of removing and re-adding the handler again.
Please try the following code:
If DataServices.IsGroupByMe(test) Then
chkGroup.Tag = "0"
chkGroup.Visible = True
chkGroup.Checked = True
Else
chkGroup.Tag = "0"
chkGroup.Checked = False
chkGroup.Visible = False
End If
Private Sub chkGroupByMe_CheckedChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles chkGroupOper.CheckedChanged
If chkGroup.Tag <> "0" Then
LoadFilteredData()
End If
chkGroup.Tag = ""
End Sub

Related

How to degrade similar tool strip button procedures to a single in VB.NET

I'm using ToolStrip and it's buttons to change GUI structure with an example code how i change GUIs with tool strip buttons. Are there any methods to use them more easy way like when clicked event of ToolStripButton handled it can call a single procedure etc.? In current case it seems i'm coding in a bad way.
For example if user click the Home button it highlights the button as selected and hides other panel elements and make visible Home's panel.
Private Sub tsbHome_Click(sender As Object, e As EventArgs) Handles tsbHome.Click
tsbHome.Checked = True
tsbTools.Checked = False
tsbReport.Checked = False
tsbAnalyze.Checked = False
'... Tool Strip Button lists continues...
pnlHome.Visible = True
pnlTools.Visible = False
pnlReport.Visible = False
pnlAnalyze.Visible = False
' ... Panel lists continues...
End Sub
if user click the Tools button it highlights the button as selected and hides other panel elements and make visible Tool's panel.
Private Sub tsbTools_Click(sender As Object, e As EventArgs) Handles tsbTools.Click
tsbHome.Checked = False
tsbTools.Checked = True
tsbReport.Checked = False
tsbAnalyze.Checked = False
'... Tool Strip Button lists continues...
pnlHome.Visible = False
pnlTools.Visible = True
pnlReport.Visible = False
pnlAnalyze.Visible = False
' ... Panel lists continues...
End Sub
There are two tricks to making this code much simpler. The first is knowing you can have more than one item in the Handles clause of an event method declaration. (You can also omit that clause, and use AddHandler to set up event handlers for a lot of controls to one method.) The other trick is knowing how to use the sender argument to determine which of the several controls connected to this method was used.
Put those together, and you get one method that will work to change to any of your views.
Private Sub NavigationMenuItem_Click(sender As Object, e As EventArgs) Handles tsbHome.Click, tsbTools.Click, tsbReport.Click, tsbAnalyzer.Click
'First Suspend Layout, to avoid extra screen re-draws
Me.SuspendLayout()
'Set your checkboxes
tsbHome.Checked = sender Is tsbHome
tsbTools.Checked = sender Is tsbTools
tsbReport.Checked = sender Is tsbReport
tsbAnalyze.Checked = sender Is tsbAnalyze
'Then De-select EVERYTHING
pnlHome.Visible = sender Is tsbHome
pnlTools.Visible = sender Is tsbTools
pnlReport.Visible = sender Is tsbReport
pnlAnalyze.Visible = sender Is tsbAnalyze
' ... lists continues...
'Finally, resume layout so all changes draw to the screen at once
Me.ResumeLayout()
End Sub
We can make this simpler if you add code to the Form Load or InitializeComponent() method to put the panels and toolstrip buttons into lists:
Private ViewButtons As List(Of ToolStripButton)
Private ViewPanels As List(Of Panel)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
ViewButtons = New List(Of ToolStripButton) From {tsbHome, tsbTools, tsbReport, tsbAnalyze}
ViewPanels = New List(Of Panel) From {pnlHome, pnlTools, pnlReport, pnlAnalyze}
For Each b In ViewButtons
AddHandler b.Click, AddressOf NavigationMenuItem_Click
Next
End Sub
Private Sub NavigationMenuItem_Click(sender As Object, e As EventArgs)
Me.SuspendLayout()
For i As Integer = 0 To ViewButtons.Length - 1
Dim selected As Boolean = ViewButtons(i) Is sender
ViewButtons(i).Checked = selected
ViewPanels(i).Visible = selected
Next
Me.ResumeLayout()
End Sub

Why DateTimePicker won't trigger keyDown and KeyPress events with the tab key?

Fellows, I am having this problem - the DateTimePicker won't trigger KeyDown and KeyPress events with the tab key (other keys are working fine, and the keyUp event as well, although it triggers after "arriving" at the DateTimePicker after pressing tab at the previous control focused). I'm using .net 4.6.1, Visual Basic and VS 2017.
What I'm trying to do -> Go to month and year directly on DateTimePicker in C# (Go to month and year directly on DateTimePicker)
Code I'm using:
Private Sub DateTimePicker1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles DateTimePicker1.KeyDown
If e.KeyCode = Keys.Tab Then
e.Handled = True
MsgBox("TAB DOWN")
End If
End Sub
Private Sub DateTimePicker1_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles DateTimePicker1.KeyPress
e.Handled = True
MsgBox("tab press")
End Sub
Private Sub DateTimePicker1_KeyUp(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles DateTimePicker1.KeyUp
If e.KeyCode = Keys.Tab Then
MsgBox("TAB UP")
e.Handled = True
End If
End Sub
Any clues?
The Tab key is used for navigation. Moving the focus from one control to another. So your KeyDown event handler can never see it, the keystroke is intercepted and used before that. You could subscribe the PreviewKeyDown event and set the e.IsInputKey = true as a workaround, check the MSDN sample code in the linked article for code.
But it is the wrong event to use anyway, you'd still want this to work when the user changes focus with the mouse instead of the keyboard. So use the Enter event instead.
Do beware that both approaches have the same problem, the focus might already be on the month part from previous usage of the control so now your code will incorrectly move it to the year part. And you can't find out what part has the focus, that is up a creek without a good paddle. A very ugly workaround for that is to change the Format property, and back, that forces the control to re-create the control window and that always resets the focus. Use BeginInvoke() to run that code. Perhaps more constructively, consider to just not display the day if you are only interested in month+year, CustomFormat property.
Sample code that implements the focus hack:
Private Sub DateTimePicker1_Enter(sender As Object, e As EventArgs) Handles DateTimePicker1.Enter
Me.BeginInvoke(
New Action(Sub()
'' Hack to reset focus
DateTimePicker1.Format = DateTimePickerFormat.Long
DateTimePicker1.Format = DateTimePickerFormat.Short
DateTimePicker1.Focus()
SendKeys.Send("{Right}")
End Sub))
End Sub
It's not the right answer to this question, although it helps as well. If you want to just make the tab behave as the right key when inside a DateTimePicker, a good (sketchy) way to do is:
Private i = 2
Protected Overrides Function ProcessTabKey(ByVal forward As Boolean) As Boolean
Dim ctl As Control = Me.ActiveControl
If ctl IsNot Nothing AndAlso TypeOf ctl Is DateTimePicker And i <> 0 Then
SendKeys.Send("{Right}")
i -= 1
Return True
End If
i = 2
Return MyBase.ProcessTabKey(forward)
End Function
You need to override ProcessCmdKey function
Private isTab As Boolean = False
Private isShiftTab As Boolean = False
Protected Overrides Function ProcessCmdKey(ByRef msg As Message, ByVal keyData As Keys) As Boolean
If keyData = Keys.Tab Then
isTab = True
'Do something with it.
Else
isTab = False
End If
Return MyBase.ProcessCmdKey(msg, keyData)
End Function

IndexOutOfRangeException error when DataGridView header is clicked

Iam trying to get DataGridView rowIndex and set it to textbox and all is well with this code
Private Sub dgv_CellContentClick(sender As Object, e As DataGridViewCellEventArgs) Handles dgv.CellContentClick
isitxt(e.RowIndex)
btnInsert.Enabled = False
btnUpdate.Enabled = True
btnDelete.Enabled = True
End Sub
and
Sub isitxt(ByVal x As Integer)
txtIDBarang.Text = dgv.Rows(x).Cells(0).Value
txtNamaBarang.Text = dgv.Rows(x).Cells(1).Value
cbJenisBarang.Text = dgv.Rows(x).Cells(2).Value
numHargaBeli.Value = dgv.Rows(x).Cells(3).Value
numHargaJual.Value = dgv.Rows(x).Cells(4).Value
End Sub
But i got IndexOutOfRangeException when i clicked on Column Header. how can i handle it ?
Note, that if you use CellContentClick, the code will be executed only if the user actually aims at text content of a cell. Usually a CellClick is makes more sense.
As for your code, you can debug and see, what's in "x", when you get an error - I guess "-1"... You can handle it then. However, the reason for this should not be in your code above.
You can also set SelectionMode = FullRowSelect and do it following way:
Private Sub dgv_CellClick(sender As Object, e As DataGridViewCellEventArgs) Handles dgv.CellClick
isitxt(Me.dgv.selectedRows(0).index)
btnInsert.Enabled = False
btnUpdate.Enabled = True
btnDelete.Enabled = True
End Sub
Unless you want to handle the cells separately, users usually prefer the FullRowSelect mode.

Managing `CheckBox_Checked` Event without an infinite loop

I know this seems like an easy fix, but I am having trouble. I have a CheckBox, when checked, I remove the data source of a DataGridView on my Windows Form and remove the ReadOnly properties of a few Textbox.
I know the CheckedChanged event will send my code into an infinite loop, but I cannot figure out which event would handle this change without changing the CheckedState each time. I have tried using Click, MouseClick, and CheckStateChanged events with no luck.
This is my current code:
Private Sub chkManual_MouseClick(sender As Object, e As EventArgs) Handles chkManual.MouseClick
If Not Me.chkManual.Checked Then
Me.chkManual.Checked = False
Me.cbRegion.SelectedIndex = -1
Me.txtIssueDate.ReadOnly = True
Me.txtCasenum.ReadOnly = True
Me.txtCommnum.ReadOnly = True
Exit Sub
Else
Me.dgDataEntry.DataSource = Nothing
Me.cbRegion.SelectedIndex = -1
Me.txtIssueDate.ReadOnly = False
Me.txtCasenum.ReadOnly = False
Me.txtCommnum.ReadOnly = False
ClearForm()
frmPDF.Hide()
Exit Sub
End If
End Sub
Properties of CheckBox: AutoCheck = True, Checked = False, and CheckState = Unchecked
I have looked into these already:
CheckBox_Checked event
Is there a simpler way to process check boxes?
CheckBox reverts to checked after being unchecked
How to check if a checkboxes state has changed
How can I prevent an assignment statement to CheckBox.Checked from raising the CheckChanged event?
http://www.vbforums.com/showthread.php?758455-CheckBox-code-got-stuck-in-an-infinite-loop-can-not-unchecked-it
EDIT
It helps if your ClearForm() sub doesn't change the CheckedState of your CheckBox back to False every time. Thank you #Visual Vincent for pointing out the obvious. Nothing is wrong with the code, changed the EventHandler to CheckedChanged
Final code (so simple):
Private Sub chkManual_CheckedChanged(sender As Object, e As EventArgs) Handles chkManual.CheckedChanged
If Me.chkManual.Checked Then
Me.dgDataEntry.DataSource = Nothing
Me.cbRegion.SelectedIndex = -1
Me.txtIssueDate.ReadOnly = False
Me.txtCasenum.ReadOnly = False
Me.txtCommnum.ReadOnly = False
ClearForm()
frmPDF.Hide()
Else
Me.cbRegion.SelectedIndex = -1
Me.txtIssueDate.ReadOnly = True
Me.txtCasenum.ReadOnly = True
Me.txtCommnum.ReadOnly = True
End If
End Sub
I'm not sure if you want the CheckBox, when Checked, to Uncheck again automatically or not!?...seems like a weird interface.
At any rate, if you want something to occur when the Check state changes:
Private Sub chkManual_CheckedChanged(sender As Object, e As EventArgs) Handles chkManual.CheckedChanged
If chkManual.Checked Then
Debug.Print("Checked")
Me.dgDataEntry.DataSource = Nothing
Me.cbRegion.SelectedIndex = -1
Me.txtIssueDate.ReadOnly = False
Me.txtCasenum.ReadOnly = False
Me.txtCommnum.ReadOnly = False
ClearForm()
frmPDF.Hide()
Else
Debug.Print("UnChecked")
Me.cbRegion.SelectedIndex = -1
Me.txtIssueDate.ReadOnly = True
Me.txtCasenum.ReadOnly = True
Me.txtCommnum.ReadOnly = True
End If
End Sub
You can uncheck the checkbox without an infinite loop like this:
Private Sub chkManual_CheckedChanged(sender As Object, e As EventArgs) Handles chkManual.CheckedChanged
Static counter As Integer
If chkManual.Checked Then
counter = counter + 1 ' just to show we're not in an infinite loop...
Debug.Print("Checked " & counter) ' just to show we're not in an infinite loop...
chkManual.Checked = False
Else
Debug.Print("UnChecked")
End If
End Sub
Not sure why you'd want to do that...seems like that would basically be a "reset" button as it couldn't stay in a checked state...
Can you remove the code that changes the checkbox state? Ex:
Me.chkManual.Checked = False
The box will check or uncheck without your code having to do it, and the event will only throw once.
I think the easiest way is to remove event listener before handling checkbox checked state. Use try-finally block to ensure that checkbox event listener is always set back.
Try
RemoveHandler chkManual AddressOf chkManual_MouseClick
Me.chkManual.Checked = False
...
Finally
AddHandler chkManual AddressOf chkManual_MouseClick
End Try

Errorprovider shows error on using windows close button(X)

Is there any way to turn the damned error provider off when i try to close the form using the windows close button(X). It fires the validation and the user has to fill all the fields before he can close the form..this will be a usability issue because many tend to close the form using the (X) button.
i have placed a button for cancel with causes validation to false and it also fires a validation.
i found someone saying that if you use Form.Close() function validations are run...
how can i get past this annoying feature.
i have a MDI sturucture and show the form using
CreateExam.MdiParent = Me
CreateExam.Show()
on the mdi parent's menuitem click
and have this as set validation
Private Sub TextBox1_Validating(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles TextBox1.Validating
If String.IsNullOrEmpty(TextBox1.Text) Then
Err.SetError(TextBox1, "required")
e.Cancel = True
End If
If TextBox1.Text.Contains("'") Then
Err.SetError(TextBox1, "Invalid Char")
e.Cancel = True
End If
End Sub
Any help is much appreciated.
googling only showed results where users were having problem using a command button as close button and that too is causing problem in my case
The ValidateChildren() method prevents the form from closing. Paste this code in your form to fix that:
protected override void OnFormClosing(FormClosingEventArgs e) {
e.Cancel = false;
}
This is quite simple fix, in your Form's Closing Event, set a flag to indicate leaving the form, for example blnLeave, when the Form gets loaded, set the flag to False, when the Closing event gets triggered, set that to True within that event handler, then the change as follows would be
Private Sub TextBox1_Validating(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles TextBox1.Validating
If (blnLeave) Then
e.Cancel = False;
Return
End If
If String.IsNullOrEmpty(TextBox1.Text) Then
Err.SetError(TextBox1, "required")
e.Cancel = True
End If
If TextBox1.Text.Contains("'") Then
Err.SetError(TextBox1, "Invalid Char")
e.Cancel = True
End If
End Sub
Edit: Amended this answer for inclusion as per OP's comments. My suggestion is to handle the Form's Closed Event as shown
Private Sub Form1_FormClosed(ByVal sender As System.Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles MyBase.FormClosed
blnLeave = True
End Sub
And handle it here in the Form's window procedure override as shown here....
Private Const SC_CLOSE As Integer = &HF060
Private Const WM_MENUSELECT As Integer = &H11F
Private Function LoWord(ByVal Num As Integer) As Integer
LoWord = Num & &HFFFF
End Function
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If m.Msg = WM_MENUSELECT Then
If LoWord(m.WParam.ToInt32()) = SC_CLOSE Then
' Handle the closing via system Menu
blnLeave = True
End If
End If
MyBase.WndProc(m)
End Sub