WPF Multi-Threading and rotating image - wpf

This one has driven me nuts for long enough.
Start a background thread to download data from a webservice on a new thread then show an image on the status bar and change the text.
I have tried with Dispatcher (with each priority) but nothing happens until the threaded sub completes. The closest I can get is implementing the equivalent of DoEvents that at least loads the image and the text, but then the image stops spinning until the thread completes.
Any ideas?
Public Sub Return_DT(ByVal TableName As String)
CurrentDT = TableName
If DownloadingDS Is Nothing Then
DownloadingDS = New Dictionary(Of String, String)
End If
If DownloadingDS.ContainsKey(TableName) = False Then
DownloadingDS.Add(TableName, "Loading")
Else
Exit Sub
End If
Select Case TableName
Case "A_Documents"
strSQL = "SELECT Document_ID, Account_Type, Account_No, Document_Description, Accounts_Only, Open_Editing, Editing_Name, Updated_Name, Updated FROM A_Documents"
Case Else
strSQL = "SELECT * FROM " & TableName
End Select
' Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, CType(Sub() LoadMetroImage(), SendOrPostCallback), Nothing)
'Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, CType(Sub() ChangeLeftStatusText("Downloading " & CurrentDT & " data..."), SendOrPostCallback), Nothing)
LoadMetroImage()
ChangeLeftStatusText("Downloading " & CurrentDT & " data...")
Application.Current.MainWindow.FindName("MainMetroStatusBar")
Dim vWorker As New BackgroundWorker
AddHandler vWorker.DoWork, AddressOf BackgroundDownload
AddHandler vWorker.RunWorkerCompleted, AddressOf DownloadCompleted
vWorker.RunWorkerAsync()
DoEvents()
End Sub
This is the closest I can get
Public Sub DoEvents()
Dim frame As New DispatcherFrame()
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, New DispatcherOperationCallback(AddressOf ExitFrame), frame)
Dispatcher.PushFrame(frame)
End Sub
Public Function ExitFrame(ByVal f As Object) As Object
CType(f, DispatcherFrame).Continue = False
Return Nothing
End Function
=== EDIT === How Return_DT is called
Public Function DT_Return(ByVal DT As DataTable, ByVal TableName As String) As DataTable
Try
If Not DT Is Nothing Then
If DT_CheckUpdated(TableName) = True Then
Return DT
Else
Return_DT(TableName)
vService = New Service1Client
Dim DS As DataSet = vService.ReturnDataSet("SELECT * FROM " & TableName, Current_HOA_ID)
Dim vDT As DataTable = DS.Tables(0).Copy
DS.Dispose()
Return vDT
End If
Else
Return_DT(TableName)
vService = New Service1Client
Dim DS As DataSet = vService.ReturnDataSet("SELECT * FROM " & TableName, Current_HOA_ID)
Dim vDT As DataTable = DS.Tables(0).Copy
DS.Dispose()
Return vDT
End If
Catch ex As Exception
EmailError(ex)
Return Nothing
End Try
End Function

I found the solution was to use Dispatcher.Timer in the UI thread to check every second to see if the DT had been downloaded..
Public Function DT_Return(ByVal DT As DataTable, ByVal TableName As String) As DataTable
Try
If Not DT Is Nothing Then
If DT_CheckUpdated(TableName) = True Then
Return DT
Else
CurrentlyDownloading = True
Return_DT(TableName)
Dim vTimer As New DispatcherTimer
vTimer.Interval = TimeSpan.FromMilliseconds(1000)
AddHandler vTimer.Tick, Sub(sender As Object, e As EventArgs)
Do While CurrentlyDownloading = True
Loop
vTimer.Stop()
End Sub
vTimer.Start()
Return DT
End If
Else
CurrentlyDownloading = True
Return_DT(TableName)
Dim vTimer As New DispatcherTimer
vTimer.Interval = TimeSpan.FromMilliseconds(1000)
AddHandler vTimer.Tick, Sub(sender As Object, e As EventArgs)
Do While CurrentlyDownloading = True
Loop
vTimer.Stop()
End Sub
vTimer.Start()
Return DT
End If
Catch ex As Exception
EmailError(ex)
Return Nothing
End Try
End Function

