2013-07-14 71 views
1

我正在寫一個小型的postIt程序,它應該使用戶能夠使用數據庫向其他計算機發送小筆記。 我有一個主要的表單,我可以在那裏寫信息並在線查看用戶,一個應該顯示消息的表單以及一個包含用戶和消息表的sql數據庫。Form.show()不響應

這個想法是,我正在將消息寫入Messagestable,我的程序使用SqlDependency來獲取新消息並將它們顯示在工作表中。到現在爲止還挺好。

問題是,當我添加一個新的消息到我的表SqlDependency觸發一個事件和我的mainForm創建一個新的表後凍結sheet.Show()被調用後。 mainForm繼續運行,但我的工作表總是不響應。

這裏是我的代碼: DBListener:

Imports System.Data 
    Imports System.Data.Sql 
    Imports System.Data.SqlClient 
    Imports System.Threading 

Public Class DBListener 

    Private changeCount As Integer = 0 

    Private Const tableName As String = "IMSMessages" 
    Private Const statusMessage As String = _ 
     "{0} changes have occurred." 
    Private exitRequested As Boolean = False 
    Private waitInProgress As Boolean = False 
    Private recieverHost As String 
    Private imsMain As IMSMain 

    Public Sub New(main As IMSMain, recieverHost As String) 
     Me.imsMain = main 
     Me.recieverHost = recieverHost 
     initCommand(recieverHost) 
     SqlDependency.Start(connectionString) 
     GetMsg(recieverHost) 
    End Sub 


    Private connectionString As String = "Data Source=NB_RANDY\SQLEXPRESS;Initial Catalog=eurom_test;Integrated Security=SSPI;" 
    Private sqlConn As SqlConnection = New SqlConnection(connectionString) 
    Private commandMesg As SqlCommand = Nothing 
    Private commandUsers As SqlCommand = Nothing 

    Private Sub initCommand(recieverHost As String) 
     commandMesg = New SqlCommand 
     commandMesg.CommandText = "SELECT MessageID,SenderHost,RecieverHost,isRead,isRecieved,Stamp from dbo.IMSMessages " & 
             "WHERE [email protected]" & 
             " AND isRecieved = 0" 
     commandMesg.Parameters.Add("@RecieverHost", SqlDbType.VarChar).Value = recieverHost 
     commandMesg.Connection = sqlConn 

     commandUsers = New SqlCommand 
     commandUsers.CommandText = "Select HostName From dbo.IMSUser" 
    End Sub 




    Private Sub OnChangeMsg(ByVal sender As System.Object, ByVal e As System.Data.SqlClient.SqlNotificationEventArgs) 
     Dim dep As SqlDependency = DirectCast(sender, SqlDependency) 
     RemoveHandler dep.OnChange, AddressOf OnChangeMsg 
     GetMsg(recieverHost) 
    End Sub 
    Private Sub OnChangeUser(ByVal sender As System.Object, ByVal e As System.Data.SqlClient.SqlNotificationEventArgs) 
     Dim dep As SqlDependency = DirectCast(sender, SqlDependency) 
     RemoveHandler dep.OnChange, AddressOf OnChangeUser 
     GetUsers() 
    End Sub 

    Public Sub GetMsg(recieverHost As String) 
     If Not sqlConn.State = ConnectionState.Open Then 
      sqlConn.Open() 

     End If 

     commandMesg.Notification = Nothing 
     Dim dep As SqlDependency = New SqlDependency(commandMesg) 
     AddHandler dep.OnChange, New OnChangeEventHandler(AddressOf OnChangeMsg) 
     Dim table As DataTable = New DataTable 
     Dim adapter As SqlDataAdapter = New SqlDataAdapter(commandMesg) 
     adapter.Fill(table) 
     imsMain.recieveNewMessages(table) 
    End Sub 
    Public Sub GetUsers() 
     If Not sqlConn.State = ConnectionState.Open Then 
      sqlConn.Open() 

     End If 

     commandMesg.Notification = Nothing 
     Dim dep As SqlDependency = New SqlDependency(commandUsers) 
     AddHandler dep.OnChange, New OnChangeEventHandler(AddressOf OnChangeUser) 
     Dim table As DataTable = New DataTable 
     imsMain.updateOnlineUserList() 
    End Sub 


End Class 

IMSMain表格:

