DS.Tab[].Clear nie czysci kontrolek wiazanych DataBinds.Add

0

Witam,
Mam problem z wyszukiwaniem danych pobieranych za tablicy Access na formularzu gdy zapytanie wykonuję powturnie nie zamykając formularza. Przy pierwszym otwarciu formularza podane zapytanie wykonuje się poprawnie, natomiast na tym samym formularzu czyszcząc pola wprowadzania danych i wprowadzeniu nowej szukanej wartości w odpowiedzi otrzymuję pierwszy odnaleziony rekord z poprzedniego zapytania. Myślę, że problem tkwi w wyczyszczeniu wcześniej pobranych danych do tablicy ds.tables[0].
Wywołując metodę "ds.tables[0].Clear()", "ds.tables[0].Dispose()" przed wykonaniem kolejnego zapytania tablica nie czyści się. Nie potrafię jej również zamknąć. Do tej pory muszę zamykać formularz i ponownie go otwierać, by uzyskać odpowiedź. Może jakaś podpowiedź ??? [???]

0

Problem tyczy sie tylko li tego jednego wiersza? zawsze jest to pierwszy lub ostatni wiersz? Sprawdz czy przypadkiem nie jest to wiersz o indeksie zgodnym NEWROWINDEX czy jakos tak.. grid moze miec wlaczony tryb dodawania wierszy i wtedy jeden z nich NIE bedzie powiazany ze zrodlem danych na twardo, tylko bedzie takim sobie edycyjnym lekkim wierszem, trzymajacym wpisane/wstawione dane tak dlugo az sie go zatwierdzi(enter)/anuluje(escape) - mozna to tez zrobic programowo. Zachowanie ktore opisujesz jest dosc podobne do tego czego bym sie spodziewal po obecnosci newrow'a ktorego Ty bys przypadkiem jakos recznie z poziomu kodu wypelnil wartosciami.. skoro jednak mowisz o ds.tables - to uzywasz dataset/datatable, wiec pewnie bindingow rowniez -- a bindingi NIGDY niczego nie wstawiaja/pobieraja do newrow.. on jest dla nich teoretycznie niewidoczny.. hhhhhhhm.. chociaz kiedys pamietam ze byl jakis zgrzyt z wartosciami w newrowidx w momencie gdy sie z grida zbindowane wiersze wywalalo inaczej niz odlaczeniem datasource'a..

nie wiem. opisz wiecej albo pokaz kluczowe fragmenty kodu [te decydujace o obsludze wierszy grida, wiazaniu, ladowaniu i czyszczeniu danych]

0

Wielokrotne użycie metody Szukanie() powoduje opisany problem. Nie używam grida tylko textBoxów, które są czyszczone przed wykonaniem kolejnego zapytania

public void Szukanie()
{
try
    {
    OleDbCommand command = oleDbConnection1.CreateCommand();
    command.CommandType = CommandType.Text;
    command.CommandText = "SELECT * FROM [ŁD Analogowe_8101] WHERE Typ like '%" + textBox1.Text + "%' and Symbol like '%" + textBox3.Text + "%' and Numer like '%" + textBox3.Text + "%' and Numer like '%" + textBox4.Text + "%' and Inicjaly like '%" + textBox5.Text + "%' and Podstawa Like '%" + textBox6.Text + "%' and Relacja like '%" + textBox8.Text + "%' and [Nazwa Klienta] like '%" + textBox9.Text + "%' and [Segment Klienta] like '%" + comboBox1.Text + "%' and [Rodzaj lacza] like '" + comboBox2.Text + "%' and [Login mod] like '%" + label10.Text + "%' and [Data mod] like '%" + label11.Text + "%'";
    OleDbDataAdapter da = new OleDbDataAdapter();
    da.SelectCommand = command;
    da.TableMappings.Add("Table","[ŁD Analogowe_8101]");
    ds = new DataSet();
    ds.Clear();
    da.Fill(ds, "[ŁD Analogowe_8101]");
    DataTable dataTable = ds.Tables[0];
    statusBar1.Text = "Znaleziono Rekordów: " + ds.Tables[0].Rows.Count.ToString();
    oleDbConnection1.Open();
    OleDbDataReader reader;
    reader = command.ExecuteReader();
    if (reader.Read())
    {
        textBox1.Text = (string)reader["Typ"].ToString();
        textBox2.Text = (string)reader["Symbol"].ToString();
        textBox3.Text = (string)reader["Numer"].ToString();
        textBox4.Text = (string)reader["Numer"].ToString();
        textBox5.Text = (string)reader["Inicjaly"].ToString();
        textBox6.Text = (string)reader["Podstawa"].ToString();
        textBox7.Text = (string)reader["Data"].ToString();
        textBox8.Text = (string)reader["Relacja"].ToString();
        textBox9.Text = (string)reader["Nazwa Klienta"].ToString();
        comboBox1.Text = (string)reader["Segment Klienta"].ToString();
        comboBox2.Text = (string)reader["Rodzaj lacza"].ToString();
        label10.Text = (string)reader["Login mod"].ToString();
        label11.Text = (string)reader["Data mod"].ToString();
        label15.Text = (string)reader["Identyfikator"].ToString();
    }
    else
    {
    statusBar1.Text = "Nie znaleziono szukanych danych";
    }
    oleDbConnection1.Close();
    }
    catch
    {
    statusBar1.Text = "Błąd połączenia z bazą";
    }
    try
    {
        BindControl(); //uzupełnianie textBox4 i textBox5 zerami do czterech znaków
        textBox1.DataBindings.Add("Text", ds.Tables[0], "Typ");
        textBox2.DataBindings.Add("Text", ds.Tables[0], "Symbol");
        textBox5.DataBindings.Add("Text", ds.Tables[0], "Inicjaly");
        textBox6.DataBindings.Add("Text", ds.Tables[0], "Podstawa");
        textBox7.DataBindings.Add("Text", ds.Tables[0], "Data");
        textBox8.DataBindings.Add("Text", ds.Tables[0], "Relacja");
        textBox9.DataBindings.Add("Text", ds.Tables[0], "Nazwa Klienta");
        comboBox1.DataBindings.Add("Text",ds.Tables[0], "Segment Klienta");
        comboBox2.DataBindings.Add("Text",ds.Tables[0], "Rodzaj lacza");
        label10.DataBindings.Add("Text",ds.Tables[0], "Login mod");
        label11.DataBindings.Add("Text",ds.Tables[0], "Data mod");
        label15.DataBindings.Add("Text",ds.Tables[0], "Identyfikator");
    }
    catch
    {
    }
    oleDbConnection1.Close();
}
0

