Hei,
Tässä harjoittelen otsikon mukaista yhdistelmää ja usean tunnin googlaaminen ei tuottanut tulosta.
Public Class PGViewModel Private PGL as ObservableCollection(Of PGModel) 'PGModel sisältää vain Id ja Name muuttujat Public ReadOnly Property AddNewCommand As ICommand Public Sub New() PGL = New ObservableCollection(Of PGModel)(PGService.GetAll) AddHandler PGL.CollectionChanged, AddressOf CollectionChanged AddNewCommand = New AddNewPGCommand(PGL) End Sub Private Sub CollectionChanged(sender As Object, e As System.Collections.Specialized.NotifyCollectionChangedEventArgs) If e.Action = Specialized.NotifyCollectionChangedAction.Add Then Try PGService.Insert(PGL(e.NewStartingIndex)) Catch ex As Exception MsgBox(ex.Message) End Try End If End Sub End Class
Public Class PGService Public Sub Insert(ByRef PG As PGModel) Using conn As New SQLiteConnection(My.Settings.ConnString) Using cmd As New SQLiteCommand With cmd .CommandType = CommandType.Text .CommandText = "INSERT INTO tblProcessGroup (Name) VALUES ('" & PG.Name & "');" '.CommandText = "INSERT INTO tblProcessGroup (Name) VALUES ('" & PG.Name & "'); SELECT last_insert_rowid();" .Connection = conn End With conn.Open() cmd.ExecuteNonQuery() 'PG.Id = cmd.ExecuteScalar() End Using End Using End Sub End Class
Public Class AddNewPGCommand Inherits CommandBase Private _PGL As ObservableCollection(Of PGModel) Public Sub New(ByRef PGL As ObservableCollection(Of PGModel)) _PGL = PGL End Sub Public Overrides Sub Execute(parameter As Object) Try _PGL.Add(New PGModel With {.Name = "TestPG11"}) Catch ex As Exception MsgBox(ex.Message) End Try End Sub End Class
Viewissä on DataGrid (bindattu PGViewModel.PGL ) ja Button(bindattu PGViewModel.AddNewCommand).
Eli button siis laukaisee AddNewCommand-komennon, joka lisää PGL kokoelmaan yhden rivin, joka taas laukaisee CollectionCanged eventin PGViewModelissa, joka kirjoittaa kyseisen tietueen tietokantaan.
Homma toimii ihan kivasti näin, mutta tarvisin tuon uuden rivin Id:n joka generoidaan automaattisesti tietokannassa(Nyt se on aina 0). Kokeilin asettaa sitä tuossa PGService.Insert metodissa (comment out rivit) mutta CollectionChanged Eventissä saan virheen (try catch message) -> "Cannot change ObservableCollection during a CollectionChanged event.". Id Muutos menee kuitenkin läpi pelkällä em. ilmoituksella. Ilman try-catch blokkeja prosessi ei mene läpi.
Miten tämä oikeaoppisesti toteutetaan?
Yleensä sen saa automaattisesti jostain. Netissä vihjataan, että SQLiteConnection-luokassa olisi LastInsertRowId. Näköjään System.Data.SQLite-kirjaston dokumentaatio on vain CHM-formaatissa ja lähdekoodiakaan ei saa netissä selattua, joten voit varmaan selailla näitä omalla koneellesi asian tarkistamiseksi.
Siis juuri tuotahan olen käyttänytkin, mutta saan tämän herjan:
"Cannot change ObservableCollection during a CollectionChanged event.".
Ihmettelen että se kuitenkin menee läpi kunhan poikkeus "käsitellään" (=MsgBox) Catch blokissa, enkä millään saata uskoa että tämä olisi se oikea tapa.
Public Class PGService Public Sub Insert(ByRef PG As PGModel) Using conn As New SQLiteConnection(My.Settings.ConnString) Using cmd As New SQLiteCommand With cmd .CommandType = CommandType.Text .CommandText = "INSERT INTO tblProcessGroup (Name) VALUES ('" & PG.Name & "');" ---------> '.CommandText = "INSERT INTO tblProcessGroup (Name) VALUES ('" & PG.Name & "'); SELECT last_insert_rowid();" .Connection = conn End With conn.Open() cmd.ExecuteNonQuery() ---------> 'PG.Id = cmd.ExecuteScalar() End Using End Using End Sub End Class
Kyllähän SQL-kyselyn muuttaminen on ihan eri asia kuin valmis LastInsertRowId-ominaisuus. Kiva jos toimii kuitenkin.
Debuggausvinkkejä: Kun saamasi virheilmoitus valittaa muokkaamisesta, niin olisiko vika siinä, että PG.Id:tä ei saa muuttaa tuossa kohti? Eli jos laitat vaikka ihan PG.Id = 123, niin tuleeko virhe tästäkin? Sittenhän sinun pitää ratkaista tätä ihan ohjelman rakenteesta lähtien, että onko tuo oikea paikka laittaa tietoja kantaan tai mitä muuta reittiä id:n voisi välittää.
Mystinen PG aiheuttaa PääGipua, mutta MVVM jää tuosta lyhyestä koodista vielä aika epäselväksi. Nähdäkseni CollectionChanged olisi tarkoitettu enemmänkin siihen, että tietoa (ja ilmoitus muuttumisesta) välittyisi tietokannasta käyttöliittymän suuntaan. Sen sijaan napista tieto pitäisi lisätä ensin kantaan ja sitten vasta tuohon ObservableCollection-olioon.
Metabolix kirjoitti:
Sen sijaan napista tieto pitäisi lisätä ensin kantaan ja sitten vasta tuohon ObservableCollection-olioon.
Tuohan se ratkaisi ongelman, kiitos avusta.
Aihe on jo aika vanha, joten et voi enää vastata siihen.