Modifikace DataGridView, když napojení je OleDB   zodpovězená otázka

VB.NET

Zdravím,

nevím za jak dlouho mně ukamenujete díky mým dotazům, ale opět mám nemalý problém. Zkouším si udělat jednoduchou databázovou aplikaci na seznam majitelů využívající MS Access ve VB.NET 2005 EE.

Prozatím mi šlo vše bez problému tj. zobrazovat data, editovat data apod. přes "moje prvky" např. textová pola kam se načte údaj a pomocí tlačítka si vyberu akci.

Jenže jsem dorazil k drobnosti jako je DataGridView. Data do něj bez problému načtu, ovšem nedokážu za žádnou cenu již data modifokovat/mazat eventuelně přidávat přímo v DataGridView.

Na netu jsem našel doslova stovky příkladů, ale všechny využívají ADO.NET (např. tento je hezký: http://searchsqlserver.techtarget.com/ge...,295582,sid87_gci1188427,00.html ) Já ovšem používám externí Databázi (tj. nechci ji začlenit přímo do programu) a navíc přistupuji k datům přes OleDB.

Protože již včerejšího večera se s tím drbu a hledám a nic nenacházím, je nějaká možnost, aby se zacházelo s DataGridViewem i pomocí OleDB? (plnění mi jde bez problémů) Případně je možné i využít BindingNavigator?

Pro výběr prvku používám tento kód (viz. níže) ale ostatní operace už nezvládám.

Soubor  modPath.vb
~~~~~~~~~~~~~~~~~~~~

Module modPath

    Public Function LocationDataFile(ByVal soubor As String) As String
        soubor = Application.StartupPath & "/../../Data files/" & soubor
        Return soubor
    End Function

End Module


Soubor  frmShowMajitele.vb
~~~~~~~~~~~~~~~~~~~~~~~~~~~

'Importování kolekce pro OleDB
Imports System.Data.OleDb

Public Class frmShowMajitele

    'Veřejně pro třídu deklarujeme cestu k DB a spojení
    '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Dim PwdCon As String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & modPath.LocationDataFile("majitele.mdb") & "; Jet OLEDB:Database Password=mojeTajneHeslo;"
    Dim oledbcon As New OleDbConnection(PwdCon)

    Private Sub frmShowMajitele_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        'Naplnění seznamu majitelů
        '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        Try
            oledbcon.Open()

            Dim strSQL As String = "SELECT * FROM majitele"
            Dim cmd As OleDbCommand = New OleDbCommand(strSQL, oledbcon)
            Dim objread As OleDbDataReader
            objread = cmd.ExecuteReader

            'Naplnění DataGridView1 - havička
            Me.dgvMajitele.Columns.Add("IDM", "IDM")
            Me.dgvMajitele.Columns.Add("titul_pred", "Titul před")
            Me.dgvMajitele.Columns.Add("jmeno", "Jméno")
            Me.dgvMajitele.Columns.Add("prijmeni", "Příjmení")
            Me.dgvMajitele.Columns.Add("titul_za", "Titul za")
            Me.dgvMajitele.Columns.Add("ulice", "Ulice")
            Me.dgvMajitele.Columns.Add("mesto", "Město")
            Me.dgvMajitele.Columns.Add("psc", "PSČ")
            Me.dgvMajitele.Columns.Add("stat", "Stát / Země")
            Me.dgvMajitele.Columns.Add("mail", "E-Mail")
            Me.dgvMajitele.Columns.Add("telefon", "Telefon")

            While objread.Read
                'Naplnění DataGridView1 - data
                Me.dgvMajitele.Rows.Add(objread("IDM"), objread("titul_pred"), objread("jmeno"), objread("prijmeni"), objread("titul_za"), objread("ulice"), objread("mesto"), objread("psc"), objread("stat"), objread("email"), objread("telefon"))
            End While
            objread.Close()
        Catch errorVariable As Exception
            MsgBox(errorVariable.ToString(), MsgBoxStyle.Critical, "Chyba programu")
        Finally
            oledbcon.Close()
        End Try
    End Sub

End Class

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Ať to upřesním, mám k dispozici tento kód, ale za žádnou cenu se mi nedaří rozchodit. Neřve totiž žádnou chybu, ale data se neuloží...

Public Class frmManual
    Dim dcManual As New OleDb.OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\xml\majitele.mdb")
    Dim daManual As OleDb.OleDbDataAdapter
    Dim dsManual As New DataSet
    Dim strSQL As String

    Private Sub frmManual_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        strSQL = "select * from majitele"
        Try
            daManual = New OleDb.OleDbDataAdapter(strSQL, dcManual)
            daManual.Fill(dsManual, "majitele")
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try
        Me.dgManual.DataSource = dsManual.Tables("majitele")
    End Sub

    Private Sub cmdUpdate_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdUpdate.Click
        Dim ans As String
        If (dsManual.HasChanges) Then
            ans = MsgBox("Changes have occurred!" & vbCrLf & "Do you wish to update?", MsgBoxStyle.YesNo + MsgBoxStyle.Information, "Updates")
            If ans = vbYes Then
                Try
                    dsManual.AcceptChanges()
                    daManual.Update(dsManual.Tables("majitele"))
                    MsgBox("Table updated!", MsgBoxStyle.Critical, "majitele")
                Catch exManual As Exception
                    MsgBox(exManual.Message)
                    Throw exManual
                End Try
            End If
        End If
    End Sub
End Class

nahlásit spamnahlásit spam 0 odpovědětodpovědět

To vypada, ze se data ulozi do datasetu ale nedostanou se zpet do databaze. . Nezkousel jste data manualne vkladat zpet pres dalsi sql prikaz?

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Pokud jsem dobře pochopil Vaši odpoveď, pokud data "natvrdo" vložím přes SQL dotaz INSERT INTO... a hodnoty beru z textových polí, comboboxů apod., tak je vše bez problému. Akorát DataGridView mně zlobí. Pokud data "natvrdo" uložím v DB, tak se mi bez problému ukážou v DataGridView.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Je totiz otazkou, zda si dokaze program zjistit z "select * from tabulka" jak data zpet zapsat.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Bohužel nevím, jak bych to mohl zjistit.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Ono to již z principu nemůže fungovat, protože data do DataGridView načítáte po řádcích a tabulka nemá možnost zjistit, odkud se data vzala.

Správný kód je takto:

Imports System.Data.OleDb

Public Class Form1

    Private da As OleDbDataAdapter
    Private dt As DataTable = New DataTable     'tabulka s daty v paměti

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
        'nastavit připojení k databázi a SQL příkaz
        Dim strConn As String = "Provider = Microsoft.Jet.OLEDB.4.0; Data Source = C:\Nwind.mdb;"
        Dim strQuery As String = "Select * from Products"
        Dim conn As OleDbConnection = New OleDbConnection(strConn)
        'pomocí DataAdapteru nahrát data do DataTable
        da = New OleDbDataAdapter(strQuery, conn)
        da.Fill(dt)
        'nastavit DataTable jako zdroj dat
        DataGridView1.DataSource = dt
    End Sub

    Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As FormClosingEventArgs) Handles Me.FormClosing
        'uložit změny do databáze (musí se vytvořit SQL příkazy)
        Dim cmd As OleDbCommandBuilder = New OleDbCommandBuilder(da)
        da.Update(dt)
        dt.AcceptChanges()
    End Sub

