Form.Closing Event - wpf

I am working with a way to close the application by asking the following question but it fails to work.
Any help is awesome!
Private Sub MainWindow_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
Dim result As Integer
result = MessageBox.Show("Are you want to close", "Exit",
MessageBoxButton.YesNo, MessageBoxImage.None)
If (result = DialogResult.No) Then
e.Cancel = True
Else
Application.Exit()
End If
End Sub

OK, this question already has an accepted answer but I thought I'd just add another, a one-liner, as it appears the OP is only doing the MessageBox check. I don't feel there's any need for a boolean or an Application.Exit() Environment.Exit(0) in a Closing event.
Private Sub MainWindow_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
If Not MessageBox.Show("Are you sure you want to close?", "Exit", MessageBoxButton.YesNo, MessageBoxImage.None) = MessageBoxResult.Yes Then e.Cancel = True
End Sub

This should work:
Private _handle As Boolean = True
Private Sub MainWindow_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
If _handle = True Then
e.Cancel = True
Dim result = MessageBox.Show("Are you want to close?", "Exit", MessageBoxButton.YesNo, MessageBoxImage.None)
If (result = MessageBoxResult.Yes) Then
_handle = False
Environment.Exit(0)
End If
End If
End Sub

Related

Elapsed Time Event Handler in WPF VB.NET

I'm implementing a timer into my program that starts when a button is pressed. Once the time has elapsed, my intention is to close the program; however, I'm getting an invalid operation exception. I have a different part of my program also using the Close() operation, but even if I remove that from the program, it'll throw the error anyway. How can I make it work properly?
Public Class Password
Public timer1 As System.Timers.Timer = New System.Timers.Timer()
Public loginAttempts As Integer = 0
Private myPassword As String = "MPCgso4char"
Private Sub AcceptButton_Click(sender As Object, e As RoutedEventArgs)
wrongPass.Content = "Incorrect password. Please try again."
wrongPass.Visibility = Visibility.Hidden
Select Case passTextBox.Text
Case = myPassword
Close()
Case = ""
wrongPass.Content = "Please enter a valid password."
wrongPass.Visibility = Visibility.Visible
Case <> myPassword
wrongPass.Visibility = Visibility.Visible
End Select
End Sub
Private Sub DeclineButton_Click(sender As Object, e As RoutedEventArgs)
passTitle.Visibility = Visibility.Hidden
AcceptButton.Visibility = Visibility.Hidden
DeclineButton.Visibility = Visibility.Hidden
showPass.Visibility = Visibility.Hidden
passTextBox.Visibility = Visibility.Hidden
If wrongPass.Visibility = Visibility.Visible Then
wrongPass.Visibility = Visibility.Hidden
End If
decBtnClickEvent.Visibility = Visibility.Visible
timer1.Interval = 1500
timer1.Enabled = True
timer1.Start()
AddHandler timer1.Elapsed, AddressOf TimedEvent
End Sub
Private Sub passTextBox_TextChanged(sender As Object, e As TextChangedEventArgs)
wrongPass.Visibility = Visibility.Hidden
End Sub
Private Sub showPass_Checked(sender As Object, e As RoutedEventArgs)
End Sub
Private Sub TimedEvent(sender As Object, e As System.Timers.ElapsedEventArgs)
Close()
End Sub
Private Sub Enter(sender As Object, e As KeyEventArgs) Handles passTextBox.KeyDown
If e.Key = Key.Enter Then
AcceptButton_Click(Nothing, Nothing)
Else
Exit Sub
End If
End Sub
End Class
Try use Application.Current.Shutdown() instead of Close()
Private Sub TimedEvent(sender As Object, e As System.Timers.ElapsedEventArgs)
Application.Current.Shutdown()
End Sub
In addition be sure that your Public Class Password is't in another thread.
In this case you can use:
Me.Dispatcher.Invoke(Sub() Application.Current.Shutdown() End Sub)

Quiz using Visual Studio