1' OleDbCommand obsluguje cos takiego jak Parameters, wiec NIE SKLEJAJ zapytania recznie, tylko umiesc w zapytaniu znaczniki "?" i uzywaj Parameters do przekazywania 'wklejek'.. dzieki temu nie bedziesz mial nigdy problemu, ze ktos Ci wpisze w textbox1 napis " '; drop table [ŁD Analogowe_8101]; --"
http://msdn.microsoft.com/en-us/library/system.data.oledb.oledbcommand.parameters(VS.71).aspx

2' czemu jednoczesnie recznie wypelniasz textboxy w bloku if-reader.read() oraz pozniej w try-catchu? bindingi sa dwustronne! one takze beda czytac z tabeli i wrzucac dane do kontrolek.. czym jest owo BindControl?

3' tworzysz nowy DataSet i zaraz potem wolasz na nim Clear. po co? przeciez swiezy DataSet i tak jest kompletnie pusty! Clear'owanie go jest bezsensu

4' to nie jest dobry pomysl, zeby dla tego zapytania ktore wysylasz uzywac obiektu DataSet do trzymania wynikow. kompletnie wystarczy Ci zwykle pojedyncze DataTable

5' to nie jest dobry pomysl, zeby za kazdym odpaleniem metody szukajacej dodawac ponownie databindingi. to jest paskudny pomysl. nie pamietam, ale albo bedziesz dostawac wyjatki (stad ten trycatch?) albo bedziesz mial wielokrotne bindy do tego samego, co by bylo jeszcze gorsze.. databindy powinny byc zdefiniowane (czytaj: dodane) JEDEN RAZ, np. w konstruktorze zaraz po InitializeCompoennt() albo np. w Form_Load itp. jezeli faktycznie chcesz je 'przepiac' na nowo, powinienes wpierw wykonac DataBindings.Clear zeby POZBYC SIE STARYCH..

i teraz po tym, zastanow sie:

  • uzywasz DataSet, ktory jest podwojnie czysty z sila wodospadu, dzieki swojej swiezosci (new) i recznej polerce (Clear). za kazdym wywolaniem Twojej 'duzej' metody, dostajesz NOWY obiekt cache'a DataSet z NOWYMI obiektami tablic DataTable
  • jezeli (a tak sadze) ponowna proba dodania bindingu wywoluje wyjatek, to u Ciebie ten kod wykona sie tylko raz, a potem bedzie przeskakiwany dzieki wygaszaniu wyjatkow w trycatch
  • to oznacza, ze bindingi dodadza sie TYLKO RAZ
  • to oznacza, ze drugie, trzecie, czwarte, N-te wywolanie metody bedzie co prawda tworzyc nowe DataSety i wewnatrz nich nowe DataTable'e, ale kod DataBindings.Add bedzie przeskakiwany
  • DataBindings.Add wykona sie tylko dla w ogóle-pierwszego wywolania Twojej duzej metody na tej konkretnej instancji formatki
  • to oznacza, ze DataBindings.Add DODA TYLKO I WYLACZNIE JEDEN binding do DataTable pochodzacego z PIERWSZEGO utworzonego DataSet/DataTable, zas nastepne zostana ot, przeskoczone
  • i ten wlasnie databinding do pierwszego DS/DT bedzie zyl od tej pory az do konca zycia formatki, i bedzie pamietal na ds.tables[0] odnoszace sie do pierwszego najwczesniejszego dataseta, i bedzie je pamietal "na zawsze", czyli az zamkniesz formatke kiedy to textboxy zostana zniszczone (i przez to binding tez wyparuje w koncu)

