Moi,
Formilla datagridview controlli jonka tietolähteenä taulu (table1) sql serveriltä.
table1:
CREATE TABLE [dbo].[Table1]( [id] [int] IDENTITY(1,1) NOT NULL, [nimi] [nvarchar](4) NOT NULL, [selitys] [nvarchar](100) NULL, CONSTRAINT [PK_Table1] PRIMARY KEY CLUSTERED ( [id] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY], CONSTRAINT [UK_table1_nimi] UNIQUE NONCLUSTERED ( [nimi] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY]
VB-koodi:
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'TODO: This line of code loads data into the 'Testi1DataSet.Table1' table. You can move, or remove it, as needed.
Me.Table1TableAdapter.Fill(Me.Testi1DataSet.Table1)
End Sub
Private Sub DGV1_RowValidating(sender As Object, e As DataGridViewCellCancelEventArgs) Handles DGV1.RowValidating
Dim dgv As DataGridView = sender
Dim row As DataGridViewRow = dgv.Rows(e.RowIndex)
Dim cell As DataGridViewCell = row.Cells(e.ColumnIndex)
If dgv.IsCurrentRowDirty Then 'jos riviä on muokattu
For Each c As DataGridViewCell In row.Cells 'käydään läpi jokainen rivin solu
If c.ColumnIndex = 1 Or c.ColumnIndex = 2 Then 'jos solun indexi on 1(nimi) tai 2(selitys)
If c.Value IsNot DBNull.Value Then 'jos solua ei ole jätetty tyhjäksi
c.Value = Trim(c.Value) 'trimmataan solu
End If
If c.ColumnIndex = 1 Then 'jos solun indexi on 1(nimi)
c.ErrorText = "" 'nollataan solun errortext
If c.Value Is DBNull.Value Or c.Value Is String.Empty Then 'jos solu on trimmauksen jälkeen tyhjä...
c.ErrorText = "nimi on tyhjä" 'asetetaan soluun virheteksti
e.Cancel = True 'keskeytetään tapahtuma
End If
End If
End If
Next
If row.Cells(1).ErrorText = "" Then 'jos nimessä ei ole virhettä, eli siihen on kirjoitettu jotain
Try 'yritetään...
Me.Table1BindingSource.EndEdit() 'tiedot datasettiin
Me.TableAdapterManager.UpdateAll(Me.Testi1DataSet) 'päivitetään tiedot tietokantaan
Catch ex As Exception 'jos ei onnistu
row.Cells(1).ErrorText = ex.Message 'kerrotaan ongelma rivin virheviestissä
e.Cancel = True '...ja perutaan tapahtuma
End Try
End If
End If
End Sub
End ClassHaluaisin, että käyttäjä ei voisi poistua riviltä ennen kuin on antanut siihen kelvolliset tiedot: eli nimi on max neljä merkkiä pitkä ja uniikki, ja että, rivin tieto tallentuisi tietokantaan heti kun riviltä poistutaan. Lisäksi nimestä pitäisi trimmata tyhjät merkit edestä ja takaa (jonka nyt tekee vb).
Toki voisin hoitaa nuo pituus- ja uniikkitarkistukset itse koodissa, mutta kun sql serveri nyt tekee sen kuitenkin, niin haluan käyttää sitä. (Voihan olla että joskus haluan vaihtaa esim. pituusrajoitusta vaikka 5 merkkiin.)
Ongelma on nyt ainakin se, että jos nimi ei ole kelvollinen ja käyttäjä on klikannut rivillä jotain toista kenttää tai painanut enteriä, ja painaa sen jälkeen esciä, voi hän sirtyä toiselle riville vaikka nimi ei ole kelvollinen.
Joitain "toimivia" kötöstyksiä olen saanut aikaan mutta koodi on ollut sellaista sillisalaattia etten tajua siitä itsekään mitään jälkikäteen ;).
Mikä eteen? Kuinka tämä tehdään niinkuin oikeasti?
ari kood kirjoitti:
Toki voisin hoitaa nuo pituus- ja uniikkitarkistukset itse koodissa, mutta kun sql serveri nyt tekee sen kuitenkin, niin haluan käyttää sitä. (Voihan olla että joskus haluan vaihtaa esim. pituusrajoitusta vaikka 5 merkkiin.)
Ottamatta muuhun kantaa, mutta eihän sinun kannata koodissa tarkistaa "onko teksti pidempi kuin 4 merkkiä", vaan "onko teksti pidempi kuin tietokantaan määritelty maksimipituus".
Grez, toki tapauskohtaisesti voidaan haluta validoida eri määrein, mitä kanta sallii. Jos samaa kenttää käytetään eri määrein, eri tarkoituksissa. Vaikka nyt sitten postinumerokenttä, jossa suomessa on eri määreet, kuin vaikka ulkomaalaisessa postinumerossa ja kantaan asetetaan maksimiksi suurimman mahdollisen postinumeron mukainen arvo.
Yleisesti tuosta ylläolevasta, mikäli annat kannan suorittaa validoinnin, saat takaisin vain transaktiovirheen. Eli kanta ei varsinaisesti "validoi" mitään, kanta palauttaa virheen. toki voit näyttää itse virhettä lomakkeellasi, mutta lähtökohtaisesti parempi tapa on käyttää erillistä luokkaa itse datalle, ja asettaa propertyille omat validointiattribuutit. Näin estät a) virheellisen tiedon tallentamisen yrityksen b) saat kenttäkohtaisesti tiedon epäonnistuneesta validoinnista. Mikäli otat virheen transaktiossa, palautuu virhe taulussa olevan sarakkeen mukaan ja käyttäjälle taatusti epäselvänä. Tämäkin tietysti on ok, jos käyttäjät ovat sen verran valveutuneita että tietävät mitä tarkoittaa vaikka palautunut virhe "Violation of UNIQUE KEY constraint 'UQ__osastot__AB6E61641273C1CD'. Cannot insert duplicate key in object 'dbo.osastot'."
groovyb kirjoitti:
Grez, toki tapauskohtaisesti voidaan haluta validoida eri määrein, mitä kanta sallii.
Tottakai, mutta kommenttini koski tuota lainaamaani alkuperäisen kirjoittajan pohdintaa että haluaisi pystyä muuttamaan määritystä nimenomaan kannan määrityksiä muuttamalla.
Ok, onko ratkaisu siis tarkistaa kelvollisuus koodissa?
Haluaisin kuitenkin tietää, onko purkkaviritetty koodi ainoa tapa estää käyttäjää siirtymään toiselle riville, vai onko tähän olemassa jokin "oikea" keino?
Tähän on ihan oikeakin keino. Datagridview:n tapauksessa, oikea tapa on hanskata validointi CellValidating -eventissä.
How to: Validate Data in the Windows Forms DataGridView Control
Linkin ohje on hyvin pelkistetty, mutta ajatus varmasti aukeaa tuota kautta.
Kiitos vastauskesta Groovyb, mutta olen kyllä tuotakin jo yrittänyt, ja paljon muitakin vaihtoehtoja. Onko sillä edes väliä tässä tapauksessa validoidaanko solun tieto RowValidating- vai CellValidating-eventissä?
Ongelmani tulee CellValidating eventissä olemaan sama, eli käyttäjä voi siirtyä toiseen soluun esc näppäimen painamisen jälkeen vaikka solun tieto ei olisi validi.
Toki voisin ehkä jotenkin estää esc näppäimen käytön, mutta silloinhan se olisi se ei haluttu purkkaviritys. Ellei sitten juuri esc-näppäimen estäminen ole se "oikea keino".
Mikäs sen esc-näppäimen painamisen funktio oikeastaan on? Voisi kuvitella (en jaksa testata) että "en haluakaan tallentaa tätä riviä".
Mielestäni ei todellisuudessa ole, eikä pitäisikään olla mitään keinoa estää käyttäjää poistumasta jostain solusta ennen kuin siihen on syötetty "validi tieto". Viimeisenä oljenkortena käyttäjä voi vaikka poistua solusta sammuttamalla tietokoneen.
Itseäni ainakin ottaisi aika suunnattomasti päähän jos käyttöön tulisi ohjelma jolla johonkin soluun eksymällä saisi lukittua esim. koko koneen tai edes ko. sovelluksen.
Grez kirjoitti:
Mikäs sen esc-näppäimen painamisen funktio oikeastaan on? ...
Hyvä huomio. Hauluaisinkin esc-näppäimen toimivan tuossa tehtävässä, mutta tässä tapauksessa olen virheellisesti alkanut syyttämään esc näppäintä ongelmastani joka oikeasti onkin nyt se, että;
Jos käyttäjä on kirjoittanut soluun duplikaatin, ja saanut siitä virheilmoituksen, pääse hän esc-näppäimen painamisen jälkeen siirtymään toiselle riville, ja saa nyt virheilmoituksen myös tästä solusta jos tekee siihen muutoksia, vaikka muutos olisi validi.
Usein ei ole hyvä käyttöliittymä, että käyttäjän kirjoittamat muutokset tallentuvat suoraan kantaan ilman erillistä "tallenna" painallusta, tai sitten olisi hyvä olla "undo" -toiminto.
Tuollaiset tiedot, jotka käyttäjä on syöttänyt, mutta joita ei ole vielä tallennettu, olisi hyvä korostaa esim. jollain värillä. Virheelliset vielä erityisemmin.
Tässä tapauksessa se kuitenkin on hyvä ratkaisu.
voithan toki validoinnin yhteydessä asettaa kaikkien rivien solut read only -tilaan, lukuunottamatta juuri muokattua. Näin muokattavaksi jäisi vain ko. rivi. Jos validointi menee läpi, poistat read only -tilat.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.Table1TableAdapter.Fill(Me.Testi1DataSet.Table1)
DataGridViewTextBoxColumn2.MaxInputLength = Testi1DataSet.Table1.Columns("nimi").MaxLength
End Sub
Private Sub Table1DataGridView_RowValidating(sender As Object, e As DataGridViewCellCancelEventArgs) Handles Table1DataGridView.RowValidating
Dim dgv As DataGridView = sender
Dim row As DataGridViewRow = dgv.CurrentRow
Dim cell As DataGridViewCell = row.Cells(e.ColumnIndex)
If dgv.IsCurrentRowDirty Or row.ErrorText <> "" Then
Try
row.ErrorText = ""
cell.Value = Trim(cell.Value)
If cell.Value = "" Then
Throw New Exception
End If
Me.Table1BindingSource.EndEdit()
Me.TableAdapterManager.UpdateAll(Me.Testi1DataSet)
Catch ex As Exception
row.ErrorText = ex.Message
End Try
End If
If row.ErrorText <> "" Then
e.Cancel = True
End If
End SubVihdoin sain aikaan tuollaisen. Näyttäisi näin äkkiseltään toimivan kuten halusinkin. Maksimipituus rajoitettu kuten Grez tuossa aiemmin vinkkasi. Virheilmoitukset tosin pitää vielä säätää käyttäjäystävällisemmäksi, mutta se ei ole ongelma.
Aihe on jo aika vanha, joten et voi enää vastata siihen.