Imports System.Data.SqlClient 
    Imports System.Threading 

    Public Class IMSMain 

    Private user As IMSUser 
    Private listener As DBListener 


    Private sqlConn As SqlConnection 

    Public Sub New() 
     InitializeComponent() 
     sqlConn = New SqlConnection("Data Source=NB_RANDY\SQLEXPRESS;Initial Catalog=eurom_test;Integrated Security=SSPI;") 



     Me.user = New IMSUser(My.Computer.Name) 
     user.register() 

     updateOnlineUserList() 
     listener = New DBListener(Me, user.HostName) 

    End Sub 
    Private Sub onSend(sender As Object, e As EventArgs) Handles bt_send.Click 
     Dim command As SqlCommand = New SqlCommand 
     Dim msgText = tb_text.Text 
     Dim reciever As IMSUser = lb_Online.SelectedItem 
     Dim insert_string As String = "Insert INTO dbo.IMSMessages(Text,RecieverHost,SenderHost,isRead,isRecieved,Stamp) Values(@Text,@RecieverHost,@SenderHost,@isRead,@isRecieved,@Stamp)" 
     Dim adapter As SqlDataAdapter = New SqlDataAdapter 
     Dim table As DataTable = New DataTable() 
     Try 

      If Not sqlConn.State = ConnectionState.Open Then 
       sqlConn.Open() 

      End If 
      command.Connection = sqlConn 
      command.CommandText = insert_string 
      adapter.SelectCommand = command 
      adapter.Fill(table) 
      command.CommandText = insert_string 
      command.Parameters.Add("@Text", SqlDbType.VarChar).Value = msgText 
      command.Parameters.Add("@RecieverHost", SqlDbType.NChar).Value = reciever.HostName 
      command.Parameters.Add("@SenderHost", SqlDbType.NChar).Value = user.HostName 
      command.Parameters.Add("@isRecieved", SqlDbType.Bit).Value = 0 
      command.Parameters.Add("@isRead", SqlDbType.Bit).Value = 0 
      command.Parameters.Add("@Stamp", SqlDbType.SmallDateTime).Value = DateTime.Now 
      command.ExecuteNonQuery() 
     Catch ex As Exception 
      Console.WriteLine("onSend: internal database exception" + ex.Message) 
     End Try 
    End Sub 

    Private Sub processMessageId(refMessageID As Integer) 
     Dim command As SqlCommand = New SqlCommand 
     Dim msgID_string As String = "SELECT * from dbo.IMSMessages " & 
             "WHERE [email protected] AND isRecieved = 0" & 
             " ORDER BY Stamp" 
     Dim isRecievedUpdate_string As String = "Update dbo.IMSMessages " & 
              "SET isRecieved=1" & 
              " WHERE [email protected]" 
     Dim adapter As SqlDataAdapter = New SqlDataAdapter 
     Dim table As DataTable = New DataTable() 
     Try 
      If Not sqlConn.State = ConnectionState.Open Then 
       sqlConn.Open() 

      End If 
      command = New SqlCommand 
      command.CommandText = msgID_string 
      command.Parameters.Add("@MessageID", SqlDbType.Int).Value = refMessageID 
      command.Connection = sqlConn 
      adapter.SelectCommand = command 
      adapter.Fill(table) 
      command = New SqlCommand 
      command.Connection = sqlConn 
      command.CommandText = isRecievedUpdate_string 
      command.Parameters.Add("@MessageID", SqlDbType.Int).Value = refMessageID 
      command.ExecuteNonQuery() 

     Catch ex As Exception 
      Console.WriteLine("processMessageID: internal database exception" + ex.Message) 
     End Try 
     Try 
      Dim row As DataRow = table.Rows(0) 
      Dim senderHost As String = row("SenderHost") 
      Dim sender As IMSUser = New IMSUser(senderHost) 
      Dim currentSheet As IMSSheet 
      Dim stringText As String = row("Text") 
      Dim stamp As Date = row("Stamp") 
      currentSheet = New IMSSheet(sender, stringText, stamp) 
      currentSheet.Show() 
     Catch ex As Exception 
      Console.WriteLine("processMessageID: error while showing sheet") 
     End Try 
    End Sub 

    Public Sub recieveNewMessages(newMessageTable As DataTable) 
     For Each row As DataRow In newMessageTable.Rows 
      Dim id As Integer = row("MessageID") 
      processMessageId(id) 
     Next 
    End Sub 

    Public Sub updateOnlineUserList() 
     lb_Online.Items.Clear() 
     Dim adapter As SqlDataAdapter = New SqlDataAdapter 
     Dim table As DataTable = New DataTable() 
     Dim command As SqlCommand = New SqlCommand 
     Dim onlineUser_string = "Select * FROM dbo.IMSUser" 
     command.CommandText = onlineUser_string 
     Try 
      If Not sqlConn.State = ConnectionState.Open Then 
       sqlConn.Open() 

      End If 
      command.Connection = sqlConn 
      adapter.SelectCommand = command 
      adapter.Fill(table) 
     Catch ex As Exception 
      Console.WriteLine("internal database exception" + ex.Message) 
     End Try 
     For Each row As DataRow In table.Rows 

      Dim tmp As IMSUser = New IMSUser(row("HostName")) 
      If Not user.Equals(tmp) Then 
       lb_Online.Items.Add(tmp) 
      End If 
     Next 
    End Sub 

