Progress Bar will not work, even when all the program does is show a Progress Bar - vb.net

I have a form which, at present, is doing nothing but opening. The form has 2x controls - a 'Close' button and a Progress Bar. However, when I open the form, I get nothing. The Progress Bar just sits there doing nothing.
I've tried both Marquee (which I understand may not work in Windows 8) and Continuous, but I can't get anywhere with either.
This is how I'm showing the form when the program starts -
Sub main()
Dim ProgressForm As New formProgress
ProgressForm.ShowDialog()
End Sub
And below are the properties for the Progress Bar. Am I missing something that would get this bar working? Thanks.
Additional Information
For my full program, I did originally try using the Block style for the Progress Bar, but I kept getting the following error when trying to update the Progress Bar from a BackgroundWorker. This is why I am trying to get a simple Marquee/Continuous bar working instead.
Additional information: Cross-thread operation not valid: Control 'proWorking' accessed from a thread other than the thread it was created on.

if you use marquee style you have to set marqueeanimationspeed to some value
//
// progressBar1
//
this.progressBar1.Location = new System.Drawing.Point(91, 118);
this.progressBar1.MarqueeAnimationSpeed = 50;
this.progressBar1.Name = "progressBar1";
this.progressBar1.Size = new System.Drawing.Size(100, 23);
this.progressBar1.Style = System.Windows.Forms.ProgressBarStyle.Marquee;
this.progressBar1.TabIndex = 0;
and use continuous style with marqueeanimationsspeed 0 to stop it

For the Progressbar to do something (apart from the Marquee Style) you need to set the Value property. If you have .Minimum=0 and .Maximum=100 then a .Value of 50 means that the Progressbar is half full. If you should use Continuous or Blocks Style depends on the Visual Styles settings and doesn't make a real difference here on Win 7 (maybe it does for example under Win XP).
The Marquee style means that you don't know how far your task has proceeded. The Progressbar then shows a continously moving piece (is only visible at runtime!!). I just tested it in Win 7 and it works.