I want to make a little quiz about me using visual studio. My goal is to have 10 questions and 4 choices each. I've tried to stack buttons and then change 10 labels at different times and it's getting a little complicated... using arrays or/and loops how could i make this more efficient? This is what I have
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.WindowState = FormWindowState.Maximized
Form1.Visible = False
Button2.Visible = False
Button3.Visible = False
Button4.Visible = False
Button5.Visible = False
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
If RadioButton4.Checked = True Then
Label3.Text = "1/10"
Label4.Text = "Nice! That was Correct!
Great job!"
Label4.ForeColor = Color.LimeGreen
End If
If Label3.Text = "1/10" Then
Button2.Visible = True
End If
If RadioButton1.Checked = True Or RadioButton2.Checked = True Or RadioButton3.Checked = True Then
Label4.ForeColor = Color.DarkRed
Label4.Text = "Oh no! that was incorrect!
Try Again!"
End If
Button2.Text = "next"
End Sub
Private Sub Button2_click(sender As Object, e As EventArgs) Handles Button2.Click
Label1.Text = "What is Luke Lopez's favorite color?"
RadioButton4.Checked = False
RadioButton1.Text = "Purple"
RadioButton2.Text = "Burgundy"
RadioButton3.Text = "Turqoise"
RadioButton4.Text = "Brown"
If RadioButton1.Checked = True Then
Label3.Text = "2/10"
Label4.Text = "Nice! That was Correct!
Great job!"
Label4.ForeColor = Color.LimeGreen
End If
If Label3.Text = "2/10" Then
Button4.Visible = True
End If
If RadioButton3.Checked = True Or RadioButton2.Checked = True Or RadioButton4.Checked = True Then
Label4.ForeColor = Color.DarkRed
Label4.Text = "Oh no! that was incorrect!
Try Again!"
End If
Button2.Text = "check"
Button4.Text = "next"
If Label3.Text = "2/10" Then
Button3.Text = "next"
Button4.Text = "Check"
End If
End Sub
Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
Button3.Visible = False
Label1.Text = "What is Luke Lopez's shoe size?"
RadioButton1.Checked = False
RadioButton1.Text = "7"
RadioButton2.Text = "8.5"
RadioButton3.Text = "7.5"
RadioButton4.Text = "8"
Button3.Visible = False
If RadioButton2.Checked = True Then
Label3.Text = "3/10"
Label4.Text = "Nice! That was Correct!
Great job!"
Label4.ForeColor = Color.LimeGreen
End If
If Label3.Text = "3/10" Then
Button5.Visible = True
Button4.Visible = False
Button3.Visible = False
Button2.Visible = False
Button1.Visible = False
End If
If RadioButton1.Checked = True Or RadioButton4.Checked = True Or RadioButton3.Checked = True Then
Label4.ForeColor = Color.DarkRed
Label4.Text = "Oh no! that was incorrect!
Try Again!"
End If
End Sub
Private Sub Button5_Click(sender As Object, e As EventArgs) Handles Button5.Click
End Sub
It's my first three questions and there are all the buttons and labels and radiobuttons checking and changing every click but I need this to be more efficient because I don't want to waste a lot of time and computer memory on this mini project.
In comments I talked about storing your data in a text file. This is what my text file looks like. Each record is on a single line and each field is separated by a pipe character |. I chose a pipe because you won't have any of that character in the text of your questions and answers.
1|Largest planet?|Jupiter|Venus|Saturn|Mars|1
2|Smallest planet?|Earth|Uranus|Mercury|Neptune|3
3|Comes closest to Earth?|Mars|Venus|Mercury|Jupiter|2
4|What planet is called the red planet?|Venus|Mars|Saturn|Neptune|2
5|What planet is furthest from Earth?|Saturn|Jupiter|Uranus|Neptune|4
6|How many planets in our solar system?|7|8|9|10|2
7|How many planets between Earth and Sun?|1|2|3|4|2
8|Which planet is said to have rings?|Jupiter|Uranus|Saturn|Neptune|3
You have been working with classes all along because a Form is a class. Classes can have Properties, methods and Events among other things. Now to the class, QuestionAnswer. The properties are pretty straight forward. The Sub New creates a new instance of the class and uses parameters to set all the properties of the class.
In the Form.Load FillQuestionList is called. This method uses the File class in System.IO (need to add an Imports for this). ReadAllLines returns an array of the all the lines in the file. We can loop through these lines and then split each line into fields. The little c after the "|" tells compiler that you intend a Char not a String. Now we can call the Sub New of QuestionAnswer providing all the properties from the parts of the line. This new instance is then added to the QuestionList so we can use it in the program.
Next, the Load event calls DisplayQuestion. This method will also be called from the btnNext click event. We get the index we want to display from the form level variable CurrentQuestionIndex. See how handy the class is to set the values we need. Loop through all the radio buttons clearing the Tag from the last question and clearing the check. The file provided the number of the correct answer. This is stored in the radio button's Tag property.
When the user clicks btnAnswer we get which radio button was selected and check the Tag property to see if the answer was correct. If yes, increment Score and display the score.
The btnNext.Click simply checks if we have reached the end of the list before incrementing the index and displaying the next question.
Public Class QuestionGame
Private QuestionList As New List(Of QuestionAnswer)
Private CurrentQuestionIndex As Integer
Private Score As Integer
Private Sub QuestionGame_Load(sender As Object, e As System.EventArgs) Handles MyBase.Load
FillQuestionList()
DisplayQuestion()
End Sub
Private Sub DisplayQuestion()
Dim QA = QuestionList(CurrentQuestionIndex)
lblQuestion.Text = QA.Question
RadioButton1.Text = QA.Answer1
RadioButton2.Text = QA.Answer2
RadioButton3.Text = QA.Answer3
RadioButton4.Text = QA.Answer4
For Each rb In Controls.OfType(Of RadioButton)
rb.Tag = ""
rb.Checked = False
Next
Select Case QA.Correct
Case 1
RadioButton1.Tag = "Correct"
Case 2
RadioButton2.Tag = "Correct"
Case 3
RadioButton3.Tag = "Correct"
Case 4
RadioButton4.Tag = "Correct"
End Select
End Sub
Private Sub FillQuestionList()
Dim lines = File.ReadAllLines("C:\Users\xxxxxxxx\Questions.txt")
For Each line In lines
Dim parts = line.Split("|"c)
QuestionList.Add(New QuestionAnswer(CInt(parts(0)), parts(1), parts(2), parts(3), parts(4), parts(5), CInt(parts(6))))
Next
End Sub
Private Sub btnAnswer_Click(sender As Object, e As EventArgs) Handles btnAnswer.Click
Dim rb = Controls.OfType(Of RadioButton)().FirstOrDefault(Function(r) r.Checked = True)
If rb IsNot Nothing AndAlso rb.Tag.ToString = "Correct" Then
Score += 1
lblScore.Text = Score.ToString
MessageBox.Show("Correct!")
btnAnswer.Enabled = False 'The user can't increase his score by clicking answer several times
Else
MessageBox.Show("Sorry, wrong answer")
End If
btnNext.Enabled = True
End Sub
Private Sub btnNext_Click(sender As Object, e As EventArgs) Handles btnNext.Click
btnNext.Enabled = False 'user can't move to next question until he answers current question
If CurrentQuestionIndex = QuestionList.Count - 1 Then
MessageBox.Show("This is the last question")
Else
CurrentQuestionIndex += 1
DisplayQuestion()
End If
End Sub
End Class
Public Class QuestionAnswer
Public Property QuestionNumber As Integer
Public Property Question As String
Public Property Answer1 As String
Public Property Answer2 As String
Public Property Answer3 As String
Public Property Answer4 As String
Public Property Correct As Integer
Public Sub New(Num As Integer, Ques As String, Ans1 As String, Ans2 As String, Ans3 As String, Ans4 As String, Cor As Integer)
QuestionNumber = Num
Question = Ques
Answer1 = Ans1
Answer2 = Ans2
Answer3 = Ans3
Answer4 = Ans4
Correct = Cor
End Sub
End Class
You can save questions, options and answers to a database and then read them to a DataTable, every time you want to show the question, just use a DataRow from the DataTable. That means you don't need to edit questions like:
Use 'Label.TextChanged' event to handle 'Label3.Text':
Private Sub Label3_TextChanged(sender As Object, e As EventArgs) Handles Label3.TextChanged
Select Case Label3.Text
Case "1/10"
Button2.Visible = True
Case "2/10"
Button4.Visible = True
Button3.Text = "next"
Button4.Text = "Check"
Case "3/10"
...
...
End Select
End Sub
Use 'If ... Else ...' statement. For Example:
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
'...
If RadioButton1.Checked = True Then
Label3.Text = "2/10"
Label4.Text = "Nice! That was Correct! Great job!"
Label4.ForeColor = Color.LimeGreen
Else
Label4.ForeColor = Color.DarkRed
Label4.Text = "Oh no! that was incorrect! Try Again!"
End If
'If RadioButton3.Checked = True Or RadioButton2.Checked = True Or RadioButton4.Checked = True Then
' Label4.ForeColor = Color.DarkRed
' Label4.Text = "Oh no! that was incorrect! Try Again!"
'End If
'...
End Sub