rozwiazan masz kilka:

  • przed DataBindings.Add, zrob DataBindings.Clear. to wyczysci stare bindingi, wiec proba dodania nowego podobnego nie rzuci wyjatkiem i ten-nowszy-binding zapamieta te-nowsza-datatable z tego-nowszego-dataset. no i wywalic reczne czytanie pol pare linii wyzej. po co je czytac dwa razy?
  • nie uzywac databindingow. ostatecznie, skoro pare linii wyzej recznie czytasz, nizej nie trzeba powtarzac..
  • uzywac databindingow poprawnie: w konstruktorze, albo w formload, wykonac RAZ owe DataBindings.Add dla kazdej kontrolki, a potem je zostawic w spokoju i tylko odswiezac im datasource'y na "nowsze wersje dataset/datatable"
  • uzywac databinfingow poprawnie(j): w DESIGNERZE upusc na formatke komponent DataSet. To Ciebie zapyta jaki 'podtyp' dataseta: wybierz UNTYPED. nazwij go jakos - np. dsCostam. NIGDY go nie niszcz ani nie wymieniaj na inny z poziomu kodu. Poloz na formatke komponent BindingSource, nazwij go bsAnalogowe, ustaw jego DataSource na dsCostam, ustaw jego DataMember na "ŁD Analogowe_8101". Ten DataSet i ten BindSrc powinny istniec zawsze, pochodza z desgnera i nie masz prawa ich recznie Dispose ani probowac podmieniac na nowe przez xyz=new XYZ(). Ale mozesz smialo je Clear'owac, ladowac, itp z poziomu kodu. Majac je, wywal z kodu wszystkie DataBindings.Add. Kazdemu textboxowi ktory to mial, znowu w Designerze, poprzez Properties owego texboxa dodaj databinding pola Text na DataSource=bsAnalogowe, Member="Inicjaly" czy co tam ten texbox mial przedstawiac. Jak juz poustawiasz te databindingi, to zostawiasz je w spokoju i nigdy nie ruszasz recznie z poziomu kodu. O ile DSet i BSource mozesz sobie czyscic i wymieniac im czesciowo konfiguracje czy dane, DataBindingow prowadzacych do BindingSource nigdy juz nie ruszasz, chyba ze masz NaprawdeBardzoWaznyPowod(TM). Od tej pory, chcac zaladowac dane na formatke, jedyne co musisz zrobic to wywolac Fill na DataSet'cie jakims zapytaniem sciagajacym tabele nazwana 'ŁD Analogowe...'. Gdy to zrobisz, DSet wygeneruje zdarzenie costamChanged ktore zostanie zauwazone przez BSource ktory natychmiast wyciagnie z niego DataMember='ld analog...' czyli odpowiedniego DataTable'a, wybierze jakis wiersz jako Current i z kolei sam wygeneruje swoje zdarzenie costamChanged. ono zostanie z kolei zauwazone przez databindingi, ktore odpytaja BSource o konkrente wartosci pol tego wiersza.. jedyne co Ty robisz, to Fill na DataSet "i juz". jak chcesz wywalic stare dane, Clear na DataSet i juz. Bindingi i BindingSource nie ruszony. BindingSource mozesz dotknac np. poprzez tymczasowa zmiane jego .DataSource z pierwotnego wyklikanego w designerze dataset'a na jakis dynamicznie utworzony chwilowy kompletnie nowy recznie utworzony datatable - np. jeslibys chcial zaprezentowac jakas tymczasowa liste wierszy ktorej NIE ma na bazie, ale ktora sobie na boku przygotowales. po skonczonej prezentacji, przywracasz BSourceowi jego DataSource=dsCostam, aby formatka wrocila do swojej pierwotnej funkcji. Ba, mozesz w ten sposob zrezygnowac z DATASET kompletnie. wywalasz go, databindingi zostawiasz aby pokazywaly na BSource, kiedys-tam w kodzie tworzysz new DataTable, wypelniasz ja poprzez Fill(dt) i wykonujesz BSource.DataSource=dt; i juz. PRAWIE ta sama funkcjonalnosc, ale bez DSet. (uwaga, wtedy bs.DataMember musi byc pusty, a nie ==nazwa tabeli). Roznica bedzie tkwila w tym, ze obiekty DataTable beda teraz zarzazane przez Ciebie, ty ich bedziesz pilnowac i Ty bedziesz decydowac ktore wyniki potrzymac jeszcze chwile a ktore wywalic juz teraz. Minus jest taki - ze nie bedziesz mogl skorzystac z DataSetowego automatycznego odswiezenia czy zapisania danych z powrotem do bazy. DataSet potrafi takie rezczy, DataTable - nie, ale skoro Ty recznie ladujesz dane do DataSeta metoda Fill to i tak z tych ficzerow DataSeta nie masz jak skorzystac wiec mozesz ronie dobrze calkiem z niego zrezygnowac.... Sorry, ale i tak duzo napisalem, nie chce mi sie jeszcze pisac JAK wszystko pozlepiac do kupy zeby z tych ficzerow skorzystac. Poczytaj MSDN, opisane wszystko jest w DataSet.SelectStatement/UpdateStatement..