Related

Playing multiple audio files using system() in C

I've written a C code to play 3 audio files one after other using vlc but the after playing first file it's not proceeding I've to press Ctrl+C or q to go to next song which I want to happen itself.
I placed system("q") after every file so that it may fulfill my task but it's still not working.
#include<stdio.h>
int main(){
system("vlc 1.mp3");
system("q");
system("vlc 2.mp3");
system("q");
system("vlc 3.mp3");
system("q");
return 0;
}
I think you should use mplayer in slave mode instead of vlc. It is more flexible and has more control. you can send command to mplayer as you wish. please study the following link
http://www.mplayerhq.hu/DOCS/tech/slave.txt
I suggest you to use python for linux and C# or VB.NET for windows. I can supply some vb.net code if you need it.
This was my old answer for another question.
but I will post here for you too.
I am developing android phone remote control + VB.NET TCP server - mplayer. I am using mplayer in slave mode. I send command from android app to VB.NET TCP server. Then the command will send to mplayer.
I will show some code that control and send the mplayer desired commands, but the server part is not finished yet. The coding is no finished yet but I hope it is useful for you.
Imports System.ComponentModel
Imports System.IO
Imports System.Data.OleDb
Public Class Form1
Private bw As BackgroundWorker = New BackgroundWorker
Dim i As Integer = 0
Dim dbFile As String = Application.StartupPath & "\Data\Songs.accdb"
Public connstring As String = "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=" & dbFile & "; persist security info=false"
Public conn As New OleDbConnection(connstring)
Dim sw As Stopwatch
Dim ps As Process = Nothing
Dim jpgPs As Process = Nothing
Dim args As String = Nothing
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
Exit Sub
Else
' Perform a time consuming operation and report progress.
'System.Threading.Thread.Sleep(500)
bw.ReportProgress(i * 10)
Dim dir_info As New DirectoryInfo(TextBox1.Text)
ListFiels("SongList", TextBox2.Text, dir_info)
End If
End Sub
Private Sub bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
If e.Cancelled = True Then
Me.tbProgress.Text = "Canceled!"
ElseIf e.Error IsNot Nothing Then
Me.tbProgress.Text = "Error: " & e.Error.Message
Else
Me.tbProgress.Text = "Done!"
End If
End Sub
Private Sub bw_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
Me.tbProgress.Text = e.ProgressPercentage.ToString() & "%"
End Sub
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
Try
ps.Kill()
Catch
Debug.Write("already closed")
End Try
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Windows.Forms.Control.CheckForIllegalCrossThreadCalls = False 'To avoid error from backgroundworker
bw.WorkerReportsProgress = True
bw.WorkerSupportsCancellation = True
AddHandler bw.DoWork, AddressOf bw_DoWork
AddHandler bw.ProgressChanged, AddressOf bw_ProgressChanged
AddHandler bw.RunWorkerCompleted, AddressOf bw_RunWorkerCompleted
funPlayMusic()
End Sub
Private Sub buttonStart_Click(sender As Object, e As EventArgs) Handles buttonStart.Click
If Not bw.IsBusy = True Then
bw.RunWorkerAsync()
End If
End Sub
Private Sub buttonCancel_Click(sender As Object, e As EventArgs) Handles buttonCancel.Click
If bw.WorkerSupportsCancellation = True Then
bw.CancelAsync()
End If
End Sub
Private Sub btnSearch_Click(sender As Object, e As EventArgs) Handles btnSearch.Click
If Not bw.IsBusy = True Then
sw = Stopwatch.StartNew()
bw.RunWorkerAsync()
sw.Stop()
Label1.Text = ": " + sw.Elapsed.TotalMilliseconds.ToString() + " ms"
End If
End Sub
Private Sub ListFiels(ByVal tblName As String, ByVal pattern As String, ByVal dir_info As DirectoryInfo)
i = 0
Dim fs_infos() As FileInfo = Nothing
Try
fs_infos = dir_info.GetFiles(pattern)
Catch ex As Exception
MessageBox.Show(ex.ToString())
End Try
For Each fs_info As FileInfo In fs_infos
i += 1
Label1.Text = i
insertData(tblName, fs_info.FullName)
lstResults.Items.Add(i.ToString() + ":" + fs_info.FullName.ToString())
If i = 1 Then
Playsong(fs_info.FullName.ToString())
Else
i = 0
lstResults.Items.Clear()
End If
Next fs_info
sw.Stop()
Label1.Text = ": " + sw.Elapsed.TotalMilliseconds.ToString() + " ms"
fs_infos = Nothing
Dim subdirs() As DirectoryInfo = dir_info.GetDirectories()
For Each subdir As DirectoryInfo In subdirs
ListFiels(tblName, pattern, subdir)
Next
End Sub
Private Sub insertData(ByVal tableName As String, ByVal foundfile As String)
Try
If conn.State = ConnectionState.Open Then conn.Close()
conn.Open()
Dim SqlQuery As String = "INSERT INTO " & tableName & " (SngPath) VALUES (#sng)"
Dim SqlCommand As New OleDbCommand
With SqlCommand
.CommandType = CommandType.Text
.CommandText = SqlQuery
.Connection = conn
.Parameters.AddWithValue("#sng", foundfile)
.ExecuteNonQuery()
End With
conn.Close()
Catch ex As Exception
conn.Close()
MsgBox(ex.Message)
End Try
End Sub
Private Sub btnClearList_Click(sender As Object, e As EventArgs) Handles btnClearList.Click
lstResults.Items.Clear()
End Sub
Private Sub funPlayMusic()
ps = New Process()
ps.StartInfo.FileName = "D:\Music\mplayer.exe "
ps.StartInfo.UseShellExecute = False
ps.StartInfo.RedirectStandardInput = True
jpgPs = New Process()
jpgPs.StartInfo.FileName = "D:\Music\playjpg.bat"
jpgPs.StartInfo.UseShellExecute = False
jpgPs.StartInfo.RedirectStandardInput = True
'ps.StartInfo.CreateNoWindow = True
args = "-fs -noquiet -identify -slave " '
args += "-nomouseinput -sub-fuzziness 1 "
args += " -vo direct3d, -ao dsound "
' -wid will tell MPlayer to show output inisde our panel
' args += " -vo direct3d, -ao dsound -wid ";
' int id = (int)panel1.Handle;
' args += id;
End Sub
Public Function SendCommand(ByVal cmd As String) As Boolean
Try
If ps IsNot Nothing AndAlso ps.HasExited = False Then
ps.StandardInput.Write(cmd + vbLf)
'MessageBox.Show(ps.StandardOutput.ReadToEndAsync.ToString())
Return True
Else
Return False
End If
Catch ex As Exception
Return False
End Try
End Function
Public Sub Playsong(ByVal Songfilelocation As String)
Try
ps.Kill()
Catch
End Try
Try
ps.StartInfo.Arguments = args + " """ + Songfilelocation + """"
ps.Start()
SendCommand("set_property volume " + "80")
Catch e As Exception
MessageBox.Show(e.Message)
End Try
End Sub
Private Sub lstResults_SelectedIndexChanged(sender As Object, e As EventArgs) Handles lstResults.SelectedIndexChanged
Playsong(lstResults.SelectedItem.ToString())
End Sub
Private Sub btnPlayJPG_Click(sender As Object, e As EventArgs) Handles btnPlayJPG.Click
Try
' jpgPs.Kill()
Catch
End Try
Try
'ps.StartInfo.Arguments = "–fs –mf fps=5 mf://d:/music/g1/Image00020.jpg –loop 200" '-vo gl_nosw
'jpgPs.Start()
Shell("d:\Music\playjpg.bat")
' SendCommand("set_property volume " + "80")
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
Private Sub btnPlayPause_Click(sender As Object, e As EventArgs) Handles btnPlayPause.Click
SendCommand("pause")
End Sub
Private Sub btnMute_Click(sender As Object, e As EventArgs) Handles btnMute.Click
SendCommand("mute")
End Sub
Private Sub btnKaraoke_Click(sender As Object, e As EventArgs) Handles btnKaraoke.Click
'SendCommand("panscan 0-0 | 1-1")
SendCommand("af_add pan=2:1:1:0:0")
End Sub
Private Sub btnStereo_Click(sender As Object, e As EventArgs) Handles btnStereo.Click
SendCommand("af_add pan=2:0:0:1:1")
End Sub
Private Sub btnStop_Click(sender As Object, e As EventArgs) Handles btnStop.Click
Playsong("d:\music\iot.mp4")
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
'SendCommand("loadfile d:\music\iot.mp4")
'SendCommand("pt_step 1")
End Sub
End Class

WPF - destroy page after unloaded has run

In a WPF app we have the need to sometimes create a new tab that contains a Page inside a Frame..
Once the page has been opened (initialised once) it still seems to stay in navigation history and attempts to load data that may not be relevant at the time.
I have tried a myriad of methods including NavigationService.RemoveBackEntry, but it still persists :-(
This is an example of how the tab/page are opened
Private Sub CashFlow_Edit(sender As Object, e As RoutedEventArgs)
Try
Dim DGV As DGVx = ReportsCashFlow_Grid.FindName("CashFlow_DGV")
e.Handled = True
IsNewRecord = False
If DGV.SelectedItems.Count = 1 Then
Dim row As System.Data.DataRowView = DGV.SelectedItems(0)
Form_ID = row("ID")
Dim vName As String = row("Name")
Dim vTab As STC_Tabx = Application.Current.MainWindow.FindName(TabName)
Dim TabControl As STCx = Application.Current.MainWindow.FindName("AccountingReports_TabControl")
If Not vTab Is Nothing Then
vTab.Close()
End If
Dim MCFrame As New Frame
Dim MCTab As New STC_Tabx
With MCTab
.Name = TabName
.Header = " " & vName & " "
.ImageSource = ReturnImageAsString("Edit.png", 16)
.CloseButtonVisibility = DevComponents.WpfEditors.eTabCloseButtonVisibility.Visible
.TabToolTip = "View or edit the " & vName & " template"
.Content = MCFrame
End With
RemoveHandler MCTab.Closing, AddressOf TabControl_TabClosing
AddHandler MCTab.Closing, AddressOf TabControl_TabClosing
Dim vGrid As Grid = Application.Current.MainWindow.FindName("MainGrid_Accounting")
RegisterControl(vGrid, MCTab)
TabControl.Items.Add(MCTab)
Dim MCPage As New ReportCashFlow_Page
MCFrame.NavigationService.Navigate(MCPage)
LoadedTabs(TabName)
MCTab.IsSelected = True
End If
Catch ex As Exception
EmailError(ex)
End Try
End Sub
To remove all the back entries do something like:
while(NavigationService.CanGoBack)
{
NavigationService.RemoveBackEntry();
}
It's not the clean bit of code I would like, but it works - create a global Boolean - when the sub that opens the tab/page is called it's set to true and the loading event will only run the loading code if this is true - it's set to false at the end.
Private Sub ReportCashFlow_Page_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
Try
If IsNewTab = False Then
Exit Sub
End If
'Run all the loading code here
Catch ex As Exception
EmailError(ex)
Finally
IsNewTab = False
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, CType(Sub() CashFlow_LoadBudget(), SendOrPostCallback), Nothing)
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, CType(Sub() ToggleReserve(), SendOrPostCallback), Nothing)
End Try
End Sub

Using a Back Ground worker in VB.Net WPF

I'm new to WPF and the whole threading format. I'm pretty sure I'm close to getting this correct, however, I'm getting the error. "The calling thread cannot access this object because a different thread owns it." I was getting this error before and i had to add the "Dispatcher.BeginInvoke(Sub() LoadLoad())" line, but I'm still getting the error. When i get the error, it highlights the line "Dim dc = New VSCSystemEntities1" in the LoadLoad() sub method. Any help would be appreciated. Thanks.
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
InitialLoad()
End Sub
Private Sub InitialLoad()
Try
loadPayeeTransactionAccounts()
bw.WorkerSupportsCancellation = True
bw.WorkerReportsProgress = True
AddHandler bw.DoWork, AddressOf bw_DoWork
AddHandler bw.ProgressChanged, AddressOf bw_ProgressChanged
AddHandler bw.RunWorkerCompleted, AddressOf bw_RunWorkerCompleted
Catch ex As Exception
MessageBox.Show(ex.Message, "Atlas Error while Loading", MessageBoxButton.OK, MessageBoxImage.Error)
Finally
End Try
End Sub
Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
Dim worker As BackgroundWorker = CType(sender, BackgroundWorker)
LoadLoad()
End Sub
Private Sub LoadLoad()
Dispatcher.BeginInvoke(Sub() LoadLoad())
txtSearch.Text = ""
Dim dc = New VSCSystemEntities1
Dim ContainsSearch = New List(Of tblActgPayeeTransactionAccounts2)
Try
ContainsSearch = (From z In dc.tblActgPayeeTransactionAccounts2 Select z Where z.intActgAcctID <> 236).ToList
For Each item In (ContainsSearch)
For i = 1 To ContainsSearch.Count
aList.add(New PayeeTransactions() With {.AccountingAccountID = item.intActgAcctID, .ProgramCode = item.chrPgmCode, _
.CarrierCode = item.intCarrierCode, .DealerNumber = item.chrDlrNum, _
.CoverageCode = item.chrCvgCode, .PayeeType = item.chrPayeeType, .FeeType = item.chrFeeType, .PayeeCode = item.intPayeeCode.ToString, _
.TransactionType = item.chrTransType, .AccountID = item.inbAcctgID, _
.CarrierDescription = item.chrCarrierDesc, .CarrierDescriptionShort = item.chrCarrierDescSht, _
.AccountCustomerID = item.intAcctCustID.ToString, .AccountCode = item.intAcctCo, _
.AccountCodeReceived = item.intAcctCoRec})
System.Threading.Thread.Sleep(200)
bw.ReportProgress(i)
Next
Next
lblCount.Content = ContainsSearch.Count()
dGrid.ItemsSource = Nothing
_ContainsText = CollectionViewSource.GetDefaultView(aList)
dGrid.ItemsSource = _ContainsText
Catch ex As Exception
Throw New Exception("Error: " & ex.Message)
Finally
dc.Dispose()
End Try
End Sub
Private Sub bw_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
Me.tbProgress.Text = e.ProgressPercentage.ToString() & "%"
End Sub
Private Sub bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
If e.Cancelled = True Then
Me.tbProgress.Text = "Canceled!"
ElseIf e.Error IsNot Nothing Then
Me.tbProgress.Text = "Error: " & e.Error.Message
Else
Me.tbProgress.Text = "Done!"
End If
End Sub
Private Sub btnRefresh_Click(sender As Object, e As RoutedEventArgs) Handles btnRefresh.Click
If Not bw.IsBusy = True Then
bw.RunWorkerAsync()
End If
End Sub

WPF ListBox - refresh

I have a ListBox populated by a DataTable - adding items, moving items all work but delete doesn't - it reflects in the DataTable but clears all items from the ListBox unless it is reloaded as part of a SelectionChanged event.
I have tried Listbox.Items.Refresh and setting the ItemsSource to Nothing and re-assigning back to the DataTable.
Any ideas?
Thanks
Private Sub Reports_BalanceSheet_NominalListBox_Delete(NomLB As String, DT As DataTable)
Try
Dim LB As ListBox = Reports_BalanceSheet_Grid.FindName(NomLB)
If LB.SelectedIndex = -1 Then
AppBoxValidation("No item has been selected for deletion!")
Exit Sub
End If
Dim FR() As DataRow = DT.Select("ID = " & LB.SelectedValue, Nothing)
Dim CatID As Integer = 0
For Each row As DataRow In FR
CatID = row("CatID")
row.Delete()
Next
DT.AcceptChanges()
Dim vDV As New DataView(DT)
vDV.RowFilter = "FormID = " & FormID & " AND CatID = " & CatID
vDV.Sort = "Position"
DT = vDV.ToTable
vDV = Nothing
Dim i As Integer = 0
For Each row As DataRow In DT.Rows
row("Position") = i
i += 1
Next
With LB
.ItemsSource = DT.DefaultView
.DisplayMemberPath = "Name"
.SelectedValuePath = "ID"
End With
Catch ex As Exception
EmailError(ex)
End Try
End Sub
Turns out the clue was 'unless it is reloaded as part of the SelectionChanged event'
added this to the end and it all works perfectly :-)
Have you ever noticed that you can spend hours going round in circles and the moment you post the problem you figure it out?
Dim MainLB As String = NomLB.Replace("Nominal", "")
Reports_BalanceSheet_ListBox_IndexChanged(MainLB, NomLB, DT)
and that runs
Private Sub Reports_BalanceSheet_ListBox_IndexChanged(ByVal MainLB As String, ByVal NominalLB As String, ByVal NomDT As DataTable)
Try
Dim LB As ListBox = Reports_BalanceSheet_Grid.FindName(MainLB)
If LB.SelectedIndex = -1 Then
Exit Sub
End If
Dim NomLB As ListBox = Reports_BalanceSheet_Grid.FindName(NominalLB)
If NomLB Is Nothing Then
Exit Sub
End If
If LB.SelectedValue Is Nothing Then
Exit Sub
End If
If LB.SelectedValue.GetType.Name Is Nothing Then
Exit Sub
End If
If LB.SelectedValue.GetType.Name <> "DataRowView" Then
Dim CatID As Integer = LB.SelectedValue
Dim DT As DataTable = NomDT.Copy()
Dim vDV As New DataView(DT)
vDV.RowFilter = "CatID = " & CatID & " AND FormID = " & FormID
vDV.Sort = "Position"
DT = vDV.ToTable
vDV = Nothing
With NomLB
.ItemsSource = DT.DefaultView
.SelectedValuePath = "ID"
.DisplayMemberPath = "NomName"
.Items.Refresh()
End With
End If
Catch ex As Exception
EmailError(ex)
End Try
End Sub

OleDbDataAdapter Fill creating duplicate rows

I'm experiencing an issue where I use a data adapter to update and then refill a datatable. After calling the fill method, the row gets duplicated. One ID has the correct (new) ID and the other shows -1 for the ID. The code below works perfect and is the simpler form of what I want my more complex code to do. Cosider the following:
Imports WindowsApplication1.testDataSet
Imports WindowsApplication1.testDataSetTableAdapters
Imports System.Data.OleDb
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Dim DA As New testTableAdapter
Dim DT As New testDataTable
DA.Fill(DT)
Dim NR As testRow = DT.Rows.Add
NR.SomeText = "Test"
Dim DA2 As New OleDbDataAdapter("SELECT * FROM test", _
DA.Connection.ConnectionString)
Dim CB As New OleDbCommandBuilder(DA2)
DA2.Update(DT)
DA.Fill(DT)
For Each R As testRow In DT.Rows
Debug.Print(R.ID)
Next
End Sub
End Class
The code above works perfect. They key column doesn't show -1, there are no duplicates. Now consider the code below from my application which causes a duplicate row with the key column resulting in -1 right after the last LoadLoadNumbers().
Dim AccountLoans As IEnumerable(Of LoanNumbersRow) = _
From L As LoanNumbersRow In LoanNumbers _
Select L Where L.AccountID = ID
If Not frmFindLoans.IsDisposed AndAlso _
frmFindLoans.DialogResult = Windows.Forms.DialogResult.OK Then
For Each L As LoanNumbersRow In AccountLoans
If (From R As DataGridViewRow In frmFindLoans.dgvLoans.Rows _
Select R Where R.Cells("LoanNumber").Value = L.LoanNumber).Count = 0 Then
If L.IsWhenDeletedNull Then
L.WhenDeleted = Now
L.DeletedBy = UserName()
End If
End If
Next
Dim NewLoan As LoanNumbersRow
Dim FindLoan As IEnumerable(Of LoanNumbersRow)
For Each R As DataGridViewRow In frmFindLoans.dgvLoans.Rows
FindLoan = From L As LoanNumbersRow In LoanNumbers.Rows _
Select L Where L.LoanNumber = R.Cells("LoanNumber").Value And _
L.AccountID = ID
If FindLoan.Count = 0 Then
NewLoan = LoanNumbers.Rows.Add
NewLoan.AccountID = Acc.AccountID
NewLoan.LoanNumber = R.Cells("LoanNumber").Value
NewLoan.LoanBusinessName = R.Cells("LoanBusiness").Value
NewLoan.LoanBorrower = R.Cells("LoanBorrower").Value
NewLoan.AddedBy = UserName()
NewLoan.WhenAdded = Now
End If
Next
Try
Dim CB As New OleDbCommandBuilder(LoanNumbersAdapter)
LoanNumbersAdapter.Update(LoanNumbers)
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.Critical, "Error saving loan number data")
Exit Sub
End Try
If Not LoadLoanNumbers() Then Exit Sub
End If
Other variables and such from a module:
Public LoanNumbersAdapter As OleDbDataAdapter
Public LoanNumbers As New LoanNumbersDataTable
Public Sub InitializeAdapters()
LoanNumbersAdapter = New OleDbDataAdapter( _
"SELECT * FROM LoanNumbers WHERE WhenDeleted IS NULL ORDER BY WhenAdded DESC", AccountingConn)
End Sub
Public Function LoadData(ByVal DA As OleDbDataAdapter, ByVal DT As DataTable) As Boolean
Try
DA.Fill(DT)
Return True
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.Critical, "Error loading the " & DT.TableName & " table")
Return False
End Try
End Function
Public Function LoadLoanNumbers() As Boolean
Return LoadData(LoanNumbersAdapter, LoanNumbers)
End Function
Why does the simple test at the top work fine, but my actual application create the duplicate row with -1 on the key column? I suppose I could clear the datatable before filling after the update but wouldn't that bog it down once it starts becoming a large table?
*BTW: The DB is MS access and it's .NET 3.5
This is my duct tape solution :(
''' <summary>
''' Removes any rows where the ID/key column is less than zero
''' </summary>
<Extension()> Public Sub DeleteRelics(ByVal DT As DataTable)
If DT.PrimaryKey.Count = 0 Then Exit Sub
For Each R As DataRow In _
(From Rows As DataRow In DT.Rows _
Select Rows Where Rows(DT.PrimaryKey.First.ColumnName) < 0)
R.Delete()
Next
DT.AcceptChanges()
End Sub

Resources