End Class

Ještě několik základních pojmů - DataTable je virtuální reprezentace výsledku SQL dotazu v paměti, de facto tabulka. Ale už v paměti v aplikaci, ne v databázi. Tu můžeme metodou Fill "nalít" do komponent, třeba do DataGridView. DataAdapter je objekt, který zprostředkovává komunikaci mezi databází a DataTable. Právě tento objekt načítá data z databáze a pomocí automaticky vygenerovaných příkazů je vrací zpět.

Jinak, nikdo vás kamenovat nebude, od toho tady fórum je, abyste se mohli zeptat.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Máš to velice pěkně vysvětlený, měl bych k tomu menší dotaz. Když jsem ještě konektoval databáze ve VB6 přes recordset, otevíral jsem rs jen na dobu nezbytně nutnou. Jak je to u VB.NET a OLE při konektivitě databáze z více míst, v kterém místě se otevírá databáze a zamyká pro ostatní uživatele, je zde možné nějaké read only? Nevím jestli pro tento dotaz založit nové téma?

Děkuji

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Super kod! Diky, tohle jsem presne potreboval :-)

nahlásit spamnahlásit spam 0 odpovědětodpovědět

dobrý den, nevím čím to je, ale kdyz pouziji váš kód tak mi to u příkazu "da.Update(dt)" vyhodí hlášku "Dynamic SQL generation for the UpdateCommand is not supported against a SelectCommand that does not return any key column information" , uz nad tím bádám týden a netuším čím to může být....

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Přeji dobrý večer.

