Imam listview ispunjen kontaktima koji se čuvaju u XML file-u. Uzmimo za primer da tamo imam 5 ljudi. Ako pokušam da izbrišem svih 5 - nakon ponovnog startovanja programa - svih 5 će ponovo biti tamo, dakle, kao da ih nisam ni brisao. Moguće je od tih 5 obrisati samo 4 (ako ih imam 100, moguće je obrisati 99) - jedan će uvek ostati. Desi se i da nakon brisanja nekog kontakta, kada pokušam sledeći - ne uspevam ni da ga izbrišem, dakle pokušam da ga selektujem i javi se sledeći error:
void Rmv() { if(Properties.Settings.Default.Remove==true) {
DialogResult dialogResult = MessageBox.Show("Are you sure you want to delete this contact?", "Confirm", MessageBoxButtons.YesNo, MessageBoxIcon.Question); if(dialogResult == DialogResult.Yes) {
Remove(); if(Properties.Settings.Default.Sync==true){ Sync();} } elseif(dialogResult == DialogResult.No) { return; } } else {
Remove(); if(Properties.Settings.Default.Sync==true){ Sync();} } } void Remove() {
Person person =new Person();
person = FindPerson(listView1.SelectedItems[0].Text); if(listView1.SelectedItems.Count>0) { try { if(listView1.SelectedItems.Count==0)return;
people.RemoveAt(listView1.SelectedItems[0].Index); foreach(ListViewItem eachItem in listView1.SelectedItems) {
listView1.Items.Remove(eachItem); } } catch{}
ClearAll();
ReadOnlyON(); } else {
MessageBox.Show("Nothing is selected!", "Notification", MessageBoxButtons.OK, MessageBoxIcon.Information); }
ReadOnlyOFF();
UserCount(); if(Properties.Settings.Default.Sync==true){ Sync();} }
Znači molim vas, spasavajte, lupam glavu oko ove gluposti koju vučem od samog početka i nikako da rešim.
[ AMD guy @ 28.11.2013. 13:15 ] @
proveri ovo
Code: person = FindPerson(listView1.SelectedItems[0].Text);
sta se nalazi u person variable. Da li je null ili ima neku vrednost?
[ Cal Lightman @ 28.11.2013. 13:55 ] @
Nemoj zameriti, kako se to proverava?
Pokušao sam ovako:
Code:
private void checkToolStripMenuItem_Click(object sender, System.EventArgs e)
{
Person person = new Person();
person = FindPerson(listView1.SelectedItems[0].Text);
if (person == null) { MessageBox.Show("Yes"); }
else { MessageBox.Show("No"); }
}
znači da selektujem kontakt i onda izvršim tu proveru. Kaže No.
[ AMD guy @ 28.11.2013. 14:51 ] @
Mislio sam da stavis breakpoint na person
Code: Person person = new Person(); //kreira instancu
person = FindPerson(listView1.SelectedItems[0].Text); //ovde stavis breakpoint(F9) , i (F5) start with debugging i vidis sta sadrzi person posle izvrsenja FindPerson methode. Trebalo bi u watch prozoru da imas person promenljivu tipa Person.
txt_Name.Text = person.Name;
[ Cal Lightman @ 28.11.2013. 15:24 ] @
Znači, selektovao sam liniju:
Code:
person = FindPerson(listView1.SelectedItems[0].Text);
i Breakpoint ---> Insert Breakpoint
nakon toga, Start debugging. Runuje se program, kliknem jednom na neki kontakt i ne mogu uopšte više da se vraćam tamo, dok ne stisnem STOP.
Watch prazan...
Nadam se da sam te dobro razumeo.
[ AMD guy @ 28.11.2013. 16:02 ] @
Izvrsi tu liniju, pa vidi sta se nalazi u toj variabli, nesto ovako.
I ne zove se watch nego locals, moja greska.
[Ovu poruku je menjao AMD guy dana 28.11.2013. u 17:12 GMT+1]
[ Cal Lightman @ 28.11.2013. 16:12 ] @
Nadam se da je to ovo.
[ AMD guy @ 28.11.2013. 16:18 ] @
Da, tako je.
Vidis da ti je Name property null pa ti zato zbog toga vraca "Object reference not set to an instance of an object".
Izgleda da moras da popunis person object sa podacima pa onda da radis Find, ovaki ti samo vraca praznu instancu.
[ Cal Lightman @ 28.11.2013. 16:24 ] @
OK, čini se da je problem dijagnostifikovan, rešavanje istog je još uvek science fiction za mene.
ovo ti radi pod predpostavkom da Name property vec ima vrednost i uporedjuje ga sa name parametrom koji je prosledjen FindPerson metodi, probaj da uradis proveru u toj metodi da li je Name property null ili ne, ako jeste onda ispisi poruku Name property ne moze biti null ili tako nesto ako nije onda uradi people.Find(x => x.Name == name); sacuvaj to u variabli tipa person i vrati(return) tu vrednost koja ce biti sacuvana ovde u person
person = FindPerson(listView1.SelectedItems[0].Text); //FindPerson ti vraca objekat tipa Person i to se cuva u person variabli.
[ Cal Lightman @ 28.11.2013. 17:41 ] @
Da, još uvek koristim.
Prijatelju, evo lupam glavu već sat vremena šta i kako - uspeo sam da samo da napunim punu vreću neuspeha. Pokušavam da provalim šta je trebalo da uradim, ali ovo što sam uradio definitivno nije ono što si mislio. A evo i šta sam skarabudžio:
Code:
private Person FindPerson(string name)
{
Person person = new Person();
if (name == null)
{
MessageBox.Show("Name cannot be null");
}
else people.Find(x => x.Name == name);
return person = FindPerson(listView1.SelectedItems[0].Text);
}
[ AMD guy @ 28.11.2013. 17:58 ] @
Citat:
A evo i šta sam skarabudžio:
Prava rec :)
Code (csharp):
private Person FindPerson(string name) {
Person person =new Person();//Vec si napravio instancu klase Person pre nego sto si pozvao FindPerson metodu, ne treba ti nova. //najbolje bi bilo da tu instancu stavis na klasni nivo(nivo klase), ovako kao sto si ti stavio ti kreira novu instancu klase cim opalis SelectedIndexChanged event. To ti predstavlja problem.
//u if petlji treba da proveravas Name property Person klase a ne parametra koji saljes, probaj person.Name == null //ispravka if(person.Name==null) {
MessageBox.Show("Name cannot be null"); } //ispravka. else{ person = person.Find(x => x.Name== name);} return person; }
Ovo je pisano napamet, ne zameri ako ima neka greska, ali mislim da je idea dobra.
Primetio sam da i u Remove metodi imas isto novu instancu, Person person = new Person(); svaki put kada to uradis kreiras novi objekat na stack-u.
pogledaj malo objasnjenje ovde.
Jel si citao nesto o klasama, objektima, OOP itd?
[Ovu poruku je menjao AMD guy dana 28.11.2013. u 19:26 GMT+1]
[ Cal Lightman @ 28.11.2013. 18:26 ] @
Dakle, prvo što sam uradio jeste da sam izbrisao sledeće (svuda gde se pominje, a to je u Remove() i u ListviewSelectedIndexChanged):
else{ person =[b]person[/b].Find(x => x.Name== name);}
... pa sam to što se crveni pokušao da zamenim sa people, jer je u originalnom kodu bilo tako (?). "Crvenilo" se povuklo i kod se uklopio.
Šta se dešava - runujem program, pokušam da selektujem nekog i iskoči MessageBox, nakon toga kada ponovo pokušam da selektujem - error.
Izgleda da su se više sile urotile protiv ove aplikacije.
[ AMD guy @ 28.11.2013. 18:46 ] @
I ova greska je dosta opisna, opet proveri koji je format i koji je opseg koji stoji u Birthday property-ju sa debugovanje kao sto sam ti pokazao za Name property.
[ Cal Lightman @ 28.11.2013. 18:58 ] @
Evo ga:
[ AMD guy @ 28.11.2013. 19:24 ] @
Vidim da ti ne stoji neka vrednost u Birthday property-ju ono sto imas je neka default vrednost, pretpostavljam da nisi popunio person objekat sa vrednostima, jel si probao sa person.Birthday.Date?
Sta kaze datetimepicker koja je vrednost njegovog Value property-ja. Jel moguce dodeliti Birthday vrednost u datetimepicker.value?
[ Cal Lightman @ 28.11.2013. 20:43 ] @
Evo probao sam da dodam .Date, isti se error javlja.
A što se tiče ovog value-a, jesi na ovo mislio?
Ja ne verujem koja komplikacija...
[ tuolarips @ 01.12.2013. 18:16 ] @
Niste na dobrom putu, pa bih uskocila u pomoc :)
Pre svega bih kod vratila u prvobitno stanje, a onda krenula sa ispravkama. Krenucu od inicijalnog problema, tj zasto se kontakti ne brisu i vracaju prilikom ponovnog startovanja aplikacije. Problem lezi u tome sto kontakte brises iz listview-a, ali ne i iz xml fajla koji ti predstavlja "bazu". Volela bih za pocetak da vidim kako popunjavas listview, da bih ti pomogla kako da permanentno obrises podatke.
Citat:
AMD guy: Da, tako je.
Vidis da ti je Name property null pa ti zato zbog toga vraca "Object reference not set to an instance of an object".
Izgleda da moras da popunis person object sa podacima pa onda da radis Find, ovaki ti samo vraca praznu instancu.
Sa ovim se ne bih slozila. Null pointer exception u njegovom slucaju ne moze da se dobije zbog toga sto je Name null. Obzirom da je kod sledeci:
txt_Name.Text = person.Name;
jasno je da null reference exception moze nastati samo ako je txt_Name null ili ako je person null. U slucaju da je Name null najgore sto bi se desilo jeste da Text polje txt_Name objekta takodje postane null.
Dakle, losa smernica vam je bila sto ste na osnovu onog breakpointa uopste konstatovali da se "Name" ne popunjava. Breakpoint stoji na liniji koda koja se jos nije izvrsila, a u person promenljivoj stoji default-no popunjen objekat nastao nakon ove linije "Person person = new Peson()". Od te konstatacije ste krenuli u skroz pogresnom smeru.
Sa komentarom da ne treba praviti nove objekte bespotrebno bih se skroz slozila, ali se ne bih slozila sa izdvajanjem person promenljive kao polje klase. To je za ovaj slucaj skroz pogresno i nije u skladu sa principima oop.
Ako se ja pitam, najvise bih volela kod u celosti, ispravila bih greske i komentarima obrazlozila ispravke. :)
[ AMD guy @ 01.12.2013. 22:24 ] @
Imali smo malu diskusiju preko PP i tamo mi je dao link na jednu verziju svog programa. Ako zelis i ako se Cal slaze dao bih ti njegovu verziju, koja je za moj ukus malo preobimna za tako malu i jednostavnu aplikaciju.
Citat:
To je za ovaj slucaj skroz pogresno i nije u skladu sa principima oop.
To sam i ja primetio ali mi je Cal rekao da jos nije upoznat sa principima OOP pa ga zato nisam previse ispravljao na tom polju.
[ Cal Lightman @ 01.12.2013. 22:38 ] @
Rešenje ovog problema sastojalo se u sledećim izmenama:
Code (csharp):
void Remove() {
Person person =new Person(); try{ person = FindPerson(listView1.SelectedItems[0].Text);} catch{return;} if(listView1.SelectedItems.Count>0) { try { if(listView1.SelectedItems.Count==0)return; foreach(ListViewItem eachItem in listView1.SelectedItems) {
people.RemoveAll(x => x.Name== eachItem.Text);
listView1.Items[listView1.Items.Count-1].Selected=true;
listView1.Items.Remove(eachItem); } } catch{}
ClearAll();
ReadOnlyON(); } else {
MessageBox.Show("Nothing is selected!", "Notification", MessageBoxButtons.OK, MessageBoxIcon.Information); }
ReadOnlyOFF();
UserCount();
Sync(); }
U čemu se po tvom mišljenju ogleda obimnost ove aplikacije tj. šta bi ti oduzeo/izbacio?
[ AMD guy @ 01.12.2013. 23:32 ] @
Jel si brojao linije koda, ova aplikacija treba da radi unos novog clana, izmenu postojeceg, brisanje i pretragu postojecih clanova. Ovo sve se radi sa XML fajlom, jel si citao o OOP, to bi ti smanjilo broj linija koda i ostavilo mogucnost za dalje prosirenje programa. Napravio bih klasu User koja bi imala sva potrebna polja Name, LastName, DOB ... i metode za Novog korisnika, izmenu, brisanje i pretragu, svu interakciju bi radio sa tom klasom a ovo sa GUI elementima to je drugorazredno. Kazem ti citaj o OOP neces zazaliti.
[ tuolarips @ 01.12.2013. 23:44 ] @
@AMD guy
Ne moras mi davati tu verziju ako ste problem resili.. Slucajno sam krenula da citam vasu temu, ucinilo mi se da ste otisli u pogresnom pravcu, pa sam htela da pomognem :)
@Cal Lightman
Po stilu pisanja koda se vidi da nisi dugo u ovoj prici. Evo ja cu napisati par stvari koje su mi upale u oko, cisto da znas za ubuduce :)
1. Ovo je AMD guy vec rekao, a evo i ja cu opet, jer malo bode oci. Pogledaj ovo parce koda:
Code:
Person person = new Person();
try { person = FindPerson(listView1.SelectedItems[0].Text); }
Ovim "Person person = new Person();" si napravio novi objekat i smestio ga u promenljivu person, a odmah nakon toga si u promenljvu person stavio rezultat izvrsavanja metoda FindPerson. Sto znaci da je jedan objekat uzaludno napravljen i odmah po nastanku je dat garbage collectoru na ciscenje. Posto s druge strane vidim da promenljivu person ne koristis van tela "try" naredbe, ja bih ovo parce koda napisala ovako:
Code:
try {Person person = FindPerson(listView1.SelectedItems[0].Text); }
2. Nikako mi se ne svidja usmeravanje toka izvrsavanja programa uz pomoc try catch naredbe.
Code:
try { person = FindPerson(listView1.SelectedItems[0].Text); }
catch { return; }
try/catch treba da se koristi iskljucivo za handle-ovanje situacija koje su stvarno izuzeci, a ne i za kontrolu toka izvrsavanja programa. To je uvek losa praksa. Ja bih FindPerson napravila tako da vraca null u slucaju da osoba nije pronadjena, tako da bi kod posle moje izmene izgledao ovako:
Code:
Person person = FindPerson(listView1.SelectedItems[0].Text);
if (person == null) { return; }
3. Uslov iz donjeg ifa nikad nece biti false. Nema potrebe za njim :)
Code:
if (listView1.SelectedItems.Count > 0)
{
try
{
if (listView1.SelectedItems.Count == 0) return;
. . .
4. Ovaj deo koda mozda ima pricu koju ne shvatam, ali trenutno mi deluje bespotrebno raditi to u svakoj iteraciji petlje:
5. Ovde je suvisno proveravati x.Name != null (pored toga postoji ista greska kao i u tacki 1).
Code:
Person person = new Person();
person = people[people.FindIndex(0, x => x.Name == listView1.SelectedItems[0].Text && x.Name != null)];
Takodje, malo je nebezbedno tek tako proslediti vrednost koju si dobio pozivanjem "FindIndex" metoda. Moze se desiti da FindIndex vrati -1, zbog toga sto iz nekog razloga nije nasao coveka sa tim imenom. Postoji elegantnije resenje, koriscenjem "Find"-a (kod sam na slepo kucala, i nisam proverila, ali trebalo bi da je tako):
PS za stringove se savetuje koriscenje Equals metode umesto ==
Code:
Person person = people.Find(x => x.Name.Equals(listView1.SelectedItems[0].Text));
6. Cini mi se da podatke u svoj xml cuvas samo pri zatvaranju forme.. Na taj nacin ne bi mogla da funkcionise nijedna ozbiljnija aplikacija. Praksa je ili da postoji neki "Save" button (gde prepustas odgovornost korisniku) ili da automatski pri svakoj izmeni cuvas podatke. Na taj nacin se osiguravas da nepredvidjenim prekidom rada aplikacije nece sav dotadasnji rad u njoj pasti u vodu..
Eto to su mojih par saveta :) Samo nemoj da pomislis da je to sto si uradio lose, daleko od toga.. Ovo su samo stvari koje sam ja naucila programirajuci i citajuci kojekakve tekstove.. Ja se nadam da ce ti ovo biti od koristi u daljem radu :)
[ AMD guy @ 02.12.2013. 00:01 ] @
Citat:
PS za stringove se savetuje koriscenje Equals metode umesto ==
Nisam, skinuo sam neke knjige i počeo sa čitanjem prve pod nazivom "Microsoft C# Programming for Absolute Beginners" od autora Andy Harris-a. Knjiga ima preko 500 strana, ja sam otprilike prešao petinu, znači bukvalno sam počeo od HelloWorld programa i rađenja Console aplikacija. Definitivno je ovo trebalo da uradim pre upuštanja u posao. Elem, polako shvatam koncept ovog jezika i naravno i sam uviđam neke greške, tipa, negde sam mogao da upotrebljavam switch metodu umesto silnih "else if-ova" itd. Planiram da završim knjigu pa da onda lepo uredim program tj. da malo sredim kod. Btw, kod ima približno 1000 linija.
@tuolarips:
Hvala na ispravkama, imaću ih u vidu. Daleko od toga da zameram, svaka je dobrodošla! Ipak, potrudiću se da prvo shvatim o čemu se ispravke sastoje (da razumem problem i rešenje istog) pre nego što ih i unesem u kod.
@EDIT:
Aplikacija poseduje mogućnost biranja između više (jednostavnih) tema, a to se u suštini ogleda u menjaju izgleda button-a, form icons-a i picturebox-eva. To sam odradio tako što sam u Resources ubacio ogroman broj slika i to drastično uvećeva veličinu konačne verzije. Planiram to da promenim tako da se to obavlja po principu button1.BackColor = Color.FromArbg(r, b, g) i da tako aplikaciju značajno smanjim. Međutim, imam izvesne probleme:
Dakle, umesto ovoga:
Code (csharp):
if(Properties.Settings.Default.Theme=="Purple") { foreach(var form in Application.OpenForms.Cast<Form>()) {
form.Icon= Properties.Resources.Purple; }
Main f1 =(Main)Application.OpenForms["Main"];
Settings f2 =(Settings)Application.OpenForms["Settings"];
f1.btn_Exit.Image= Properties.Resources.EXIT_purple; }
... pokušavam da napravim ovo:
Code (csharp):
if(Properties.Settings.Default.Theme=="Purple") { foreach(var form in Application.OpenForms.Cast<Form>()) {
form.Icon= Properties.Resources.Purple; }
Main f1 =(Main)Application.OpenForms["Main"];
Settings f2 =(Settings)Application.OpenForms["Settings"];
... ali bez željenih rezultata, buttons i picturebox se jednostavno ne menjaju. Verovatno nešto radim pogrešno, a i sam ću uvideti šta kada u knjizi stignem do dela o klasama. To mi pravi velike probleme, što ne znam kako da nešto što se nalazi na jednoj formi call-ujem iz druge forme - i otuda silno gomilanje koda - što AMD kaže - natrpavanje kutija.
[Ovu poruku je menjao Cal Lightman dana 02.12.2013. u 01:18 GMT+1]
[ AMD guy @ 02.12.2013. 00:17 ] @
Nisi mogao da nadjes neku noviju knjigu, ta je iz 2002 uzmi barem iz 2008 ili 2010 godine.
C# je izasao 2001 pa ne bih bas uzimao knjigu iz 2002.
Sa kutijama sam hteo da objasnim koncept kreiranja objekata i njihovog cuvanja na stacku u memoriji. Ali to moze da bude malo naprednjiji koncept.
[ Cal Lightman @ 02.12.2013. 00:34 ] @
Haha, da, baš je old school.
Nego, sve što tamo piše mi je do sad "važilo" u VS 2013, je l' se nešto menjalo u međuvremenu? Mislim, ako jeste, da ne učim istoriju... Kad kažeš noviju, je l' postoji od istog autora novije verzije iste knjige ili si mislio da ujedno potražim i drugog autora?
Inače sviđa mi se kako je napisana knjiga i nekako sam krenuo po njoj i sve mi odgovara, baš će biti trulo ako se nešto menjalo.
[ ravni @ 02.12.2013. 07:43 ] @
Citat:
PS za stringove se savetuje koriscenje Equals metode umesto ==
Ko li to savetuje?
[ AMD guy @ 02.12.2013. 07:49 ] @
Head First C#
Microsoft Visual C# 2012 Step by Step
Illustrated C# 2012
Ove knjige preporucujem, procitao sam.
[ tuolarips @ 02.12.2013. 08:22 ] @
@AMD guy, ravni
Equals i == rade isti posao, te taj savet nije suvise bitan (koliko jeste za ostale referentne tipove). Medjutim, negde je presudno koristiti Equals zajedno s StringComparison parametrom. Naime, u mom timu se desio bug kog su jedva skontali (jer se nije reprodukovao na svim racunarima), a odnosio se na to sto kad se u windowsu postavi jezik na srpski, zarezi pri pisanju brojeva su se tretirali kao tacke a tacke kao zarezi. Da smo koristili Equals zajedno sa StringComparison parametrom, to se ne bi desilo.
Pretpostavila sam da ce neko nesto iskomentarisati za taj moj komentar (kao sto sam i ja iskomentarisala kad su meni prvi put rekli da koristim equals bez jakih argumenata) :D :)
[ tuolarips @ 02.12.2013. 08:24 ] @
Sto se knjiga tice, za kraj (posle svih koje je AMD guy naveo) bih predlozila Effective C# (Second edition) od Bill Wagner-a. :)
[ ravni @ 02.12.2013. 13:00 ] @
Citat:
...negde je presudno koristiti Equals zajedno s StringComparison parametrom...
Sasvim u redu.
Citat:
...koliko jeste za ostale referentne tipove...
A sta se desava sa ostalim referentnim tipovima. Zar je == stvarno toliki promasaj?
[ tuolarips @ 02.12.2013. 13:17 ] @
Zavisi sta ocekujes da ti rezultat bude. Equals i == imaju skroz drugaciju svrhu. == poredi objekte po referenci. Recimo da imas ovaj slucaj
Code:
Person person1 = new Person("Jovan", "Jovanovic");
Person person2 = person1;
Person person3 = new Person("Jovan", "Jovanovic");
Kada uradis person1 == person2, dobices true. Medjutim kad uradis person == person3, dobices false, iako je sadrzaj ta dva objekta isti (iste stvari smo prosledili konstruktoru). Dvostruko jednako se nikad ne treba override-ovati, iako to c# dozvoljava. I treba se koristiti iskljucivo kada je potrebno poredjenje po referenci.
S druge strane Equals je pozeljno override-ovati. Po default-u Equals takodje poredi po referenci, ali bilo koje ozbiljnije third party biblioteke ce u svojim klasama override-ovati equals. Ako pretpostavimo da je u klasi Person Equals adekvatno override-ovan, ona cemo i za slucaj person1.Equals(person2) kao i za slucaj person1.Equals(person3) dobiti true.
Zakljucak: == se koristi za poredjenje referenci, dok se Equals koristi za poredjenje sadrzaja objekta. Vise mozes procitati u knjizi koju sam navela iznad (Item 6: Understand the Relationships Among the Many Different Concepts of Equality)
Sad smo malo odlutali od teme, ali dobro.
[Ovu poruku je menjao tuolarips dana 02.12.2013. u 14:44 GMT+1]
[ ravni @ 03.12.2013. 07:32 ] @
Citat:
za stringove se savetuje koriscenje Equals metode umesto ==... taj savet nije suvise bitan (koliko jeste za ostale referentne tipove)... Zakljucak: == se koristi za poredjenje referenci, dok se Equals koristi za poredjenje sadrzaja objekta
Onda, zapravo onaj savet i nije neki savet?
[ tuolarips @ 03.12.2013. 14:13 ] @
Zar nije to upravo ono sto sam ja rekla u delu kog si citirao?