How to update selected row in datagridview through textbox located on another form?

Hi guys i've been working so far with my system and it's almost done, but there is one thing i can't solve yet(not unless if you help me out).
so here's my code:
Private Sub btnEditmain_Click(sender As Object, e As EventArgs) Handles btnEditmain.Click
Try
editdgv()
Form2.Show()
DataGridView2.AllowUserToAddRows = True
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
here's the private sub editdgv
Private Sub editdgv()
Dim i = DataGridView2.CurrentRow.Index
With DataGridView2
Form2.txtPeriod.Text = IIf(IsDBNull(.Rows(i).Cells("period").Value), " ", .Rows(i).Cells("period").Value)
Form2.txtVouch.Text = IIf(IsDBNull(.Rows(i).Cells("vouch_amt").Value), " ", .Rows(i).Cells("vouch_amt").Value)
Form2.txtIndivAmt.Text = IIf(IsDBNull(.Rows(i).Cells("individual_amt").Value), " ", .Rows(i).Cells("individual_amt").Value)
Form2.txtCheckno.Text = IIf(IsDBNull(.Rows(i).Cells("check_no").Value), " ", .Rows(i).Cells("check_no").Value)
Form2.txtDmailed.Text = IIf(IsDBNull(.Rows(i).Cells("D_MAILED").Value), " ", .Rows(i).Cells("D_MAILED").Value)
Form2.txtDirno.Text = IIf(IsDBNull(.Rows(i).Cells("DIR_NO").Value), " ", .Rows(i).Cells("DIR_NO").Value)
Form2.txtYrlvl.Text = IIf(IsDBNull(.Rows(i).Cells("year_student").Value), " ", .Rows(i).Cells("year_student").Value)
Form2.txtUpdatedBy.Text = IIf(IsDBNull(.Rows(i).Cells("who_updated").Value), " ", .Rows(i).Cells("who_updated").Value)
End With
End Sub
i was able to pass the value of the selected rows in datagridview to another form(textbox)
all things are working fine from there..
Now what i wanna do is to update that selected datagridview which is now in my textbox(s).
through a button click.
now i'm stuck at saving changes that i've done in the textbox, so it will be updated on the database.
pls help me on how to update the selected row in datagridview in textbox via button click.
thanks!
Public Class Form2
Private Sub btnSave_Click(sender As Object, e As EventArgs) Handles btnSave.Click
End Sub
End Class
I have created this code in notepad. So, it might have some issue.
FORM 1
Private Sub btnEditmain_Click(sender As Object, e As EventArgs) Handles btnEditmain.Click
Try
Dim dt As DataTable = TryCast(DataGridView2.DataSource, DataTable)
Dim dr As DataRow = dt.Rows(DataGridView2.CurrentRow.Index)
Dim frm As New Form2
frm.dr = dr
frm.Show()
DataGridView2.AllowUserToAddRows = True
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
FORM 2
Public dr As DataRow = Nothing
Private Sub Form2_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
With dr
txtPeriod.Text = IIf(IsDBNull(.Item("period")), vbNullString, .Item("period").ToString)
txtVouch.Text = IIf(IsDBNull(.Item("vouch_amt")), vbNullString, .Item("vouch_amt").ToString)
txtIndivAmt.Text = IIf(IsDBNull(.Item("individual_amt")), vbNullString, .Item("individual_amt").ToString)
txtCheckno.Text = IIf(IsDBNull(.Item("check_no")), vbNullString, .Item("check_no").ToString)
txtDmailed.Text = IIf(IsDBNull(.Item("D_MAILED")), vbNullString, .Item("D_MAILED").ToString)
txtDirno.Text = IIf(IsDBNull(.Item("DIR_NO")), vbNullString, .Item("DIR_NO").ToString)
txtYrlvl.Text = IIf(IsDBNull(.Item("year_student")), vbNullString, .Item("year_student").ToString)
txtUpdatedBy.Text = IIf(IsDBNull(.Item("who_updated")), vbNullString, .Item("who_updated").ToString)
End With
End Sub
Private Sub btnSave_Click(sender As Object, e As EventArgs) Handles btnSave.Click
With dr
.Item("period") = txtPeriod.Text
.Item("vouch_amt") = txtVouch.Text
.Item("individual_amt") = txtIndivAmt.Text
.Item("check_no") = txtCheckno.Text
.Item("D_MAILED") = txtDmailed.Text
.Item("DIR_NO") = txtDirno.Text
.Item("year_student") = txtYrlvl.Text
.Item("who_updated") = txtUpdatedBy.Text
End With
dr.Table.AcceptChanges
End Sub