Here's a little boilerplate I use with a BackgroundWorker and a ProgressBar and Label.
Public Class BackgroundWorkerUI
Private args As New ProgressArgs
Private Sub bw_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles bw.DoWork
'set ProgressBar style to Marquee
args.Style = ProgressBarStyle.Marquee
bw.ReportProgress(0) 'triggers the progress changed event to update UI on correct thread
'some long operation
Threading.Thread.Sleep(5000)
'Set ProgressBar style to Continuous
args.Style = ProgressBarStyle.Continuous
For i As Integer = 0 To 100
If bw.CancellationPending Then
e.Cancel = True
Exit For
End If
args.Current = i
args.Max = 100
args.Status = String.Format("({0} of {1}) Updating...", args.Current, args.Max)
bw.ReportProgress(0)
'some operation
Threading.Thread.Sleep(100)
Next
End Sub
Private Sub bw_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles bw.ProgressChanged
lblStatus.Text = args.Status
If args.Style = ProgressBarStyle.Marquee Then
bar.Style = args.Style
bar.MarqueeAnimationSpeed = 15
Else
bar.Style = ProgressBarStyle.Continuous
bar.Minimum = args.Min
bar.Maximum = args.Max
bar.Value = args.Current
End If
End Sub
Private Sub bw_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bw.RunWorkerCompleted
If e.Error IsNot Nothing Then
MessageBox.Show(e.Error.Message, "Background Worker Exception", MessageBoxButtons.OK, MessageBoxIcon.Error)
Else
If e.Cancelled Then
lblStatus.Text = "Operation canceled"
Else
lblStatus.Text = "Done"
End If
End If
End Sub
Private Sub BackgroundWorkerUI_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
If bw.IsBusy Then
Dim r = MessageBox.Show("A background process is still running. Are you sure you want to quit?", "Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
If r = Windows.Forms.DialogResult.Yes Then
bw.CancelAsync()
End If
e.Cancel = True
End If
End Sub
Private Class ProgressArgs
Inherits EventArgs
Public Property Status As String
Public Property Current As Integer
Public Property Min As Integer
Public Property Max As Integer
Public Property Style As ProgressBarStyle
Public Sub New()
Status = ""
Current = 0
Min = 0
Max = 0
Style = ProgressBarStyle.Continuous
End Sub
End Class
End Class

Related

Highlight scroll bar of textbox [duplicate]

Is it possible to show/hide the scroll bar in a text box only when the line count in the text box is more than the number of lines displayed?
Consider using the RichTextBox -- it has that behavior built in.
Thanks dummy, it works! Here short version of dummy answer in c#
Call this code at the end of your SizeChanged and TextChanged handlers:
Size textBoxRect = TextRenderer.MeasureText(
this.YourTextBox.Text,
this.YourTextBox.Font,
new Size(this.YourTextBox.Width, int.MaxValue),
TextFormatFlags.WordBreak | TextFormatFlags.TextBoxControl);
try
{
this.YourTextBox.ScrollBars = textBoxRect.Height > this.YourTextBox.Height ?
ScrollBars.Vertical :
ScrollBars.None;
} catch (System.ComponentModel.Win32Exception)
{
// this sometimes throws a "failure to create window handle" error.
// This might happen if the TextBox is unvisible and/or
// too small to display a toolbar.
}
Public Class TextBoxScrollbarPlugin
Private WithEvents mTarget As TextBox
''' <summary>
''' After the Handle is created, mTarget.IsHandleCreated always returns
''' TRUE, even after HandleDestroyed is fired.
''' </summary>
''' <remarks></remarks>
Private mIsHandleCreated As Boolean = False
Public Sub New(item As TextBox)
mTarget = item
mIsHandleCreated = mTarget.IsHandleCreated
End Sub
Private Sub Update()
If Not mTarget.IsHandleCreated Then
Return
ElseIf Not mIsHandleCreated Then
Return
End If
Dim textBoxRect = TextRenderer.MeasureText(mTarget.Text,
mTarget.Font,
New Size(mTarget.Width, Integer.MaxValue),
TextFormatFlags.WordBreak + TextFormatFlags.TextBoxControl)
Try
If textBoxRect.Height > mTarget.Height Then
mTarget.ScrollBars = ScrollBars.Vertical
Else
mTarget.ScrollBars = ScrollBars.None
End If
Catch ex As System.ComponentModel.Win32Exception
'this sometimes throws a "failure to create window handle"
'error.
'This might happen if the TextBox is unvisible and/or
'to small to display a toolbar.
If mLog.IsWarnEnabled Then mLog.Warn("Update()", ex)
End Try
End Sub
Private Sub mTarget_HandleCreated(sender As Object, e As System.EventArgs) Handles mTarget.HandleCreated
mIsHandleCreated = True
End Sub
Private Sub mTarget_HandleDestroyed(sender As Object, e As System.EventArgs) Handles mTarget.HandleDestroyed
mIsHandleCreated = False
End Sub
Private Sub mTarget_SizeChanged(sender As Object, e As System.EventArgs) Handles mTarget.SizeChanged
Update()
End Sub
Private Sub mTarget_TextChanged(sender As Object, e As System.EventArgs) Handles mTarget.TextChanged
Update()
End Sub
End Class
Private mPlugins As New List(Of Object)
Private Sub Form_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
mPlugins.Add(New TextBoxScrollbarPlugin(txtBoxOne))
mPlugins.Add(New TextBoxScrollbarPlugin(txtBoxTwo))
mPlugins.Add(New TextBoxScrollbarPlugin(txtBoxThree))
End Sub
I've got tnimas solution working in vb. Functions quite well as written and I've not seen the errors.
Private Sub TextBoxSizeChanged(sender As Object, e As EventArgs) Handles Me.SizeChanged
Dim textBoxRect As Size = TextRenderer.MeasureText(TextBox.Text, TextBox.Font, New Size(TextBox.Width, Integer.MaxValue), TextFormatFlags.WordBreak Or TextFormatFlags.TextBoxControl)
Try
TextBox.ScrollBar = If(textBoxRect.Height > TextBox.Height, ScrollBars.Vertical, ScrollBars.None)
Catch ex As Exception
'handle error
End Try
End Sub
I myself tried tnimas' solution but unable to catch the exception, so I use the WinApi to toggle the visible state of scrollbars instead like so:
Size textBoxRect = TextRenderer.MeasureText(YourTextBox.Text, YourTextBox.Font, new Size(YourTextBox.Width, int.MaxValue), TextFormatFlags.WordBreak | TextFormatFlags.TextBoxControl);
WinApi.ShowScrollBar(YourTextBox.Handle, (int)WinApi.ScrollBar.SB_VERT, textBoxRect.Height > YourTextBox.Height ? true : false);
This method does not cause the exception, though should note that hiding scrollbars like this will disable scroll messages, but this is fine if you're just hiding the scrollbars for when the text area can't be scrolled anyway.

Timer does not update labels

In a Windows Forms application, I've got a timer with the following code:
Public Class SyncForm
Public pbvalue As Integer = 0
Public pbProcess As String = ""
Sub DataProcess()
pb.Value += 1
pbProcess = "Read, match and compare"
...
pb.Value += 1
pbProcess = "Exporting..."
End Sub
Private Sub timerpb_Tick(sender As Object, e As EventArgs) Handles timerpb.Tick
pb.Value = pbValue
lbReadProcess.Text = pbProcess
lbReadProcess.Refresh()
pb.PerformStep()
End Sub
End Class
I start the DataProcess sub with a button on this form.
During the process, the progress bar is updated correctly, but the label on the form (lbReadProcess), that should indicate the currently running process, not. Can someone give me a hint, why not? Thanks.

Vb.Net Background Worker Updating UI Not Working

I have a background worker that is supposed to be updating a ToolStripLabel with some status messages. However, the updating is not happening, but no errors are being thrown. Here's the code I am using:
Private Sub BackgroundWorker3_DoWork(sender As Object, e As DoWorkEventArgs) Handles BackgroundWorker3.DoWork
BackgroundWorker3.WorkerReportsProgress = True
Dim Counter As Integer = 0
Do Until BW1Running = False
Counter = Counter + 1
Threading.Thread.Sleep(1000)
Incident_Form.BackgroundWorker3.ReportProgress(Counter)
If Counter >= 100 Then
e.Result = False
Return
End If
Loop
If BW1Running = False Then
Counter = 100
Incident_Form.BackgroundWorker3.ReportProgress(Counter)
End If
End Sub
Private Sub BackgroundWorker3_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles BackgroundWorker3.ProgressChanged
Me.ToolStripStatusLabel1.Text = e.ProgressPercentage.ToString
End Sub
Nothing happens when the ProgressChanged is fired. I've debugged it and it'll print a line to the output window, but it will not update that label. Any ideas on what I'm missing?
You're calling:
Incident_Form.BackgroundWorker3.ReportProgress()
instead of just:
BackgroundWorker3.ReportProgress()
Your BackgroundWorker3_ProgressChanged method is subscribed to the ProgressChanged event of the BackgroundWorker located in the current form, not in the Incident_Form form.
Remove Incident_Form from the beginning of the BackgroundWorker3.ReportProgress() calls and you should be good to go.

How to reset the close reason when close is cancelled

Question
Is it possible to reset the CloseReason provided by the FormClosingEventArgs in the FormClosing event of a modal dialog?
Symptoms
Setting the DialogResult of a modal dialog can result in an "incorrect" CloseReason if the close event have previously been cancelled.
Details
(The following code is just sample code to highlight the inconvenience)
Imagine I have a form with two buttons, OK and Cancel, displayed as a modal dialog.
Me.btnOk = New Button With {.DialogResult = Windows.Forms.DialogResult.OK}
Me.btnCancel = New Button With {.DialogResult = Windows.Forms.DialogResult.Cancel}
Me.AcceptButton = Me.btnOk
Me.CancelButton = Me.btnCancel
Any attempts to close the form will be cancelled.
If I click each button (including the [X] - close form button) in the following order, the close reasons will be as following:
Case 1
btnOk::::::::::: None
btnCancel::: None
X::::::::::::::::::: UserClosing
Now, if I repeat the steps you'll see that the UserClosing reason will persist:
btnOk::::::::::: UserClosing
btnCancel::: UserClosing
X::::::::::::::::::: UserClosing
Case 2
X::::::::::::::::::: UserClosing
btnCancel::: UserClosing
btnOk::::::::::: UserClosing
Same here. Once you click the X button the close reason will always return UserClosing.
Sample application
Public Class Form1
Public Sub New()
Me.InitializeComponent()
Me.Text = "Test"
Me.FormBorderStyle = Windows.Forms.FormBorderStyle.FixedDialog
Me.MinimizeBox = False
Me.MaximizeBox = False
Me.ClientSize = New Size(75, 25)
Me.StartPosition = FormStartPosition.CenterScreen
Me.btnOpenDialog = New Button() With {.TabIndex = 0, .Dock = DockStyle.Fill, .Text = "Open dialog"}
Me.Controls.Add(Me.btnOpenDialog)
End Sub
Private Sub HandleOpenDialog(sender As Object, e As EventArgs) Handles btnOpenDialog.Click
Using instance As New CustomDialog()
instance.ShowDialog()
End Using
End Sub
Private WithEvents btnOpenDialog As Button
Private Class CustomDialog
Inherits Form
Public Sub New()
Me.Text = "Custom dialog"
Me.ClientSize = New Size(400, 200)
Me.StartPosition = FormStartPosition.CenterParent
Me.FormBorderStyle = Windows.Forms.FormBorderStyle.FixedDialog
Me.MinimizeBox = False
Me.MaximizeBox = False
Me.tbOutput = New RichTextBox() With {.TabIndex = 0, .Bounds = New Rectangle(0, 0, 400, 155), .ReadOnly = True, .ScrollBars = RichTextBoxScrollBars.ForcedBoth, .WordWrap = True}
Me.btnExit = New Button With {.TabIndex = 3, .Text = "Exit", .Bounds = New Rectangle(10, 165, 75, 25), .Anchor = (AnchorStyles.Bottom Or AnchorStyles.Left)}
Me.btnOk = New Button With {.TabIndex = 1, .Text = "OK", .Bounds = New Rectangle(237, 165, 75, 25), .Anchor = (AnchorStyles.Bottom Or AnchorStyles.Right), .DialogResult = Windows.Forms.DialogResult.OK}
Me.btnCancel = New Button With {.TabIndex = 2, .Text = "Cancel", .Bounds = New Rectangle(315, 165, 75, 25), .Anchor = (AnchorStyles.Bottom Or AnchorStyles.Right), .DialogResult = Windows.Forms.DialogResult.Cancel}
Me.Controls.AddRange({Me.tbOutput, Me.btnExit, Me.btnOk, Me.btnCancel})
Me.AcceptButton = Me.btnOk
Me.CancelButton = Me.btnCancel
End Sub
Private Sub HandleExitDialog(sender As Object, e As EventArgs) Handles btnExit.Click
Me.exitPending = True
Me.Close()
End Sub
Protected Overrides Sub OnFormClosing(e As FormClosingEventArgs)
If (Not Me.exitPending) Then
e.Cancel = True
Me.tbOutput.Text += (String.Format("DialogResult={0}, CloseReason={1}{2}", Me.DialogResult.ToString(), e.CloseReason.ToString(), Environment.NewLine))
Me.DialogResult = Windows.Forms.DialogResult.None
End If
MyBase.OnFormClosing(e)
End Sub
Private exitPending As Boolean
Private WithEvents btnExit As Button
Private WithEvents btnCancel As Button
Private WithEvents btnOk As Button
Private WithEvents tbOutput As RichTextBox
End Class
End Class
Update
I was of the impression that if either the Form.AcceptButton or Form.CancelButton (IButtonControl) was clicked the close reason would be set to UserClosing, but this is not the case. In the following code you'll see that all it do is setting the DialogResult of the owning form to that of its own DialogResult.
Protected Overrides Sub OnClick(ByVal e As EventArgs)
Dim form As Form = MyBase.FindFormInternal
If (Not form Is Nothing) Then
form.DialogResult = Me.DialogResult
End If
MyBase.AccessibilityNotifyClients(AccessibleEvents.StateChange, -1)
MyBase.AccessibilityNotifyClients(AccessibleEvents.NameChange, -1)
MyBase.OnClick(e)
End Sub
The Control class do have a property named CloseReason but it's defined as Friend, thus not accessible.
I also thought that setting the forms DialogResult would result in a WM message being sent, but all it does is setting a private field.
So I delved into reflector and followed the stack. The following image is a highly simplified illustration.
This is how the CheckCloseDialog method looks like:
Friend Function CheckCloseDialog(ByVal closingOnly As Boolean) As Boolean
If ((Me.dialogResult = DialogResult.None) AndAlso MyBase.Visible) Then
Return False
End If
Try
Dim e As New FormClosingEventArgs(Me.closeReason, False)
If Not Me.CalledClosing Then
Me.OnClosing(e)
Me.OnFormClosing(e)
If e.Cancel Then
Me.dialogResult = DialogResult.None
Else
Me.CalledClosing = True
End If
End If
If (Not closingOnly AndAlso (Me.dialogResult <> DialogResult.None)) Then
Dim args2 As New FormClosedEventArgs(Me.closeReason)
Me.OnClosed(args2)
Me.OnFormClosed(args2)
Me.CalledClosing = False
End If
Catch exception As Exception
Me.dialogResult = DialogResult.None
If NativeWindow.WndProcShouldBeDebuggable Then
Throw
End If
Application.OnThreadException(exception)
End Try
If (Me.dialogResult = DialogResult.None) Then
Return Not MyBase.Visible
End If
Return True
End Function
As you can see the modal message loop checks the DialogResult in every cycle and if the conditions are met it will use the stored CloseReason (as observed) when creating the FormClosingEventArgs.
Summary
Yes, I know that the IButtonControl interface have a PerformClick method which you can call programmatically, but still, IMO this smells like a bug. If clicking a button is not a result of a user action then what is?
It is pretty important to understand why this is behaving the way it does, you are liable to get yourself into trouble when you rely in the CloseReason too much. This is not a bug, it is a restriction due to the way Windows was designed. One core issue is the way the WM_CLOSE message is formulated, it is the one that sets the train in motion, first firing the FormClosing event.
This message can be sent for lots of reasons, you are familiar with the common ones. But that's not where it ends, other programs can send that message as well. You can tell the "flaw" from the MSDN Library article I linked to, the message is missing a WPARAM value that encodes the intent of the message. So there isn't any way for a program to provide a reasonable CloseReason back to you. Winforms is forced to guess at a reason. It is of course an entirely imperfect guess.
That's not where it ends, the DialogResult property is a problem as well. It will force a dialog to close when any code assigns that property. But again the same problem, there isn't any way for such code to indicate the intent of the assignment. So it doesn't, it leaves in internal Form.CloseReason property at whatever value it had before, None by default.
This was "properly" implemented in .NET 1.0, there was only the Closing event and it didn't give a reason at all. But that didn't work out so well either, apps that used it chronically prevented Windows from shutting down. They just didn't know that it was inappropriate to, say, display a message box. The .NET 2.0 FormClosing event was added as a workaround for that. But it needs to work with the imperfect guess.
It is important to rate the CloseReason values, some are very accurate and some are just guesses:
CloseReason.WindowsShutdown - reliable
CloseReason.ApplicationExitCall - reliable
CloseReason.MdiFormClosing - reliable, not very useful
CloseReason.FormOwnerClosing - reliable, not very useful
CloseReason.TaskManagerClosing - complete guess, will be returned when any program sends a WM_CLOSE message, not just Task Manager
CloseReason.UserClosing - complete guess, will also be returned when your program calls the Close() method for example
CloseReason.None - it just doesn't know.
Yes, Winforms not setting the CloseReason back to None when your FormClosing event handler cancels is arguably a bug. But it isn't the kind of bug that actually really matters. Since you can't treat UserClosing and None differently anyway.
I would probably call that a bug.
As you mentioned, the CloseReason property is marked internal (or Friend in VB.Net terms) so one work-around to the problem is using Reflection to reset that value yourself:
Protected Overrides Sub OnFormClosing(e As FormClosingEventArgs)
If Not exitPending Then
e.Cancel = True
tbOutput.AppendText(String.Format("DialogResult={0}, CloseReason={1}{2}", _
Me.DialogResult.ToString(), e.CloseReason.ToString(), _
Environment.NewLine))
Dim pi As PropertyInfo
pi = Me.GetType.GetProperty("CloseReason", _
BindingFlags.Instance Or BindingFlags.NonPublic)
pi.SetValue(Me, CloseReason.None, Nothing)
End If
MyBase.OnFormClosing(e)
End Sub
No guarantee that this code would work on future versions of WinForms, but I'm guessing it's a safe bet these days. :-)
Private Const WM_SYSCOMMAND As Int32 = &H112
Private Const SC_CLOSE As Int32 = &HF060
'Private Const SC_MAXIMIZE As Int32 = &HF030
'Private Const SC_MINIMIZE As Int32 = &HF020
'Private Const SC_RESTORE As Int32 = &HF120
Private _commandClose As Boolean = False
Protected Overrides Sub WndProc(ByRef m As Message)
If CInt(m.Msg) = WM_SYSCOMMAND Then
If (m.WParam.ToInt32 And &HFFF0) = SC_CLOSE Then _commandClose = True
End If
MyBase.WndProc(m)
End Sub
Private Sub baseClick(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Click
Close()
End Sub
Protected Overrides Sub OnFormClosing(ByVal e As FormClosingEventArgs)
If _commandClose Then DialogResult = ' ...
MyBase.OnFormClosing(e)
End Sub
reference: MSDN - WM_SYSCOMMAND message
hmm, actually, this does work. but unlike the official docs, SC_CLOSE fires for Alt+F4, etc... as well, even though not specifically mentioned.
It does not fire when calling the Form.Close() method. therefore, working as intended.
however, it will still return UserClosing if you call the Close() method, which is by design.
note: SC_SCREENSAVE can be used to detect/prevent screensavers, along with SC_MONITORPOWER. the documentation on that seems a bit vague.

New Multi-threading not working

I'm trying to create a thread so when I click a button it creates a new PictureBox from a class, this is how far I've got but nothing comes up on the screen at all.
Form1 code:
Public Class Form1
Private pgClass As New SecondUIClass
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
pgClass = New SecondUIClass
pgClass.x += 100
pgClass.thread()
End Sub
End Class
Class Code:
Imports System.Threading
Public Class SecondUIClass
Public Const count As Integer = 1000
Public emeny(count - 1) As PictureBox
Public counter As Integer = 0
Public x As Integer = 0
Private trd As Thread
Public Sub thread()
trd = New Thread(AddressOf NewUIThread)
trd.SetApartmentState(ApartmentState.STA)
trd.IsBackground = False
trd.Start()
End Sub
Private Sub NewUIThread()
emeny(counter) = New PictureBox
emeny(counter).BackColor = Color.Red
emeny(counter).Visible = True
emeny(counter).Location = New System.Drawing.Point(x, 100)
emeny(counter).Size = New System.Drawing.Size(10, 50)
Form1.Controls.Add(emeny(counter))
For z = 0 To 13
emeny(counter).Location = New Point(emeny(counter).Location.X + 10, emeny(counter).Location.Y)
Application.DoEvents()
Threading.Thread.Sleep(100)
Next
counter += 1
End Sub
End Class
I have posted something similar before on here but it was different, the pictureBoxes were showing on the screen but I was trying to get them to move at the same time but they wouldn't move, they only moved one at a time. The question that I asked before was this Multi threading classes not working correctly
I made a few assumptions for this answer so it may not work for you out of the box but I think it will put you on the right track without using any Thread.Sleep calls because I personally don't like building intentional slows to my apps but that's a personal preference really.
So For my example I just used a bunch of textboxes because I didn't have any pictures handy to fiddle with. But basically to get it so that the user can still interact with the program while the moving is happening I used a background worker thread that is started by the user and once its started it moves the textboxes down the form until the user tells it to stop or it hits an arbitrary boundary that I made up. So in theory the start would be the space bar in your app and my stop would be adding another control to the collection. For your stuff you will want to lock the collection before you add anything and while you are updating the positions but that is up to your discretion.
So the meat and potatoes:
in the designer of the form I had three buttons, btnGo, btnStop and btnReset. The code below handles the click event on those buttons so you will need to create those before this will work.
Public Class Move_Test
'Flag to tell the program whether to continue or to stop the textboxes where they are at that moment.
Private blnStop As Boolean = False
'Worker to do all the calculations in the background
Private WithEvents bgWorker As System.ComponentModel.BackgroundWorker
'Controls to be moved.
Private lstTextBoxes As List(Of TextBox)
'Dictionary to hold the y positions of the textboxes.
Private dtnPositions As Dictionary(Of Integer, Integer)
Public Sub New()
' Default code. Must be present for VB.NET forms when overwriting the default constructor.
InitializeComponent()
' Here I instantiate all the pieces. The background worker to do the adjustments to the position collection, the list of textboxes to be placed and moved around the form
' and the dictionary of positions to be used by the background worker thread and UI thread to move the textboxes(because in VB.NET you can not adjust controls created on the UI thread from a background thread.
bgWorker = New System.ComponentModel.BackgroundWorker()
Me.lstTextBoxes = New List(Of TextBox)
Me.dtnPositions = New Dictionary(Of Integer, Integer)
For i As Integer = 0 To 10
Dim t As New TextBox()
t.Name = "txt" & i
t.Text = "Textbox " & i
'I used the tag to hold the ID of the textbox that coorelated to the correct position in the dictionary,
' technically you could use the same position for all of them for this example but if you want to make the things move at different speeds
' you will need to keep track of each individually and this would allow you to do it.
t.Tag = i
dtnPositions.Add(i, 10)
'Dynamically position the controls on the form, I used 9 textboxes so i spaced them evenly across the form(divide by 10 to account for the width of the 9th text box).
t.Location = New System.Drawing.Point(((Me.Size.Width / 10) * i) + 10, dtnPositions(i))
Me.lstTextBoxes.Add(t)
Next
'This just adds the controls to the form dynamically
For Each r In Me.lstTextBoxes
Me.Controls.Add(r)
Next
End Sub
Private Sub Move_Test_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Try
'Don't need to do anything here. Placeholder
Catch ex As Exception
MessageBox.Show("Error: " & ex.Message)
End Try
End Sub
Private Sub btnGo_Click(sender As Object, e As EventArgs) Handles btnGo.Click
Try
If Not bgWorker.IsBusy Then
'User starts the movement.
bgWorker.RunWorkerAsync()
End If
Catch ex As Exception
MessageBox.Show("Error: " & ex.Message)
End Try
End Sub
Private Sub btnReset_Click(sender As Object, e As EventArgs) Handles btnReset.Click
Try
'Reset the positions and everything else on the form for the next time through
' I don't set the blnStop value to true in here because it looked cooler to keep reseting the textboxes
' and have them jump to the top of the form and keep scrolling on their own...
For Each r In Me.lstTextBoxes
r.Location = New System.Drawing.Point(r.Location.X, 10)
Next
For i As Integer = 0 To dtnPositions.Count - 1
dtnPositions(i) = 10
Next
Catch ex As Exception
MessageBox.Show("Error: " & ex.Message)
End Try
End Sub
Private Sub bgWorker_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles bgWorker.DoWork
Try
'This is where we do all the work.
' For this test app all its doing is scrolling through each value in the dictionary and incrementing the value
' You could make the dictionary hold a custom class and have them throttle themselves using variables on the class(or maybe travel at an angle?)
For i As Integer = 0 To dtnPositions.Count - 1
dtnPositions(i) += 1
Next
Catch ex As Exception
blnStop = True
End Try
End Sub
Private Sub bgWorker_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bgWorker.RunWorkerCompleted
Try
'Once the background worker is done updating the positions this function scrolls through the textboxes and assigns them their new positions.
' We have to do it in this event because we don't have access to the textboxes on the backgroun thread.
For Each r In Me.lstTextBoxes
r.Location = New System.Drawing.Point(r.Location.X, dtnPositions(CInt(r.Tag)))
Next
'use linq to find any textboxes whose position is beyond the threshhold that signifies they are down far enough.
' I chose the number 100 arbitrarily but it could really be anything.
Dim temp = From r In Me.lstTextBoxes Where r.Location.Y > (Me.Size.Height - 100)
'If we found any textboxes beyond our threshold then we set the top boolean
If temp IsNot Nothing AndAlso temp.Count > 0 Then
Me.blnStop = True
End If
'If we don't want to stop yet we fire off the background worker again and let the code go otherwise we set the stop boolean to false without firing the background worker
' so we will be all set to reset and go again if the user clicks those buttons.
If Not Me.blnStop Then
bgWorker.RunWorkerAsync()
Else
Me.blnStop = False
End If
Catch ex As Exception
MessageBox.Show("Error: " & ex.Message)
End Try
End Sub
Private Sub btnStop_Click(sender As Object, e As EventArgs) Handles btnStop.Click
Try
'The user clicked the stop button so we set the boolean and let the bgWorker_RunWorkerCompleted handle the rest.
Me.blnStop = True
Catch ex As Exception
MessageBox.Show("Error: " & ex.Message)
End Try
End Sub
End Class
Theres a lot of code there but a lot of it is comments and I tried to be as clear as possible so they are probably a little long winded. But you should be able to plop that code on a new form and it would work without any changes. I had the form size quite large (1166 x 633). So I think that's when it works best but any size should work(smaller forms will just be more cluttered).
Let me know if this doesn't work for your application.
This is a problem that is well suited to async/await. Await allows you to pause your code to handle other events for a specific period of time..
Private Async Function Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) As Task Handles Button1.Click
pgClass = New SecondUIClass
pgClass.x += 100
await pgClass.NewUIThread()
End Sub
End Class
Class Code:
Imports System.Threading
Public Class SecondUIClass
Public Const count As Integer = 1000
Public emeny(count - 1) As PictureBox
Public counter As Integer = 0
Public x As Integer = 0
Private Async Function NewUIThread() As Task
emeny(counter) = New PictureBox
emeny(counter).BackColor = Color.Red
emeny(counter).Visible = True
emeny(counter).Location = New System.Drawing.Point(x, 100)
emeny(counter).Size = New System.Drawing.Size(10, 50)
Form1.Controls.Add(emeny(counter))
For z = 0 To 13
emeny(counter).Location = New Point(emeny(counter).Location.X + 10, emeny(counter).Location.Y)
await Task.Delay(100) 'The await state machine pauses your code here in a similar way to application.doevents() until the sleep has completed.
Next
counter += 1
End Sub
End Class