Tak czy owak, zwroc uwage, ze ani w podejsciu przez BS+DS ani przez BS nigdy nie dotyka sie juz DataBindingow. Jak sie uprzesz, mozesz tez zrobic sobie podejscie bez BS, z samym DS, ale wtedy sadze ze tez databindingi da sie 'wyklikac' poprzez Designera i nigdy wiecej nie ruszac. Wszelkie wymiany zrodel danych (o ile w ogole sa jakiekolwiek, nie pomyl wymiany ŹRODLA danych z wymiana SAMYCH danych we zrodle) ucinane sa prawie zawsze na poziomie BindingSource -- stad jego nazwa zreszta i taki jego sens istnienia -- zas z punktu widzenia kontrolek nigdy NIC sie w wiazaniu danych nie zmienia.. chyba ze masz NaprawdeBardzoWaznyPowod(TM) ktorego Ci nie zycze trafic:)

pff.. to tyle.. mam nadzieje ze przynajmniej teraz zaczniesz dostrzegac dlaczego jest tyle roznych klas/komponentow i jak sie ich uzywa, zeby, paradokslanie, sie narobic mniej.

0

Dziękuję za obszerną wypowiedź,
Nawiązując do problemu odpowiadam
ad1. Dzięki za podpowiedź
ad2. textBoxy wypełniałem w bloku if(reader.read()) by wyświetlić znaleziony wynik, bindowanie miałobyć po to by można było się poruszać po wyniku jeżeli znaleziono więcej niż jedną odpowiedź. Tutaj zgodzę się że pierwszy blok jest zbędny, jednak nie mogę w prosty sposób przenieść bindowania do formloadera ponieważ przy starcie wyrzuca wyjątek o nie istniejącym obiekcie ds.tables[0], który tworzony jest dopiero przy zapytaniu. Formularz przy starcie ma się otwierać pusty, by użytkownik decydował czy wprowadza dane, czy ich szuka. BindControl() to metoda do uzupełniania textBoxów 3 i 4, które mają wyświetlać przydzielony kolejny numer w formacie 0000 0001 itd, czyli uzupełniać numer zerami z lewej strony do 8 znaków
private void IntToCurrencyString(object sender, ConvertEventArgs cevent)
{// The method converts only to string type. Test this using the DesiredType.
if(cevent.DesiredType != typeof(string)) return;
cevent.Value = ((int)cevent.Value).ToString("D8");
}
private void BindControl()
{// Creates the binding first. The OrderAmount is a Decimal type.
Binding b = new Binding("Text", ds.Tables[0], "Numer");
b.Format += new ConvertEventHandler(IntToCurrencyString);
label14.DataBindings.Add(b);
textBox3.Text = label14.Text.Remove(4,4);
textBox4.Text = label14.Text.Remove(0,4);
}
Ad3. Nadmierne czyszczenie DataSet, to moje próby pozbycia się problemu wielokrotnego zapytania, ale nie przyniosły rezultatu więc usunięto
Ad4. Nie rozumiem jak to zamienić na samo DataTable
Ad5. Jeśli możesz to napisz jak dodać Binding do konstruktora, lub Form_Loadu biorąc pod uwagę tworzenie się ds.Tables[0] dopiero w momęcie zapytania.
Dodam dodatkowo, że muszę pracować na VS2003 .NET i nie mam wszystkich komponentów dostępnych w nowszych wersjach Visual Studio (problem związany z pracą aplikacji na starszych wersjach WINDOWS_XP), więc nie mogę dodać componentu BindingSource, jak podałeś w dalszym opisie.
Metoda czyszczenia DataBindings.clear() przed wykonaniem kolejnego zapytania nie skutkuje.

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0