Having taken your advice on-board (thank-you so VERY much), now seem to
have a working program, with no dead-locks!
How does this look?
[VB.NET]
Imports System.Data.SqlServerCe
Public Class Form1
Inherits System.Windows.Forms.Form
#Region " Windows Form Designer generated code "
'Form overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
MyBase.Dispose(disposing)
End Sub
'NOTE: The following procedure is required by the Windows Form Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
Friend WithEvents ListBox1 As System.Windows.Forms.ListBox
Friend WithEvents Button1 As System.Windows.Forms.Button
Friend WithEvents ProgressBar1 As System.Windows.Forms.ProgressBar
Friend WithEvents Label1 As System.Windows.Forms.Label
Private Sub InitializeComponent()
Me.ListBox1 = New System.Windows.Forms.ListBox
Me.Button1 = New System.Windows.Forms.Button
Me.ProgressBar1 = New System.Windows.Forms.ProgressBar
Me.Label1 = New System.Windows.Forms.Label
'
'ListBox1
'
Me.ListBox1.Location = New System.Drawing.Point(8, 8)
Me.ListBox1.Size = New System.Drawing.Size(168, 93)
'
'Button1
'
Me.Button1.Location = New System.Drawing.Point(104, 128)
Me.Button1.Text = "Sync Now"
'
'ProgressBar1
'
Me.ProgressBar1.Location = New System.Drawing.Point(8, 108)
Me.ProgressBar1.Size = New System.Drawing.Size(168, 12)
'
'Label1
'
Me.Label1.Font = New System.Drawing.Font("Tahoma", 9.0!,
System.Drawing.FontStyle.Bold)
Me.Label1.Location = New System.Drawing.Point(40, 32)
Me.Label1.Size = New System.Drawing.Size(104, 32)
Me.Label1.Text = "Populating, Please Wait"
Me.Label1.TextAlign = System.Drawing.ContentAlignment.TopCenter
Me.Label1.Visible = False
'
'Form1
'
Me.ClientSize = New System.Drawing.Size(186, 159)
Me.Controls.Add(Me.Label1)
Me.Controls.Add(Me.ProgressBar1)
Me.Controls.Add(Me.Button1)
Me.Controls.Add(Me.ListBox1)
Me.Font = New System.Drawing.Font("Tahoma", 8.25!,
System.Drawing.FontStyle.Regular)
Me.Text = "Form1"
End Sub
Public Shared Sub Main()
Application.Run(New Form1)
End Sub
#End Region
Public Sub New()
MyBase.New()
InitializeComponent()
bgWorker.WorkerReportsProgress = True
' I have no idea what AliceBlue looks like, I only have a monochrome
device to hand!
Label1.BackColor = Color.AliceBlue
End Sub
Private Const FILENAME As String = "\Program Files\MyApp\MyData.sdf"
Private Const MAXVALUE As Integer = 1000
Private WithEvents bgWorker As New
OpenNETCF.ComponentModel.BackgroundWorker
Private ConnectionLocker As New Object
Private bReporting As Boolean
Public ReadOnly Property Connection() As SqlCeConnection
Get
SyncLock ConnectionLocker
Static cnn As SqlCeConnection
If cnn Is Nothing Then _
cnn = New SqlCeConnection
If cnn.ConnectionString Is Nothing OrElse
cnn.ConnectionString.Trim().Length = 0 Then _
cnn.ConnectionString = "Data Source = '" + FILENAME + "'"
If Not IO.File.Exists(FILENAME) Then
Dim e As SqlCeEngine
Dim c As SqlCeCommand
Try
e = New SqlCeEngine(cnn.ConnectionString)
e.CreateDatabase()
cnn.Open()
c = New SqlCeCommand("CREATE TABLE SomeTable (ID int IDENTITY,
Description nvarchar(255))", cnn)
c.ExecuteNonQuery()
Finally
If Not (c Is Nothing) Then c.Dispose()
If Not (e Is Nothing) Then e.Dispose()
End Try
End If
If cnn.State <> ConnectionState.Open Then _
cnn.Open()
Return cnn
End SyncLock
End Get
End Property
Private Sub Form1_Activated(ByVal sender As Object, ByVal e As
System.EventArgs) Handles MyBase.Activated
Try
Me.Label1.Visible = True
Application.DoEvents()
Me.ListBox1.DataSource = Nothing
Me.ListBox1.ValueMember = "ID"
Me.ListBox1.DisplayMember = "Description"
Me.ListBox1.DataSource = GetSomeData()
Catch ex As Exception
MessageBox.Show(ex.Message)
Finally
Me.Label1.Visible = False
End Try
End Sub
Public Function GetSomeData() As DataTable
Dim da As SqlCeDataAdapter
Try
SyncLock ConnectionLocker
da = New SqlCeDataAdapter("SELECT ID, Description FROM SomeTable",
Connection)
Dim rtn As New DataTable
da.Fill(rtn)
Return rtn
End SyncLock
Finally
If Not (da Is Nothing) Then da.Dispose()
End Try
End Function
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click
bgWorker.RunWorkerAsync()
End Sub
Private Sub bgWorker_DoWork(ByVal sender As Object, ByVal e As
OpenNETCF.ComponentModel.DoWorkEventArgs) Handles bgWorker.DoWork
Dim cmd As SqlCeCommand
bReporting = False
Try
SyncLock ConnectionLocker
cmd = New SqlCeCommand("INSERT INTO SomeTable (Description)
VALUES(?)", Connection)
cmd.Parameters.Add("@Description", SqlDbType.NVarChar)
For i As Integer = 1 To MAXVALUE
cmd.Parameters(0).Value = i
cmd.ExecuteNonQuery()
If Not bReporting Then
bReporting = True
bgWorker.ReportProgress(CInt((i / MAXVALUE) * 100), i)
End If
Next
End SyncLock
Catch ex As Exception
MessageBox.Show(ex.Message)
Finally
If Not (cmd Is Nothing) Then cmd.Dispose()
End Try
End Sub
Private Sub bgWorker_ProgressChanged(ByVal sender As Object, ByVal e As
OpenNETCF.ComponentModel.ProgressChangedEventArgs) Handles
bgWorker.ProgressChanged
Me.ProgressBar1.Value = e.ProgressPercentage
Application.DoEvents()
bReporting = False
End Sub
Private Sub bgWorker_RunWorkerCompleted(ByVal sender As Object, ByVal e
As OpenNETCF.ComponentModel.RunWorkerCompletedEventArgs) Handles
bgWorker.RunWorkerCompleted
Me.ProgressBar1.Value = Me.ProgressBar1.Maximum
Application.DoEvents()
End Sub
End Class
[/VB.NET]
Daniel said:
I hope my replies to your other posts (in this and other threads) give
you something to work at.
Just some quick comments on the code you posted:
1. The area with button1_click, syncnow, updateprogressbar is a classic
fit for using the BackgroundWorker instead. Do check that out.
2. If all you are doing in a Catch is Throw, then you might as well omit
the catch block.
3. Rather than explicit use of Monitor.Enter/Exit, do use SyncLock as it
will make your code clearer to read
4. If your UI needs to fetch some data (e.g. your activated method) and
the data cannot be read right now (e.g. your db is busy being used by
something else) do not block the UI. E.g. put a friendly message to the
user that allows them to cancel or retry (if you don't want to do that
automatically). At the moment you are kind of assuming that the data will
be returned no matter what.
5. Generally don't lock on properties and specifically don't lock on the
connection object. Use a dedicated object for this purpose (object obj =
new object())
Other than that, using the EventWaitHandle (WaitOne with timeout) as I
suggested is your best bet here.
Cheers
Daniel