Overload resolution failed because no accessible 'new' can be called without a narrowing conversion

I have a problem.
I am getting this error:
Overload resolution failed because no accessible 'new' can be called
without a narrowing conversion.
Private Sub bt_hapus_Click(sender As Object, e As EventArgs) Handles bt_hapus.Click
Try
Dim sqlda As New SqlClient.SqlDataAdapter("Delete from tblpasien where No_Rkm_Mds=" & Me.No_Rkm_MdsTextBox.Text, Me.KlinikGigiDataSet)
sqlda.Fill(dbpasien, "tblpasien")
MsgBox("Data telah berhasil dihapus")
bersih()
pasif()
normal()
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
All source code :
Public Class frm_pasien
Dim dbpasien As New DataSet
Dim dvpasien As New DataView
Dim tekan As Integer
Dim cari As Integer
Private Sub TblpasienBindingNavigatorSaveItem_Click(sender As Object, e As EventArgs) Handles TblpasienBindingNavigatorSaveItem.Click
Me.Validate()
Me.TblpasienBindingSource.EndEdit()
Me.TableAdapterManager.UpdateAll(Me.KlinikGigiDataSet)
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'TODO: This line of code loads data into the 'KlinikGigiDataSet.tblpembayaran' table. You can move, or remove it, as needed.
Me.TblpembayaranTableAdapter.Fill(Me.KlinikGigiDataSet.tblpembayaran)
'TODO: This line of code loads data into the 'KlinikGigiDataSet.tblpasien' table. You can move, or remove it, as needed.
Me.TblpasienTableAdapter.Fill(Me.KlinikGigiDataSet.tblpasien)
End Sub
Private Sub BindingNavigatorDeleteItem_Click(sender As Object, e As EventArgs) Handles BindingNavigatorDeleteItem.Click
End Sub
Private Sub BindingNavigatorAddNewItem_Click(sender As Object, e As EventArgs) Handles BindingNavigatorAddNewItem.Click
End Sub
Private Sub bt_keluar_Click(sender As Object, e As EventArgs) Handles bt_keluar.Click
Dim pesan As DialogResult = MsgBox("Apakah anda yakin akan keluar", MsgBoxStyle.OkCancel)
If pesan = DialogResult.OK Then
Me.Close()
Else
Exit Sub
End If
End Sub
Private Sub bt_hapus_Click(sender As Object, e As EventArgs) Handles bt_hapus.Click
Try
Dim sqlda As New SqlClient.SqlDataAdapter("Delete from tblpasien where No_Rkm_Mds=" & Me.No_Rkm_MdsTextBox.Text, Me.KlinikGigiDataSet)
sqlda.Fill(dbpasien, "tblpasien")
MsgBox("Data telah berhasil dihapus")
bersih()
pasif()
normal()
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
Private Sub bersih()
Me.No_Rkm_MdsTextBox.Clear()
Me.NamaTextBox.Clear()
Me.UmurTextBox.Clear()
Me.Tgl_LhrTextBox.Clear()
Me.AlamatTextBox.Clear()
Me.No_HpTextBox.Clear()
Me.No_Rkm_MdsTextBox.Focus()
End Sub
Private Sub pasif()
Me.No_Rkm_MdsTextBox.Enabled = False
Me.NamaTextBox.Enabled = False
Me.UmurTextBox.Enabled = False
Me.Tgl_LhrTextBox.Enabled = False
Me.AlamatTextBox.Enabled = False
Me.No_HpTextBox.Enabled = False
End Sub
Private Sub normal()
Me.bt_tambah.Enabled = True
Me.bt_edit.Enabled = True
Me.bt_simpan.Enabled = False
Me.bt_reset.Enabled = False
Me.bt_hapus.Enabled = False
Me.bt_keluar.Enabled = True
End Sub
Private Sub binding()
Me.No_Rkm_MdsTextBox.DataBindings.Clear()
Me.No_Rkm_MdsTextBox.DataBindings.Add("Text", dvpasien, "Id")
Me.NamaTextBox.DataBindings.Clear()
Me.NamaTextBox.DataBindings.Add("Text", dvpasien, "Nama")
Me.UmurTextBox.DataBindings.Clear()
Me.UmurTextBox.DataBindings.Add("Text", dvpasien, "Alamat")
Me.Tgl_LhrTextBox.DataBindings.Clear()
Me.Tgl_LhrTextBox.DataBindings.Add("Text", dvpasien, "Ttl")
Me.AlamatTextBox.DataBindings.Clear()
Me.AlamatTextBox.DataBindings.Add("value", dvpasien, "Jkl")
Me.No_HpTextBox.DataBindings.Clear()
Me.No_HpTextBox.DataBindings.Add("Text", dvpasien, "Pekerjaan")
End Sub
Private Sub No_Rkm_MdsTextBox_TextChanged(sender As Object, e As EventArgs) Handles No_Rkm_MdsTextBox.TextChanged
If Len(Me.No_Rkm_MdsTextBox.Text) < 10 Then
Exit Sub
End If
dvpasien.Sort = "Id"
Try
cari = dvpasien.Find(Me.No_Rkm_MdsTextBox.Text)
If cari = -1 Then
If tekan = 1 Then
Me.No_Rkm_MdsTextBox.Focus()
Else
MsgBox("Data tidak ada")
bersih()
End If
Else
If tekan = 1 Then
MsgBox("Data sudah ada")
bersih()
Else
binding()
tampilgrid()
Me.bt_edit.PerformClick()
End If
End If
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
Private Sub tampilgrid()
Throw New NotImplementedException
End Sub
End Class
Please help this is my essay from my lecturer.
Have you declared your dataset with New keyword?
Means something as follows:
Dim dbpasien As New DataSet()
Or
Using dbpasien As DataSet = New DataSet()
MSDN explains this exception is due to bad overloaded method types
You have made a call to an overloaded method, but the compiler cannot find a method that can be called without a narrowing conversion. A narrowing conversion changes a value to a data type that might not be able to precisely hold some of the possible values.

