In VB.Net I've created a background worker which seems to work. It is set up like this:
Private bw As BackgroundWorker = New BackgroundWorker
Public Sub construct_configure()
bw.WorkerReportsProgress = True
bw.WorkerSupportsCancellation = True
AddHandler bw.DoWork, AddressOf bw_DoWork
AddHandler bw.ProgressChanged, AddressOf bw_ProgressChanged
AddHandler bw.RunWorkerCompleted, AddressOf bw_RunWorkerCompleted
end sub
Then I have the doWork Sub set up like this:
Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
' create the picture box
pic_holder = New PictureBox()
' Show GIF and disable whatever you need to
With pic_holder
.Image = My.Resources.counter_gif
.Size = New Size(200, 100)
.Location = New Point(0, 0)
.Visible = True
.BackColor = Color.Red
End With
Main.Controls.Add(pic_holder)
pic_holder.BringToFront()
MessageBox.Show("worker started")
End Sub
The message box shows up when I click on my button to call the Sub, but the animated gif never does. I'm trying to simply set up an indicator that there is some background work going on. The gif shows up when I set it up in the parent sub.
Also, I'd like to place the gif in a panel that already exists, but every time I try to, I get a cross threading error that says the panel was created in another thread. How can I get around that?
Thanks.
It's recommended that if you want to invoke the main form you should do this in the ProgressChanged event. The bw_ProgressChanged is called on the same thread as the main form.
Private Sub bw_DoWork(sender As Object, e As DoWorkEventArgs) Handles bw.DoWork
'WORKER THREAD, do not invoke main form.
Dim worker As BackgroundWorker = CType(sender, BackgroundWorker)
'Report start
worker.ReportProgress(0)
'Do some work...
Threading.Thread.Sleep(5000)
'Report end.
worker.ReportProgress(100)
End Sub
Private Sub bw_ProgressChanged(sender As Object, e As ProgressChangedEventArgs) Handles bw.ProgressChanged
'MAIN THREAD, safe to invoke main form.
If (e.ProgressPercentage = 0) Then
'Started: Do something...
ElseIf (e.ProgressPercentage = 100) Then
'Stopped: Do something...
End If
End Sub
Related
I am creating some tabs and I need two things to work that I can't get to work. I need to AddHandler for a Textbox.Keypress event AND a Button.Click event. I can make these things work outside of the tabcontrol but not in.
In the example below my text box and buttons have same name from on tab to the another, I thought that might be my problem but even changing names between tabs does not work. I assume I need to be more specific in the AddHandler part to give the tab name as well as control. There is a logic in my real code to allow me to give unique names to each tab panel and controls, but i can't get the simple part to work.
I left some of the things I tried commented, but I tried LOTS and LOTS of other things.
Public Class Form1
Public Sub addTab(tabPageName As String)
Dim tabpage As New TabPage
tabpage.Text = tabPageName
tabpage.Name = "tabPage1" 'real code has logic to make sure names are unique
Dim label1 As New Label
Dim txtCreator As New TextBox
Dim combox1 As New ComboBox
Dim tabPageButton2 As New Button
tabPageButton2.Parent = tabpage
label1.Parent = tabpage
txtCreator.Parent = tabpage
combox1.Parent = tabpage
label1.Location = New Point(10, 10)
txtCreator.Location = New Point(150, 10)
combox1.Location = New Point(300, 10)
tabPageButton2.Location = New Point(20, 40)
label1.Text = "Creator"
txtCreator.Name = "txtCreator"
'fill the comboboxes...this will come from a database but testing now.
combox1.Items.Add("one")
combox1.Items.Add("two")
combox1.Items.Add("three") 'ok that works so should work from DB no problem.
tabRoleClass.TabPages.Add(tabpage)
End Sub
Private Sub Form1_Load(sender As Object, e As System.EventArgs) Handles Me.Load
addTab("First Tab")
AddHandler Controls("tabRoleClass.tabPage1.tabPageButton2").Click, AddressOf tabPageButton_click
'AddHandler CType(Controls("tabPageButton"), Button).Click, AddressOf tabPageButton_click
'AddHandler Controls("tabPageButton").Click, AddressOf tabPageButton_click
AddHandler CType(Controls("txtCreator"), TextBox).KeyPress, AddressOf txtcreator_keypress 'the Keypress to call lookup
End Sub
Private Sub tabPageButton_click(sender As System.Object, e As System.EventArgs) 'Handles tabPageButton.click
MessageBox.Show(tabRoleClass.SelectedTab.Name.ToString)
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
addTab("Second Tab")
tabRoleClass.SelectedIndex = tabRoleClass.TabCount - 1
'AddHandler Controls("tabRoleClass.tabPage1.tabPageButton2").Click, AddressOf tabPageButton_click
'AddHandler CType(Controls("tabPageButton"), Button).Click, AddressOf tabPageButton_click
'AddHandler Controls("tabPageButton").Click, AddressOf tabPageButton_click
'AddHandler CType(Controls("txtCreator"), TextBox).KeyPress, AddressOf txtcreator_keypress 'the Keypress to call lookup
End Sub
Private Sub txtcreator_keypress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) 'Handles txtCreator.KeyPress
MessageBox.Show("keypress worked on " & tabRoleClass.SelectedTab.Name.ToString)
End Sub
End Class
This is a very confusing question and your code could really do with some cleaning, but you need to add the AddHandler code to the addTab subroutine as pointed out by #Plutonix:
Public Sub addTab(tabPageName As String)
Dim tabpage As New TabPage
Dim tabPageButton As New Button
Dim txtCreator As New TextBox
/.../
AddHandler tabPageButton.Click, AddressOf tabPageButton_click
AddHandler txtCreator.KeyDown, AddressOf txtcreator_keypress
tabRoleClass.TabPages.Add(tabpage)
End Sub
Private Sub tabPageButton_click()
MessageBox.Show(tabRoleClass.SelectedTab.Name.ToString)
End Sub
Private Sub txtcreator_keypress()
MessageBox.Show("keypress worked on " & tabRoleClass.SelectedTab.Name.ToString)
End Sub
Private Sub Form1_Load(sender As Object, e As System.EventArgs) Handles Me.Load
addTab("First Tab")
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
addTab("Second Tab")
tabRoleClass.SelectedIndex = tabRoleClass.TabCount - 1
End Sub
AddHandler works by adding event handlers to your controls. This means that each time an event is raised during this runtime, the new event handler will handle the event; everytime you click your tabPageButton the associated event tabPageButton_click will handle it.
Therefore, you will only need to add the handler once, preferably upon the creation of the control. There is absolutely no need to create them upon every single keypress, for example. You should look up event handlers on MSDN.
Hope this helps!
Sorry if the code was confusing, I cut up my actual code to make a "sample" and I can see the confusion. Now of course I AM confused, I originally had the AddHandler INSIDE the addTab sub that creates the tab and it didn't work there, I incorrectly assumed the reason was that the control was not yet created so I moved it out. Moving it back in this sub this morning worked perfectly, I don't know what I did wrong but its working GREAT by moving it up to where it belongs, thanks A LOT, I worked on this for 2 days trying and googling things. Next time I will post real code instead of a sample to be less confusing and also remove my commented attemps (I thought those would help show what I was trying but I think it didn't)
I’m trying to use events to update a textbox from a background worker from a different class.
It is the same problem as mentioned in this SO post, except I'm using VB.NET. I'm trying to implement the 2nd suggested solution by #sa_ddam213.
I’m getting an error: “Cross-thread operation not valid: Control 'txtResult' accessed from a thread other than the thread it was created on.”
Here is my code:
Public Class DataProcessor
Public Delegate Sub ProgressUpdate(ByVal value As String)
Public Event OnProgressUpdate As ProgressUpdate
Private Sub PushStatusToUser(ByVal status As String)
RaiseEvent OnProgressUpdate(status)
End Sub
Public Sub ProcessAllFiles(ByVal userData As UserData)
'Do the work
End Sub
End Class
Public Class MainForm
Private bw As New BackgroundWorker
Private dp As New DataProcessor
Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
Dim worker As BackgroundWorker = CType(sender, BackgroundWorker)
If bw.CancellationPending = True Then
e.Cancel = True
Else
dp.ProcessAllFiles(CType(e.Argument, UserData))
End If
End Sub
Private Sub dp_OnProgressUpdate(ByVal status As String)
txtResult.Text = status
End Sub
Private Sub MainForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
bw.WorkerReportsProgress = True
bw.WorkerSupportsCancellation = True
AddHandler bw.DoWork, AddressOf bw_DoWork
AddHandler bw.ProgressChanged, AddressOf bw_ProgressChanged
AddHandler bw.RunWorkerCompleted, AddressOf bw_RunWorkerCompleted
AddHandler dp.OnProgressUpdate, AddressOf dp_OnProgressUpdate
End Sub
End Class
Thanks all!
The event still comes from a different thread than the UI. You need to delegate the woke back to the UI. I don't check InvokeRequired because I know it is from the worker thread.
Me is the form, Invoke asks for the delegate that will handle the work of bring the data to the UI thread. Here my Delegate Sub is a lambda Sub instead of using a normal Sub routine - simpler design.
Private Sub dp_OnProgressUpdate(ByVal status As String)
'invoke the UI thread to access the control
'this is a lambda sub
Me.Invoke(Sub
'safe to access the form or controls in here
txtResult.Text = status
End Sub)
End Sub
Maybe you could try doing something like this?
Private Delegate Sub del_Update(ByVal status As String)
Private Sub dp_OnProgressUpdate(ByVal status As String)
If txtResult.InvokeRequired Then
txtResult.Invoke(New del_Update(AddressOf dp_OnProgressUpdate), New Object() {status})
Else
target_textbox.text = status
End If
End Sub
can someone help me, how to create button/control like that in VB.NET .. that i mean, how to create/use style like that when i pointing the cursor or click the control, the style like transparent. that style different from button style.. please help me.. i have search everywhere but nothing. thank's before
Here is the image:
If you want to accomplish what you are looking for take a look at this example it might help you work out any issues you have:
Sub Main()
Dim myButton As Button = New Button()
myButton.Text = "Hello World"
myButton.BackColor = Color.Blue
myButton.FlatAppearance.BorderColor = Color.LightBlue
myButton.FlatAppearance.BorderSize = 2
AddHandler myButton.Click, AddressOf Me.OnMyButtonClick
AddHandler myButton.MouseHover, AddressOf Me.OnMyButtonEnter
AddHandler myButton.MouseLeave, AddressOf Me.OnMyButtonLeave
Panel1.Controls.Add(myButton)
End Sub
As you can see I have created a new button, and can modify all of the attributes of the button that you were looking for: Back color, Border color, and Border width, even the content like text or the image. Now you will want to create the events that will perform the actions that you are looking to accomplish:
Private Sub OnMyButtonClick(sender As Object, e As EventArgs)
Dim currentButton As Button = CType(sender, Button)
currentButton.Text = "I have been clicked!"
currentButton.BackColor = Color.LightBlue
currentButton.FlatAppearance.BorderColor = Color.Blue
currentButton.FlatAppearance.BorderSize = 1
End Sub
Private Sub OnMyButtonEnter(sender As Object, e As EventArgs)
Dim currentButton As Button = CType(sender, Button)
currentButton.BackColor = Color.LightGreen
End Sub
Private Sub OnMyButtonLeave(sender As Object, e As EventArgs)
Dim currentButton As Button = CType(sender, Button)
currentButton.BackColor = Color.Blue
End Sub
As you can see we have defined the 3 events you were looking to capture Mouse Click, Mouse Hover, and Mouse Leave, and within each one we can modify the button as we please. From here you can make a global parameter that monitors the color of the button, or you can monitor the state, from there you can know within each event what you want to do. Take for instance the Mouse Leave event.
You showed that you want to create a border, but have a white background as you can see, all you would have to do is just:
myButton.BackColor = Color.White
myButton.FlatAppearance.BorderColor = Color.LightBlue
That's easy enough, but there is one very important part that you need to know. If you want your button to actually perform events, ones that you created, you have to attach them to the button. Lets that a look at this:
AddHandler myButton.Click, AddressOf Me.OnMyButtonClick
AddHandler myButton.MouseHover, AddressOf Me.OnMyButtonEnter
AddHandler myButton.MouseLeave, AddressOf Me.OnMyButtonLeave
As you can see from the above code you instruct the program what to do when a certain event occurs such as Mouse Click. An important part to do with event handlers is to attach them after creation so that they will perform the actions right away. That is why I placed the attachment of the event handler in the Main() before I added the button to Panel1.
Let's try with this:
Import your image to resource.
You will need 3 different image: Normal, Hover and Clicked. Change this to your image name: CustomButtonN, CustomButtonH and CustomButtonC.
Add a Class with any name. Then add the code below:
Inherits Windows.Forms.Button 'To make this class as button command
Public Sub New()
Me.FlatStyle = Windows.Forms.FlatStyle.Flat 'Make it flat
Me.FlatAppearance.BorderSize = 0 'With borderless
Me.FlatAppearance.MouseDownBackColor = Color.Transparent 'You know this
Me.FlatAppearance.MouseOverBackColor = Color.Transparent 'And this
Me.BackColor = Color.Transparent 'Transparent key
Me.ForeColor = Color.Black 'Text color
Me.Width = 32 'Button width
Me.Height = 32 'Button height
Me.BackgroundImage = My.Resources.CustomButtonN 'Normal
Me.BackgroundImageLayout = ImageLayout.Stretch 'To stretch the image
End Sub
Private Sub CustomButton_MouseDown(ByVal sender As Object, _
ByVal e As MouseEventArgs) Handles Me.MouseDown
Me.BackgroundImage = My.Resources.CustomButtonC 'Click event
End Sub
Private Sub CustomButton_MouseHover(ByVal sender As Object, _
ByVal e As EventArgs) Handles Me.MouseHover
Me.BackgroundImage = My.Resources.MiniButtonH 'Hover event
End Sub
Private Sub CustomButton_MouseLeave(ByVal sender As Object, _
ByVal e As EventArgs) Handles Me.MouseLeave
Me.BackgroundImage = My.Resources.CustomButtonN 'Leave even
End Sub
Private Sub CustomButton_MouseUp(ByVal sender As Object, _
ByVal e As MouseEventArgs) Handles Me.MouseUp
Me.BackgroundImage = My.Resources.CustomButtonH 'Click release
End Sub
Note: You must compile the project after adding a class or editing:
Press F5 to start it and then wait for 3 sec, then stop it.
Finally see your Toolbox panel click your button name then put to your form.
Im writing an app that takes and copy's folders (selected by the user in a listbox) to a specified location, it has a progress bar that should run the whole time but it seems that the backgroundworker isnt catching the process.. it just freezes the UI until the copy is complete.. when I put in a message between copy's the message works so I know it is capable of working.. im missing something..
Private Sub Button4_Click_1(sender As Object, e As EventArgs) Handles Btn_SaveApps.Click
bw.WorkerSupportsCancellation = True
bw.WorkerReportsProgress = True
AddHandler bw.DoWork, AddressOf bw_DoWork
AddHandler bw.ProgressChanged, AddressOf bw_ProgressChanged
AddHandler bw.RunWorkerCompleted, AddressOf bw_RunWorkerCompleted
If Not bw.IsBusy = True Then
bw.RunWorkerAsync(Module1.SaveApp)
End If
End Sub
Module Module1
Function SaveApp() As Process
Dim WinStrApps As String = ("C:\TransferFrom")
Form1.tbProgress.Style = ProgressBarStyle.Marquee
For Each Item In Form1.Selected_Apps.SelectedItems
On Error Resume Next
Form1.tbProgress.Visible = True
Dim FileLoc = ("C:\TransferTo\")
If System.IO.Directory.Exists(WinStrApps + Item) = True Then
Directory.CreateDirectory(FileLoc + Item)
My.Computer.FileSystem.CopyDirectory(WinStrApps + Item, FileLoc + Item)
Else
MsgBox(Item + " does not exist, or is a system App. Please choose another application")
End If
Next
MsgBox("Completed saving the applications")
End Function
I think you may have called the thread wrong, the thread has to begin in the sub bw_DoWork
Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
Module1.SaveApp
end sub
and any updates passed back to the UI thread need to be passed to
Private Sub bw_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
Form1.tbProgress.value = e.ProgressPercentage
End Sub
however you wont be able to access the update routine from your sub as the routine is private of Form1... you may have to create a delegate in your module.
I am having a little issue, I finally figured out how to add a background worker to my application now my only problem is it does not end the thread, or atleast not fast enough when I am clicking my cancel button. I must be doing something wrong. I would like for it to abort the thread as soon as the button is clicked. Is this feasable? My thread is by no means extensive.
I am going to post some examples of the way I am doing it.
Public Sub New()
InitializeComponent()
bw.WorkerReportsProgress = True
bw.WorkerSupportsCancellation = True
AddHandler bw.DoWork, AddressOf bw_DoWork
' AddHandler bw.ProgressChanged, AddressOf bw_ProgressChanged
AddHandler bw.RunWorkerCompleted, AddressOf bw_RunWorkerCompleted
End Sub
My DoWork Event
Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
If bw.CancellationPending = True Then
e.Cancel = True
WorkEventRunning = False
Else
CheckForIllegalCrossThreadCalls = False
Dim worker As BackgroundWorker = CType(sender, BackgroundWorker)
'my long winded event
' more of my long winded event.
End if
My Cancel button Code
Private Sub ToolStripButton2_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ToolStripButton2.Click
'Stop
If bw.WorkerSupportsCancellation = True Then
WorkEventRunning = False
bw.CancelAsync()
bw.Dispose()
End If
And my WorkCompleted
Private Sub bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
If e.Cancelled = True Then
'canceled
WorkEventRunning = False
ElseIf e.Error IsNot Nothing Then
MsgBox(e.Error.Message)
Else
'done
End If
End Sub
In the DoWork event, test for CancellationPending inside the long winded event.
Supposing that this long procedure contains a For-Loop or a ForEach then at every loop test for CancellationPending
For example:
Dim worker As BackgroundWorker = CType(sender, BackgroundWorker)
Dim x as Integer
For x = 0 to 1000000
If worker.CancellationPending = True Then
e.Cancel = True
Return
Else
.... ' Execute the works for this loop
End If
Next
The same test could be done inside the RunWorkerCompleted event because CancelAsync() sets the internal value for the CancellationPending property. See this question to look at the inner workings of CancelAsync()