procházím Váš web a snažím se najít odpověď na zřejmě banální problém který právě řeším. Jak vidno nejsem sám, protože kolega výše řešil naprosto stejný problém a velmi mne zaujala Vaše reakce - správný kód. Nechci příliš zdržovat ale vše chápu až na poznámku "uložit změny do databáze (musí se vytvořit SQL příkazy)" mohl byste prosím z mého pohledu tuto důležitou poznámku trošku více rozvést ve smyslu co je tím myšleno, co se za tím skrývá a jaké SQL příkazy máte na mysli?, respektive co je nutné vytvořit? Hodně by mi to pomohlo. Zbytek kódu chápu.

Děkuji předem za ochotu popostrčit.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Objekt OleDbDataAdapter (v příkladu da) jak je uvedeno výše disponuje metodou Fill pro "nalití" hodnot z podkladové SQL databáze do DataSetu resp. DataTable. Ono nalití není nic jiného než provedení SQL dotazu SELECT. Tento select je uložen ve vlastnosti SelectCommand objektu da a je inicializován voláním konstruktoru da = New OleDbDataAdapter(strQuery, conn).

Pro promítnutí změn z DataSetu (potažmo tedy z Vašeho DataGridView) zpět do podkladové databáze má OleDbDataAdapter metodu Update. Tato metoda potřebuje pro svoji činnost mít inicializovány vlastnosti InsertCommand, UpdateCommand a DeleteCommand objektu OleDbDataAdapter pomocí nichž vlastní změny provádí. Tyto vlastnosti je možno inicializovat "ručně" - obdobně jako pro SelectCommand vytvořením objektů OleDbCommand s vlastními SQL příkazy (INSERT, UPDATE, DELETE) a jejich přiřazením příslušným vlastnostem objektu OleDb DataAdapter. V příkladu Tomáše Hercega je použit tzv OleDbCommandBuilder, což je objekt, který prostým zavoláním svého konstruktoru (s parametrem da) "automaticky" (jaksi "potichu" a často jinak, než chcete) vytvoří příslušné SQL příkazy a přiřadí je vlastnostem InsertCommand, UpdateCommand a DeleteCommand objektu OleDbDataAdapter.

Pak je teprve možno volat metodu Update.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Pane, díky za skvělé vysvětlení.

nechápal jsem k čemu tam je ten New OleDbCommandBuilder(da).

Teď už je to jasné. Nicméně přeci jen bych dal přednost té ruční metodě. Jasně má to asi někdy výhodu a někdy ne. Vzhledem k tomu že nepotřebuju s daty nějak dál pracovat (jen uložit změny do podkladové mdb :-) by mi i tohle s tím CommandBuilderem stačilo, kdyby mi to fungovalo. Všechno proběhne ok, žádnou chybu to nehlásí ale když se mdb znovu otevře, data tam ne a nejsou

přiložím kousek kódu, snad ta chyba kterou zatím bohužel nevidím z toho bude patrnější...

Dim cmd As OleDbCommandBuilder = New OleDbCommandBuilder(da)

        Dim command As OleDbCommand
        command = New OleDbCommand("INSERT INTO Kolo (ID, Day, Distance, Time, Pulse, AvgSpeed, Memo) " & _
                                   "VALUES (?, ?, ?, ?, ?, ?, ?);", conn)

        da.InsertCommand = command

        da.Update(dt)
        dt.AcceptChanges()

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Ve Vaší ukázce kombinujete "automatické" generování SQL příkazů pomocí OleDbCommandBuilderu a "ruční" nastavení příkazu pro INSERT. To určitě lze, ale plnou kontrolu máte jen nad operacemi, které používají příkaz INSERT, tedy při vložení nových řádků do mřížky potažmo tabulky. Zkuste se při krokování programu podívat jaké příkazy vygeneroval OleDbCommandBuilder a přiřadil je vlastnostem da.UpdateCommand a da.DeleteCommand.

Níže přikládám kus kódu pro řešení podobného problému, který mi funguje. Používá sice jmenný prostor SQLClient pro práci s MS SQL Serverem, ale metody a principy by měly být stejné (je tam trochu jiná syntaxe pro předávání parametrů).

Mezi objekt DataTable mám vložen ještě objekt BindingSource, který lze s výhodou použít pro filtrování a řazení dat v mřížce

Imports System.Data.SqlClient
Imports System.Windows.Forms
Imports System.IO
Imports System.Diagnostics

