Why is my event handler firing two times? - vb.net

I have a bunch of panels that I am adding to a single parent panel and I want to add event listeners to all of the panels but not until after they have all been added to the parent (becuase I don't want the event listeners firing each time a new panel gets added). So I am using the following code:
Dim temp_object As question_bar = Nothing
For Each q As Object In review_holder.Controls
If TypeOf q Is question_bar Then
temp_object = q
AddHandler temp_object.Resize, AddressOf temp_object.resize_me
End If
Next
For Each q As Object In review_holder.Controls
If TypeOf q Is question_bar Then
temp_object = q
temp_object.resize_me()
End If
Next
But I noticed that the resize_me() subroutine is getting fired twice for each control. I only want it to fire once. So I traced it out using this code
MsgBox((New System.Diagnostics.StackTrace).GetFrame(1).GetMethod.Name)
and I see that each time it gets called the calling methods are both this subroutine and _Lambda$_365. What the heck is that? How do I find out where that is coming from?
BTW, this is a winforms app using VS2012.
EDIT ------------------------------------------------------------------------
Public Sub resize_me()
MsgBox((New System.Diagnostics.StackTrace).GetFrame(1).GetMethod.Name)
If Me.minimized = True Then
Me.Height = 0
Exit Sub
End If
number_panel.Width = my_parent.number_width
number_text.Width = my_parent.number_width
number_separator.Left = number_panel.Right
question_panel.Left = number_separator.Right
question_panel.Width = question_panel.Parent.Width * initial_question_width + (question_padding * 2)
End Sub

Well changing size properties when you are inside a resize event could explain why your code is recalled again a second time. Usually I try to avoid this kind of situations but this is not always possible. In these cases then a global variable that acts as a flag to block the reentry could save the day
Dim insideResize As Boolean
Public Sub resize_me()
if insideResize = True Then
Exit Sub
End if
insideResize = True
Try
If Me.minimized = True Then
Me.Height = 0
Exit Sub
End If
number_panel.Width = my_parent.number_width
number_text.Width = my_parent.number_width
number_separator.Left = number_panel.Right
question_panel.Left = number_separator.Right
question_panel.Width = question_panel.Parent.Width * initial_question_width + (question_padding * 2)
Finally
insideResize = False
End Try
End Sub
To stay on the safe side with this patterns remember to always use a Try/Finally block to be sure that when you exit from the Resize event the global flag is correctly set back to false.

Related

Task is running and cannot be finished

Have strange behaviour in my task which is not finishing. I use this all the time but i suppose its because sub i am passing to it is iteracting with form - changing selection and refreshing some listbox probably therefore its stack there but i am not sure. Lets see the code:
This is the sub i want to be run in task:
Public Sub UnselectExistingConnectionsItems()
Dim SentenceId, SubSubKategorieId, SubSectionId As Integer
SubSectionId = CbSubSections.SelectedValue 'combobox
If WithSubSubkategorie = SubSubKategorieEnum.Without Then
SubSubKategorieId = 0
Else
SubSubKategorieId = CbSubSubKategorie.SelectedValue 'combobox
End If
Unselect:
For i As Integer = 0 To LB_Sentences.SelectedItems.Count - 1
Dim sKey As ListBoxItem
sKey = LB_Sentences.SelectedItems(i)
SentenceId = HtmlDescription.HtmlSentence.GetSentenceIdByName(sKey.Text)
If HtmlDescription.HtmlSubSubSections_Sentences.CheckIfConnectionAlreadyExist(SentenceId, SubSectionId, SubSubKategorieId) Then
sKey.IsSelected = False
LB_Sentences.Refresh()
GoTo Unselect
End If
Next
End Sub
i put it to Task like this:
Dim pic As New FrmCircularProgress(eCircularProgressType.Line)
Dim work As Task = Task.Factory.StartNew(Sub()
'--Run lenghty task UnselectExistingConnectionsItems()
'--Close form once done (on GUI thread)
pic.Invoke(New Action(Sub() pic.StopCircular()))
pic.Invoke(New Action(Sub() pic.Close()))
End Sub)
'--Show the form
pic.ShowDialog()
Task.WaitAll(work)
and FrmCircularProgress is just form ( i use it almost everywhere where i have to user wait and its working besides this particural case):
Public Class FrmCircularProgress
Sub New(progressType As DevComponents.DotNetBar.eCircularProgressType)
InitializeComponent()
CircularProgress1.ProgressBarType = progressType
StartCircular()
End Sub
Public Sub StartCircular()
Me.CircularProgress1.IsRunning = True
End Sub
Public Sub StopCircular()
Me.CircularProgress1.IsRunning = False
End Sub
End Class
what could be wrong? is it because procedure is interacting with listbox and combobxes? If so how to fix that, i read something about invoking listbox and comboboxes but have no idea how to fix that.
EDIT:
I think besides those lines:
sKey.IsSelected = False
LB_Sentences.Refresh()
I have to make those:
LB_Sentences.Invoke(Sub() sKey.IsSelected = False
End Sub)
LB_Sentences.Invoke(Sub() LB_Sentences.Refresh()
End Sub)
because i am in diffrent thread. Somehow i dont know how to convert those lines:
SubSectionId = CbSubSections.SelectedValue
SubSubKategorieId = CbSubSubKategorie.SelectedValue
probably loop also have to be invoked. Waiting your help.
There is a rule that says "The only thread that can modify a control in a window is the thread that created the window". Any other thread trying to modify something in the window will generate a cross-thread call exception.
So in your first edit you got it right, you have to invoke the functions.
However, this doesn't fix your problem of not finishing Task.
I believe that doing sKey.IsSelected = False does not unselect anything in your ListBox, therefore causing an infinite loop... Also that Goto statement is very bad programming habits and should not be used. There is always another solution that will make your code easier to debug/maintain/read...
ListBoxItem is not a type that exists in the .Net Framework. So either you created that class either it's something else (and I don't know what...)
What you can do to solve your problem is :
Get the indices of all selected items in a list
Run through your list, and check if they should be selected :
If they should be selected, do nothing
if they shouldn't, unselect them.
Which makes your code like this (and you remove that ugly Label and Goto that you don't want in your code)...
Public Sub UnselectExistingConnectionsItems()
Dim SentenceId, SubSubKategorieId, SubSectionId As Integer
SubSectionId = CbSubSections.SelectedValue 'combobox
If WithSubSubkategorie = SubSubKategorieEnum.Without Then
SubSubKategorieId = 0
Else
SubSubKategorieId = CbSubSubKategorie.SelectedValue 'combobox
End If
'We create an array to remind our initial selection
Dim sel = New Integer(LB_Sentences.SelectedItems.Count - 1) {}
LB_Sentences.SelectedIndices.CopyTo(sel, 0)
For i = 0 To sel.Length - 1
Dim sKey As ListBoxItem
'We get our selected item
sKey = LB_Sentences(sel(i))
SentenceId = HtmlDescription.HtmlSentence.GetSentenceIdByName(sKey.Text)
If HtmlDescription.HtmlSubSubSections_Sentences.CheckIfConnectionAlreadyExist(SentenceId, SubSectionId, SubSubKategorieId) Then
'We must remove it from the selection
LB_Sentences.Invoke(Sub() LB_Sentences.SelectedItems.Remove(sKey))
End If
Next
'We do the Refresh at the end so we gain some process time...
LB_Sentences.Invoke(Sub() LB_Sentences.Refresh())
End Sub

Dispose dynamic created Controls (Labels) in a quick way

i have a lot of dynamic created Labels in a Windows Form, nearly 30 x 80 pieces.
The disposing of this controls works very slow. It takes nearly 1 second where I can see it work step to step.
I use the following code (to dispose all the controls, where the Name starts with "var":
For i As Integer = dlg_main.Controls.Count - 1 To 0 Step -1
If LCase(Left(dlg_main.Controls(i).Name, 3)) = "var"
dlg_main.Controls(i).Dispose()
End If
Next
Is the a quicklier way to dispose the controls?
One option would be to put the labels in a panel then Dispose the panel.
Panel1.Dispose()
problem is solved, see how to declare and to initialize:
Module mdl_main
Private pn1 As System.Windows.Forms.Panel
Public Sub ShowPanelAgain()
pn1 = New System.Windows.Forms.Panel
pn1.Name = "dynPanel1"
dlg_Main.Controls.Add(pn1)
pn1.AutoSize = True
pn1.AutoScroll = True
End Sub
Sub PanelLöschen()
Try
If pn1.IsDisposed = False Then
pn1.Dispose()
dlg_Main.Text = "pn gelöscht"
End If
Catch ex As Exception
MsgBox("Fehler: " & ex.Message)
End Try
End Sub

Progess bar in vb.net 2008 while File loading into the grid

I have written in vb.net for progress bar. I am thinking of there is a better way than this.
Here is what my code is :
Private Function ImportDataFiles(ByRef pobjDataLoadDTO As DataLoadDTO) As Boolean
Try
lblStatus.Visible = True
lblStatus.Text = ""
myProgressBar.Visible = True
myProgressBar.Value = 0
For Each drRow As ImportData.TRow In pobjDataLoadDTO.FileInfo.Select("categ_code = 'abc'")
If pobjDataLoadDTO.FileTimes.ContainsKey(drRow.KEY_CODE) AndAlso _
pobjDataLoadDTO.FileTimes(drRow.KEY_CODE) > pobjDataLoadDTO.UploadTimes(drRow.KEY_CODE) Then
pobjDataLoadDTO.DestinationTablename = drRow.KEY_CODE
If mobjDataLoadBO.ImportDataFiles(pobjDataLoadDTO) Then
drRow.DATA_TXT = mobjCommonBO.ONow.ToString
End If
End If
lblStatus.Text = drRow.KEY_CODE.Trim & "is loading...."
lblStatus.Refresh()
myProgressBar.PerformStep()
lblStatus.Refresh()
Next
Return True
Catch ex As Exception
Return False
End Try
End Function
Right now It is working, But I want to use more efficient way, like using Backgroundworkerprocess...etc., Any Ideas on this one?
Since your function runs in main thread, I assume your application is freezing and not very smooth while upload in progress.
1 - Drop Backgroundworker control on the form
2 - set "reportProgress" property of the worker to "True"
3 - Move your loop code into "DoWork" event of the worker control. And call worker.RunWorkerAsync. You can pass needed arguments to it
4- the code that refreshes progress bar move into "ProgressChange" event of the worker. This is important as you can't call control from worker thread. and ProgressChange is running in the main thread. You can also delete "Refresh" method call. That will not be needed anymore. Every time you want to refresh the progress bar call "ReportProgress" method of the worker
5-Use "RunWorkerCompleted" worker event, to do your clean up, and hide your progress bar
Its also might be a good idea to check if worker is already working before initiating, like
If worker.IsBusy Then
If worker.CancellationPending = False Then
worker.CancelAsync()
End If
Exit Sub
End If

MS-Access hidding and showing a button conditionally through vba

I am developing a quality control system for my company, and I i want to connect it to a label printer, which is already taken care off, the problem now is with the button itself.
I want the print label button to only be enabled and visible after the whole check was made, what i've got now is this:
Private Sub Form_BeforeUpdate(Cancel As Integer)
Button_label.Visible = False
Button_label.Enabled = False
End Sub
Private Sub Motor_OK_Change()
Dim ok As Boolean
ok = Motor_OK.Value
If ok = 1 Then
Button_label.Visible = True
Button_label.Enabled = True
End If
End Sub
It does work on conceiling the button, but it fails when trying to enable it again and making it visible upon making the check. It's important to refer that I have tried using If ok = True instead of If ok= 1, I dont know how important that is.
Thanks
You are not testing for both conditions (ok=0), perhaps the following will work:
Replace:
If ok = 1 Then
Button_label.Visible = True
Button_label.Enabled = True
End If
With:
Button_label.Visible = ok
Button_label.Enabled = ok
New Solution:
Instead of using the Form Before_Update event, use Form_Current event. If there is only 1 record, you can also use the Form_Load event. I would also suggest that you remove the Visible property and use only the Enabled one so users can see their is print functionality.
Private Sub Form_Current()
'button_Label.Visible = False
button_Label.Enabled = False
End Sub
Instead of using the Motor_OK Before_Updateevent, use After_Update event
Private Sub Motor_OK_AfterUpdate()
Dim ok As Boolean ' defaults to False
If IsNumeric(Motor_OK.Value) Then ok = True ' remove this statement if Motor_OM is
' alpha numeric.
If Not IsNull(Motor_OK.Value) Then ok = True ' otherwise following statement
' to avoid NULL error
'button_Label.Visible = ok
button_Label.Enabled = ok
End Sub
Let me know if I can be of further help.

Picturebox location change 20 times per second not redrawing

I'm trying to make a few images do the nice slidey thingy that I've seen lots of Microsoft applications use. The one where the movement starts slow speeds up half way there and then comes to a nice slow stop in it's new location. I've got all the calculations figured out, getting and setting the picture box locations, Confirmation using console.writeline that the image locations are correct, and even a test run that works in a simplified format.
But in the full blown version It's not repainting the image. In fact, it looks like nothing has happened at all while the script is running. I've tried Me.Refresh(), Invalidate(), Timer.Enabled = True/False, and Me.Update(). None of which have worked. The last step is the most frustrating: I'm calling my SetPanelLocation() method at the end to ensure that the panel ends up in the final location regardless of if the movement worked. Nothing happens on this call either, even though immediately after this routine fails I can call the same method from another user event and it starts working again like nothing was wrong.
I'm creating my own PictureBox class called clsFeedImageBox which inherits PictureBox that includes this functionality (along with other features). Each image is only 300x225 pixels so they're not massive images that take a lot of time to redraw. Each instance of this class is in a common Forms.SplitterPanel. I use a lot of comments out of habit so i left them in here, maybe they'll add some light.
Public Class clsFeedImgBox
Inherits PictureBox
Private iRank As Integer 'rank in whatever feed this file gets put in
Private iRankTarget As Integer 'rank to move to when rank feed event starts
Private iTopStart As Integer 'starting top location before feed event
Private iTopTarget As Integer 'final Top location after feed event
Private WithEvents tMyTimer As New System.Timers.Timer
Private WithEvents oParent As FeedBase 'splitter panel, all location info comes from the parent
Public Sub New(ByRef sender As FeedBase, ByVal rank as Integer)
'set objects
oParent = sender
'set .Image property to pre-made thumbnail
Image.FromFile(ThumbPath) 'ThumbPath is a property which is set by this point (some code has been removed)
'setup initial position
setPanelLocation(rank)
'set autosize
Me.SizeMode = PictureBoxSizeMode.StretchImage
'set Image Scroll timer interval to 20 fps (1000 / 20 = 50)
tMyTimer.Interval = 50
End Sub
Public Sub scroll(ByVal newRank As Integer)
'setPanelLocation(newRank) <== this works, timed movements don't
iRankTarget = newRank
iTopStart = Me.Top
iTopTarget = oParent.ImgTop(newRank) 'gets an integer for the new Top location
tMyTimer.Start()
End Sub
Private Sub myScrollStep() Handles tMyTimer.Elapsed
'tMyTimer.Enabled = False 'this idea with the enabled = True at the end didn't work
iTickCount += 1
Dim iScrollPerc As Integer 'scroll % between Start and End * 100
iScrollPerc = oParent.ScrollStep(iTickCount, Rank) 'this part works
Console.WriteLine(strThumbName & " scrollPerc: " & iScrollPerc.ToString)
If iScrollPerc >= 100 Then
'scroll event complete
Console.WriteLine(strThumbName & " SetFinalLocation")
Me.setPanelLocation(iRankTarget) '<== This line doesn't work here, but works when called by other means
'stop Feed updates
tMyTimer.Stop()
'reset iTickCount for next movement
iTickCount = 0
Else
'scrolling still going
Dim newTop As Integer
newTop = Math.Round(iTopTarget - (((100 - iScrollPerc) * (iTopTarget - iTopStart)) / 100)) 'this part works
'Console.WriteLine(strThumbName & " TopTarget: " & newTop)
Me.Top = newTop 'Nothing happens here
End If
'Me.Left = oParent.ImgLeft
'Me.Width = oParent.ImgWidth
'Me.Height = oParent.ImgHeight 'that didn't work
'Me.Refresh() 'this didn't work
'Invalidate() 'this didn't do much good either
'Me.Update() 'Aaaaand no cigar, time for StackOverflow
'tMyTimer.Enabled = True
End Sub
Public Sub setPanelLocation(ByVal rank As Integer)
iRank = rank
Me.MyRePaint()
End Sub
Public Sub MyRePaint()
'repaint image box with everything in it's current rank
Me.Left = oParent.ImgLeft
Me.Top = oParent.ImgTop(iRank)
Me.Width = oParent.ImgWidth
Me.Height = oParent.ImgHeight
End Sub
End Class
What gives? There must be some inner workings of VB.NET that will help me figure this out. I'm using VS 2012 and Win8
You could make a WPF application and use a Slider control instead of "manually" making a slider with planes, picture boxes, etc, etc.