End Class 

IMSSheet表格:

Public Class IMSSheet 
     Dim IsDraggingForm As Boolean = False 
     Private MousePos As New System.Drawing.Point(0, 0) 

    Public Sub New(session As IMSUser, text As String, stamp As Date) 
     Ini 

tializeComponent() 
      Me.tb_sender.Text = session.HostName 
      addText(text, stamp) 
     End Sub 

     Private Sub addText(msg As String, stamp As DateTime) 
      Dim currentText As String = tb_text.Text 
      currentText = currentText + stamp.ToString + Environment.NewLine + msg + Environment.NewLine 
      tb_text.Text = currentText 
     End Sub 
     Private Sub IMSSheet_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs) Handles MyBase.MouseDown 
      If e.Button = MouseButtons.Left Then 
       IsDraggingForm = True 
       MousePos = e.Location 
      End If 
     End Sub 

     Private Sub IMSSheet_MouseUp(ByVal sender As Object, ByVal e As MouseEventArgs) Handles MyBase.MouseUp 
      If e.Button = MouseButtons.Left Then IsDraggingForm = False 
     End Sub 

     Private Sub IMSSheet_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs) Handles MyBase.MouseMove 
      If IsDraggingForm Then 
       Dim temp As Point = New Point(Me.Location + (e.Location - MousePos)) 
       Me.Location = temp 
       temp = Nothing 
      End If 
     End Sub 

     Private Sub onClose(sender As Object, e As EventArgs) Handles bt_Close.Click 
      Me.Close() 
     End Sub 


     Private Sub bt_minimize_Click(sender As Object, e As EventArgs) Handles bt_minimize.Click 
      Me.WindowState = FormWindowState.Minimized 
     End Sub 

     Private Sub bt_Close_MouseHover(sender As Object, e As EventArgs) Handles bt_Close.MouseHover 

     End Sub 

     Private Sub bt_minimize_MouseHover(sender As Object, e As EventArgs) Handles bt_minimize.MouseHover 

     End Sub 
    End Class 

有趣的是,如果我已經有一些新的消息在我運行程序之前在我的Messagestable中,他們將顯示cor rectly。 如果我使用Form.ShowDialog()顯示工作表,它也可以工作,但它不是我希望它工作的方式。

由於我出於主意,我希望你能幫助我。

+0

在Show()調用上設置一個斷點。當它中斷時,使用Debug + Windows + Threads來查看代碼在哪個線程上運行。很高的可能性,你會發現它不是你的程序的主UI線程*。這使得窗體像門扇一樣死去。您需要使用Control.BeginInvoke()來讓代碼在UI線程上運行,表單有機會運行。 –

+0

您的Catch塊是否會捕獲異常,您無法看到,因爲您正在使用Console.Writeline和表單應用程序?請改用MessageBox.Show(ex.Message)。 – tinstaafl

+0

謝謝..我會嘗試你所提到的。 @tinstaafl我已經試圖捕捉任何異常,但沒有...你是對的我現在使用調試而不是控制檯。 – user2580785

回答

0

您遇到問題,因爲SqlDependency的更改事件發生在與您的UI線程不同的線程上。從SqlDepending documentation

可在從啓動命令執行 線程不同的線程來生成OnChange事件。

當調用在非UI線程sheet.Show()Show()會死機因爲沒有消息循環來處理UI事件。在ImsMain上使用Invoke()在您的UI線程上創建並顯示ImsSheet

+0

我必須嘗試這個,但似乎這可能是問題所在。 – user2580785

+0

好的,非常感謝,這解決了我的問題! – user2580785