Public Class frmMain
  Private dtAkce As DataTable
  Private daAkce As SqlDataAdapter
  Private dsDostihy As DataSet
  Private cbAkce As SqlCommandBuilder
  Private WithEvents bsAkce As BindingSource
  Private cmdPom As SqlCommand

  Private Sub Main_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    Try
      con = New SqlConnection(My.Settings.Default.ConnnectionString)
      con.Open()                                        'Otevreni spojeni na databazi
    Catch exc As SqlException
      MessageBox.Show(exc.Number & vbCrLf & exc.Message)
      Exit Sub
    Catch oexc As Exception
      MessageBox.Show(oexc.Message)
      Exit Sub
    End Try

    dsDostihy = New DataSet("Dostihy")

    cmdAkce = New SqlCommand(cmdAkceText, con)        'Definice Selectu pro data adapter Akce
    daAkce = New SqlDataAdapter(cmdAkce)              'Inicializace datoveho adapteru
    dtAkce = New DataTable("Akce")                    'Inicializace datove tabulky dtAkce
    dsDostihy.Tables.Add(dtAkce)                      'Pridani datove tabulky dtAkce do datasetu
    daAkce.MissingSchemaAction = MissingSchemaAction.AddWithKey  'Zaridi, ze do schematu v Datasetu se pridaji informace o klicich z podkladove DB
    daAkce.Fill(dtAkce)                               'Naplneni tabulky dtAkce z podkladove databaze
    cbAkce = New SqlCommandBuilder(daAkce)            'Automaticke vygenerovani SQL prikazu pro Update, Insert a Delete

    'ruční vytvoření příkazu pro DELETE
    cmdPom = New SqlCommand("DELETE FROM [Akce] WHERE [AKC_ID] = @p1", con) 
    parPom = New SqlParameter("@p1", SqlDbType.Int, 0, ParameterDirection.Input, False, 0, 0, "AKC_ID", DataRowVersion.Current, Nothing)
    cmdPom.Parameters.Add(parPom)
    daAkce.DeleteCommand = cmdPom

    'ruční vytvoření příkazu pro UPDATE
    cmdPom = New SqlCommand("UPDATE [Akce] SET [AKC_DatumKonani] = @p1, [ZAV_ID] = @p2, [AKC_Nazev] = @p3 WHERE [AKC_ID] = @p4", con)
    parPom = New SqlParameter("@p1", SqlDbType.DateTime, 0, ParameterDirection.Input, False, 0, 0, "AKC_DatumKonani", DataRowVersion.Current, Nothing)
    cmdPom.Parameters.Add(parPom)
    parPom = New SqlParameter("@p2", SqlDbType.Int, 0, ParameterDirection.Input, False, 0, 0, "ZAV_ID", DataRowVersion.Current, Nothing)
    cmdPom.Parameters.Add(parPom)
    parPom = New SqlParameter("@p3", SqlDbType.NVarChar, 0, ParameterDirection.Input, False, 0, 0, "AKC_Nazev", DataRowVersion.Current, Nothing)
    cmdPom.Parameters.Add(parPom)
    parPom = New SqlParameter("@p4", SqlDbType.Int, 0, ParameterDirection.Input, False, 0, 0, "AKC_ID", DataRowVersion.Current, Nothing)
    cmdPom.Parameters.Add(parPom)
    daAkce.UpdateCommand = cmdPom
    dgwAkce.AutoGenerateColumns = False

    bsAkce = New BindingSource(dsDostihy, "Akce")               'Inicializace vazebniho prvku pro tabulku Akce
    dgwAkce.DataSource = bsAkce                                 'Inicializace datove mrizky pro tabulku Akce

'obecna procedura pro ulozeni jakekoli tabulky z datasetu
  Private Sub UlozTabulku(ByRef dtPom As DataTable, ByRef daPom As SqlDataAdapter)
    BindingContext(dtPom).EndCurrentEdit()
    Dim dt As DataTable = dtPom.GetChanges               'nacteni zmenenych radku do pomocne data table
    If Not dt Is Nothing Then
      Try
        daPom.Update(dt)
        dtPom.AcceptChanges()
      Catch exc As SqlException
        MessageBox.Show(exc.Number & vbCrLf & exc.Message)
        Exit Sub
      Catch oexc As Exception
        MessageBox.Show(oexc.Message)
        Exit Sub
      End Try
    End If
  End Sub

  Private Sub bsAkce_CurrentChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles bsAkce.CurrentChanged
    UlozTabulku(dtAkce, daAkce)
  End Sub

  Private Sub btnLoad_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLoad.Click
    daAkce.Fill(dtAkce)
  End Sub

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.
  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:
Heslo:

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback