ListBoxy oraz ComboBoxy a ich value

0

Temat z początku wydawał mi się łatwy. Lecz im więcej na ten temat szukałem w necie i się nim bawiłem tym więcej nerwów mnie to kosztowało.

Może ktoś mi tu ładnie i miło wytłumaczy jak dynamicznie gdy mam liste obiektow w których mam zapisane id_obiektu oraz name_obiektu uzupelnic listbox/combox tymi obiektami tak aby wyswietlana byla ich nazwa a w value trzymane było podane przeze mnie id.

Wyczytałem i sprawdziłem ze:

  1. Gdy mam liste obiektow gdy jeden properties jest stringiem a drugi nie to jezeli podam ją jako datasource to string automatycznie ustawi się jako displaymember, a drugi properties jako value
    Oto kod:
 List<Insurer> insurers = new List<Insurer>();


            var ins_tmp = from i in db.tb_Insurers
                          select new Insurer( i.ID, i.Name );


            foreach (Insurer ins in ins_tmp)
            {
                insurers.Add(new Insurer(ins.ID, ins.Name.ToString()));

            }


            comboBox1.DataSource = insurers;

Oczywiscie nie działa:>
Tj. wyswietla mi nie nazwe insurera a :namespace.typobiektu

2).

Probowalem tez bardziej łopatologicznie:

List<Insurer> insurers = new List<Insurer>();


            var ins_tmp = from i in db.tb_Insurers
                          select new Insurer( i.ID, i.Name );


            foreach (Insurer ins in ins_tmp)
            {
                comboBox1.Items.Add(ins.Name.ToString());
                comboBox1.ValueMember = ins.ID.ToString();
                
            }

i tu w momencie gdy chce :

  label4.Text = comboBox1.SelectedValue.ToString();

Pojawia mi się error:
Object reference not set to an instance of an object.

tyle co odkryłem to , że valueMember chyba nie ma nic wspolnego z selectedValue bo gdy go wypisuje to po prostu wypisuje mi ID ostatnio dodanego insurera..

Za wszelkie rady / informacje na tematy które mogłem przeoczyć byłbym b. wdzięczny.

0

ale żeś nakrecil.. po co w tym pierwszym kodzie sciagasz obiekty z db.tb_Insurers jako "new Insurer", zeby potem foreachem znowu przeksztalcic je na inne "new Insurer" ? przeciez to co z linq dostaniesz to juz beda obiekty insurer!

calkowicie wystarczajace jest:

    comboBox1.DataSource = from i in db.tb_Insurers
        select new Insurer( i.ID, i.Name );

a co do comba:

  • jesli nie ustawisz ComboBox.DisplayMember, ComboBox bedzie wyswietlal "item".ToString() jako opis, stad namespace:object
  • wniosek: ustaw ComboBox.DisplayMember="Name", bo te wlasciwosc item'a z datasource chcesz wyswietla jako opis
  • jesli nie ustawisz ComboBox.ValueMember, to combo bedzie zwracalo w SelectedValue caly wybrany item - czyli wybrany obiekt Insurer
  • jesli ustawisz ValueMember na jakies pole, tutaj np. na ID, to combo bedzie zwracalo wartosc tego pola pobrana z wybranego obiektu
  • w momencie gdy NIE MA nic wybranego, combo zwraca selectedvalue=null oraz selectedvalue=-1. nie jestes w stanie ocenic po selectedvalue czy nic nie wybrano, czy ValueMember pokazywalo property obiektu ktore akurat zdarzylo sie miec wartosc null. do sprawdzania czy cos wybrano, uzywaj zawsze selectedindex!=-1 i dopiero wtedy dobieraj sie ewentualnie do SelectedValue. jesli wtedy SelectedValue bedzie null, to znaczy, ze wybrany item mial property o nazwie {ValueMember} nie ustawione i rowne null
0

Dzięki wielkie.
Tylko , że:
Nie bardzo działa :>

edit:
Podczas debugowania odkryłem , że po linijcie comboBox1.DisplayMember = "Name", wciąż jest == "", jakaś właściwość comboBoxa musze zmienic zeby móc to w ogóle zmienic, czy o co może chodzić?

0

ah.. no tak, nie powiedzialem..

idzie o to, ze z nie wiadomych blizej chyba juz nikomu powodow, ComboBox jest skonstruowany tak, ze zmiana DataSource [np. z null na Twoja kolekcje] czasem powoduje .... wyczyszczenie DisplayMember i ValueMember.. ktos tak w M$ wymyslil.. moze dlatego, ze zalozono ze bedzie sie do tego wykorzystywac BindingSource i DataSource comba sie zmieniac nigdy nie bedzie za to zmeiniac bedzie sie datasource bindingsource.. teraz cholera go wie. o ile dobrze pamietam, inne kontrolki sie tak nie zachowuja, tylko ta jedna konkretna

sprawdz sam - postaw breakpointa przed datasource=.... i sprawdz na nim wartosc displaymember, potem prze'step'uj sie prezz ustawianie ds i zobacz displaymember po-fakcie.. moze to nie ten przypadek, bez kodu trudno powiedziec - wybadaj po prostu w debuggerze kiedy zmiana Name->null sie dokonuje

0

No to sprawdze czy ListBox podziała, a jeżeli nie to jakie jeszcze mógłbym wykorzystać kontrolki o podobnym działaniu ? Ew. czy jest jakikolwiek sposób żeby to obejść?

edit:
W ListBoxie jest to samo;/

edit2:
Ok poki co sprawdzam to co napisałes:>

0

jesli to to o czym pisze, po combo.DataSource = xxx; napisz combo.DisplayMemeber=blah;combo.ValueMember=bleh i tyle, no kurcze blade..

0

No proste, tak przecież robiłem bo i tak napisałes 3 posty wczesniej. Ale cały problem w tym , że po tej komendzie zarówno displayMember jak i valueMember wciąż były "".
Mianowicie wygląda to tak:

           listBox1.DisplayMember = "Name";
            label1.Text = "t"; // tutaj jeszcze displaymember = "Name"
             listBox1.DataSource = from i in db.tb_Insurers
                          select new Insurer( i.ID, i.Name );// po tym juz jest puste




             listBox1.DisplayMember = "Name"; 
            listBox1.ValueMember = "ID";// displayMember wciaz jest == ""
0

okeyj.. to juz dziwne.. [zakladajac ze pozostale okolice masz poprawne..]

nie chce mi sie kopac na MSDN, wiec.. reflector mowi:

public object DataSource
{
    set
    {
        if (((value != null) && !(value is IList)) && !(value is IListSource))
            throw new ArgumentException(SR.GetString("BadDataSourceForComplexBinding"));
        if (this.dataSource != value)
        {
            try { this.SetDataConnection(value, this.displayMember, false); }   /// <----- wyjatek tu
            catch { this.DisplayMember = ""; }    // <--- powoduje to
            if (value == null) { this.DisplayMember = ""; }  // <--- chyba ze podasz null jako datasource, to tez czysci DispMembr
}   }   }
public string DisplayMember
{
    set
    {
        BindingMemberInfo displayMember = this.displayMember;
        try { this.SetDataConnection(this.dataSource, new BindingMemberInfo(value), false); }  //<-- wyjatek tutaj
        catch { this.displayMember = displayMember; }  //<-- powoduje olanie podanej wartosci i przywrocenie poprzedniej
}   }
public string ValueMember
{
    set
    {
        if (value == null) value = "";
        BindingMemberInfo newDisplayMember = new BindingMemberInfo(value);
        if (!newDisplayMember.Equals(this.valueMember))    // <- jesli byla zmiana wartosci valuemember na inna..
        {
            if (this.DisplayMember.Length == 0)
                this.SetDataConnection(this.DataSource, newDisplayMember, false);    // <- brak trycatch! wyjatek tutaj wywali program
            if (((this.dataManager != null) && (value != null)) && ((value.Length != 0) && !this.BindingMemberInfoInDataManager(newDisplayMember)))
                throw new ArgumentException(SR.GetString("ListControlWrongValueMember"), "value");
            this.valueMember = newDisplayMember;
            this.OnValueMemberChanged(EventArgs.Empty);
            this.OnSelectedValueChanged(EventArgs.Empty);
}   }   }

stad wnioski:

  • jesli w valuemember by cos rzucilo wyjatkiem, to bys to na pewno zobaczyl. w takim razie valuemember pewnie ustawia sie i zapamietauje prawidlowo - tak sie nie dzieje, program sie nie wywala z wyjatkiem na tej linii, prawda?

  • datasource i displaymember uzywaja SetDataConnection, jesli ona walnie wyjatkiem - nie przyjmuja wartosci i 'resetuja' sie do null albo do poprzedniej. Ty tego wyjatku nigdy nie zobaczysz, bo jest obudowane trycatchem.
    stad wniosek: musial poleciec w srodku comboboxa wyjatek, jeszcze w DataSource:set i zczyscic Ci DisplayMember, potem pozniejsze proby ustawienia DisplayMember owocowaly za pewne tym samym wyjatkiem w SetDataConnection i przywracala sie stara wartosc (pusta od czasu DataSource:set !)

sprobuj zrobic tak: na starcie formatki NIE ustawiaj ValueMember, niech bedzie puste, ustaw je prawidlowo dopiero PO ustawieniu datasource. jesli poleci wyjatek i wywali aplikacje - czegos sie dowiedzelismy: musial poleciec z SetDataConnection. opisz to, wklej tresc wyjatku.

drugie podejscie: zrob tak samo jak przed chwila, ale po datasource:set ustaw ValueMember na "Name", odpal, sprwadz czy wyjatek wywali aplikacje, opisz, wklej wyjatek.

trzecie: teraz bedzie ciezej, bo wyjatek bedzie wygaszony.. ale mozna go 'wykryc'..
odpal program jeszcze raz w trybie debug, zhaltuj go na linijce DATASOURCE = xxx, przejdz do debuggera, obejrzyj zawartosc okienka Output, wyczysc je, przestepuj sie przez ta linie, sprawdz w Output czy pojawila sie informacja o wyjatku (First chance exception blah blah). jesli tak - wklej tutaj te inforamcje.

troche wyjasnien co do SetDataConnection, wyjatek w nim leci np. gdy:

  • nie uda sie wyciagnac z obiektu datasource'a informacji o liscie wlasciwosci jego elementow
  • property elementu wskazane przez DisplayMember jest typu IList
  • element w ogole nie ma w property o takiej nazwie (sprawdzane case-insensitive zeby bylo smieszniej..)
    i wyjatek wtedy zwie sie.. ArgumentException. jak go zauwazysz podczas w/w trzech prob, to mow o tym wyraznie i sprawdz 30 razy czy dobre rzeczy wpisales w Display/Value member i czy kolekcja w DataSource na pewno na bank na 100% nie jest przypadkiem pusta..

wiecej Ci raczej juz w ciemno nie powiem.. musialbys wyslac cala formatke, lacznie z przykladowymi danymi jakie Ci linq sciaga z bazy

0

Huh Wielkie dzięki za obszerna wypowiedź, dostarczająca sporą dawke wiedzy.
A à propos problemu , był bardziej prozaiczny , mianowicie wystarczyło zmienić troche zapytanie linq:

  comboBox1.DataSource = from i in db.tb_Insurers
                                  select new Insurer { ID = i.ID, Name = i.Name};

Dzięki wielkie za okazane zainteresowanie i pomoc.

I zapewne do usłyszenia:>

0

8|
to Co ten Twoj dwuparametrowy konstruktor Insurer'a wtedy robil w poprzednim zapytaniu skoro explicite ustawienie propertiesow klasy przy konstrukcji naprawilo problem..???

0

this.ID = ID;
this.Name = Name;

o to robił

0

przeciez miedzy tymi dwoma kodami nie ma w takim razie zadnej roznicy.. jestes pewien ze nic wiecej nie zmieniales..?

hm.. ok, moze byc mala roznica, ale bardzo watpliwe.. sprawdz czy:
string tmp = ( from i in db.tb_Insurers select new Insurer( i.ID, i.Name ) ).First().Name;
nie rzuca przypadkiem wyjatkiem.. zdziwiloby mnie to, no ale..

0

Ogolnie to przy tym nowym zapytaniu oczywiscie musial zmienic klase na:

  public class Insurer
    {

        public int ID { get; set; }
        public string Name { get; set; }


    }

zamiast:

public class Insurer
{ 
private int ID;
private string Name;
public Insurer(int ID, string Name)
{
  this.ID = ID;
  this.Name = Name;
}
}

I to rozwiązało sprawe

A co do twego pytania , to jak sam widzac ta stara klase pewnie sie domyslasz, nawet sie nie kompiluje bo nie mialem metody zwracajacej prywatnego Name... cóż no w skrócie to zawracałem głowe bo podstawowej klasy nie umiałem dobrze napisac;/

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