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 Class
Haluaisin, 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 Sub
Vihdoin 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.