How to cancel BackgroundWorker in WPF without DoEvents

I have a search box that works great in WinForms, but is giving me trouble in WPF.
It works by starting a search each time a letter is pushed, similar to Google.
If (txtQuickSearch.Text.Length >= 3) Or (e.Key = Key.Enter) Then
If SearchWorker.IsBusy Then
SearchWorker.CancelAsync()
Do While SearchWorker.IsBusy
'Application.DoEvents()
'System.Threading.Thread.Sleep(500)
Loop
End If
doSearchText = txtQuickSearch.Text
SearchWorker.RunWorkerAsync()
End If
Every time a key is pushed it cancels the current searchWorker then restarts it. In WinForms the Do while searchworker.isbusy doevents loop worked great, but since I don't have access to that anymore, I need to figure out a better way to do it. Sleep() deadlocks it, and I've tried just doing i+=1 as a way to pass time until it's not busy, but that doesn't work either...
What should I do?
Update: Here's what I changed it to. It works, but the cancel part doesn't seem to ever trigger, this doesn't seem to be running async...
Imports System.ComponentModel
Imports System.Collections.ObjectModel
Imports System.Threading
Imports System.Threading.Tasks
Public Class QuickSearch
Private doSearchText As String
Private canceled As Boolean
Private curSearch As String
Dim searchResults As New ObservableCollection(Of ocSearchResults)
'Task Factory
Private cts As CancellationTokenSource
Private searchtask As Task(Of ObservableCollection(Of ocSearchResults))
Private Sub txtQuickSearch_KeyDown(ByVal sender As System.Object, ByVal e As System.Windows.Input.KeyEventArgs) Handles txtQuickSearch.KeyDown
If e.Key = Key.Enter Then
curSearch = ""
End If
If ((txtQuickSearch.Text.Length >= 3) Or (e.Key = Key.Enter)) And Not txtQuickSearch.Text = curSearch Then
If Not cts Is Nothing Then
cts.Cancel()
ColorChecker.CancelAsync()
Try
' searchtask.Wait()
Catch ex As AggregateException
MsgBox(ex.InnerException.Message)
End Try
cts = New CancellationTokenSource
Else
cts = New CancellationTokenSource
End If
Dim cToken = cts.Token
Me.Height = 400
doSearchText = txtQuickSearch.Text
'This always completes fully before continuing on to tRunWorkerComplete(searchtask.Result) '
searchtask = Task(Of ObservableCollection(Of ocSearchResults)).Factory.StartNew(Function() tPerformSearch(cToken), cToken)
Try
tRunWorkerCompleted(searchtask.Result)
Catch ex As AggregateException
' MsgBox(ex.InnerException.Message)
End Try
Else
If Not cts Is Nothing Then
cts.Cancel()
End If
searchResults.Clear()
End If
End Sub
Function tPerformSearch(ByVal ct As CancellationToken) As ObservableCollection(Of ocSearchResults)
On Error GoTo sError
canceled = False
If curSearch = doSearchText Then
canceled = True
Return Nothing
End If
curSearch = doSearchText
Dim SR As New ObservableCollection(Of ocSearchResults)
Dim t As ocSearchResults
Dim rSelect As New ADODB.Recordset
Dim sSql As String = "SELECT DISTINCT CustomerID, CustomerName, City, State, Zip FROM qrySearchFieldsQuick WHERE "
Dim sWhere As String = "CustomerName Like '" & doSearchText & "%'"
SR.Clear()
With rSelect
.Open(sSql & sWhere & " ORDER BY CustomerName", MyCn, ADODB.CursorTypeEnum.adOpenStatic, ADODB.LockTypeEnum.adLockReadOnly)
Do While Not .EOF
If ct.IsCancellationRequested Then ' This never shows true, the process always returns a value, as if it wasn't async'
canceled = True
Return Nothing
End If
Do While IsDBNull(.Fields("CustomerID").Value)
.MoveNext()
Loop
t = New ocSearchResults(.Fields!CustomerID.Value, .Fields!CustomerName.Value.ToString.Trim, .Fields!City.Value.ToString.Trim, .Fields!State.Value.ToString.Trim, .Fields!Zip.Value.ToString.Trim)
If Not SR.Contains(t) Then
SR.Add(t)
End If
aMoveNext:
.MoveNext()
Loop
.Close()
End With
Return SR
Exit Function
sError:
MsgBox(ErrorToString, MsgBoxStyle.Exclamation)
End Function
Sub tRunWorkerCompleted(ByVal SR As ObservableCollection(Of ocSearchResults))
If canceled Then
Exit Sub
End If
If cts.IsCancellationRequested Then
Exit Sub
End If
searchResults.Clear()
For Each t As ocSearchResults In SR
searchResults.Add(t)
Next
ColorChecker = New BackgroundWorker
ColorChecker.WorkerReportsProgress = True
ColorChecker.WorkerSupportsCancellation = True
ColorChecker.RunWorkerAsync(searchResults)
lblRecordCount.Text = "(" & searchResults.Count & ") Records"
progBar.Value = 100
Exit Sub
sError:
MsgBox(ErrorToString)
End Sub
I don't know enough VB to give you any well written sample code, but if you're on .Net 4.0 I suggest switching to the System.Threading.Tasks namespace, which has cancellation abilities.
If (txtQuickSearch.Text.Length >= 3) Or (e.Key = Key.Enter) Then
If TokenSource Is Not Nothing Then
TokenSource.Cancel()
TokenSource = New CancellationTokenSource()
End If
Task.Factory.StartNew(SomeSearchMethod, txtQuickSearch.Text, TokenSource.Token)
End If
I am not sure a BackgroundWorker is flexible enough to provide an elegant solution for this type of background processing anyway. I think what I would do is to create a single dedicated thread for doing the searching. This thread would use the producer-consumer pattern for accepting work items and processing them.
The following code is a rough sketch of how I see this strategy working. You would call the SearchAsync method to request a new search. That method accepts a callback that gets invoked when and if the search operation found something. Notice that the consumer code (in the Run method) cancels its current search operation if another search request is queued. The effect is that the consumer only ever processes the latest request.
Public Class Searcher
Private m_Queue As BlockingCollection(Of WorkItem) = New BlockingCollection(Of WorkItem)()
Public Sub New()
Dim t = New Thread(AddressOf Run)
t.IsBackground = True
t.Start()
End Sub
Public Sub SearchAsync(ByVal text As String, ByVal callback As Action)
Dim wi = New WorkItem()
wi.Text = text
wi.Callback = callback
m_Queue.Add(wi)
End Sub
Private Sub Run()
Do While True
Dim wi As WorkItem = m_Queue.Take()
Dim found As Boolean = False
Do While Not found AndAlso m_Queue.Count = 0
' Continue searching using your custom algorithm here.
Loop
If found Then
wi.Callback()
End If
Loop
End Sub
Private Class WorkItem
Public Text As String
Public Callback As Action
End Class
End Class
Here is where the elegance happens. Look at how you can implement the logic from the UI thread now.
If (txtQuickSearch.Text.Length >= 3) Or (e.Key = Key.Enter) Then
searcher.SearchAsync(txtQuickSearch.Text, AddressOf OnSearchComplete)
End If
Note that OnSearchComplete will be executed on the worker thread so you will need to call Dispatcher.Invoke from within the callback if you want to publish the results to a UI control.
You can simulate a DoEvents in WPF by doing (in C#):
Dispatcher.Invoke(DispatcherPriority.Background, new Action(() => {}));

Resources