problem pri mazani polozek v listview   zodpovězená otázka

VB.NET

Dobry vecer,

mam problem pri hromadnem promazavani listview, kde se mazou polozky odlisene jinou barvou(ForeColor). V cyklu for se dostavam mimo rozsah indexu, ale nemuzu prijit na to proc. Uz nad tim sedim dve hodiny a nemuzu se hnout z mista...

Dim X As Integer = MsgBox("Opravdu si přejete odstranit staré upomínky?", vbOKCancel + vbExclamation, "Odstranit staré položky")

        If X = vbOK Then
            For i As Integer = 0 To ListView1.Items.Count - 1
                If ListView1.Items(i).ForeColor = Color.FromKnownColor(KnownColor.Firebrick) Then
                    ListView1.Items(i).Remove()
                End If
            Next i
        End If
nahlásit spamnahlásit spam 0 odpovědětodpovědět

Jen takový nápad: Zkuste použít cyklus For Each, protože u něj se Vám tohle jistě nestane. Kdyžtak ještě napište a já to zkusím prozkoumat :-)

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

diky, to je ono, tohle reseni me nenapadlo...

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

Ten nápad s For each je dobrý, protože konstrukce s klasickým For... má dvě vady:

1) Neprojdete všechny záznamy:

pokud se totiž dostanete na záznam číslo 5, (máte i=5) a záznam vyhoví Vaší podmínce a vy jej smažete, co se stane? Záznam zmizí, záznam číslo 6 se stane pětkou, 7 se stane šestkou atd.

Vy načnete nový cyklus, ovšem z hlediska funkčnosti před náběhem do nového cyklu se vám i zvýší na 6. No a procházíte záznam číslo 6, jenomže původní šestka, která je nyní na pozici 5, ta zůstane Vaším cyklem neposkvrněná.

2) nejsem si jist, ale dle všeho ten výraz za tím To (horní limit hodnot i) se u cyklu For vyhodnotí na začátku cyklu, proto, pokud něco z kolekce odmažete, dostanete se mimo rozsah hodnot.

Zkyste tedy ten cyklus For each, nebo mám ověřeno ještě, že funguje klasický cyklus do ... while (tam se ten výraz vyhodnocuje při každém průchodu znovu), ale musíte (kvůli bodu 1) zabezpečit, že ke zvýšení čítače i dojde pouze, pokud nesmažete aktuální záznam (v praxi v jednoduchých případech dáte do Vašeho rozhodovacího příkazu ještě větev else tak, že bude-li podmínka if splněna, smažete záznam, nebude-li, zvýšíte i o 1 a jdete do dalšího cyklu.

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

Tak jsem se na to díval, a D je správně.:

a) skutečně For i=0 to ... si vyhodnotí výraz "To" na začátku cyklu, proto se již nepřizpůsobí změněné délce kolekce a zavede Vás mimo její dimenze

b) dokonce ani to For each není lékem (myslel jsem, že je trošku inteligentnější), ale aby se cyklus nemusel zaobírat nějakým ošetřováním změny rozsahu, tak Vám jednoduše vyhodí chybu, pokud procházenou kolekci změníte

c) ještě pro procházení kolekcemi je možno použít enumerator, ten jsem teď sice nezkoušel, ale v dokumentaci píšou, že se nesmí použít k modifikaci procházené kolekce

d) takže zbývá možnost D, tj ten obezný do while cyklus, nějak asi tak (pouze systém řešení):

Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
        TextBox1.Text = ""
        Dim seznam As New List(Of Integer)
        seznam.Add(0)
        seznam.Add(1)
        seznam.Add(2)
        seznam.Add(3)
        seznam.Add(4)

        TextBox1.Text = "počet prvků: " & seznam.Count & vbCrLf
        Dim i As Integer = 0

        Do While i < seznam.Count
            TextBox1.Text &= i.ToString & " - " & seznam(i) & vbCrLf
            If seznam(i) Mod 2 = 0 Then
                seznam.Remove(i)
            Else
                i += 1
            End If
        Loop
        TextBox1.Text &= "počet prvků: " & seznam.Count & vbCrLf
    End Sub

pokud toto spustím, tak dostanu výpis:

počet prvků: 5

0 - 0

0 - 1

1 - 2

1 - 3

2 - 4

počet prvků: 2

tj. na začátku má colekce 5 prvků

pak procházím prvek 0, který má obsah 0,

dále procházím opět prvek 0, teď má ale obsah 1 (ten předchozí jsem smazal a všechny následující se mi tím pádem posunuly)

atd.atd - projdu všechny prvky a nevyvede mě z míry změna rozsahu kolekce.

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

Tak jsi to opět vyřešil :-)

Taky jsem si myslel, že For Next bude lepší, ale když se nad tím zamyslíš, tak vlastně ani není proč, protože jednoduče pracuje stále s tou samou kolekcí a je tak líný, že jí ani nechce aktualizovat. To že vyhodí chybu je docela podraz, ale myslím, že řešení s Do, je skvělé!

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

Při odstraňování položek z kolekce je vždy nutné postupovat pozpátku. Tj. ne od 0 do 99 ale od 99 do 0. V případě For Each je to ten samý případ, protože For Each pracuje s enumerátorem, který vnitřně prochází pole hodnot.

For index As Integer = (ListView1.SelectedItems.Count - 1) To 0 Step -1
  ListView1.Items.Remove(ListView1.SelectedItems(index).Name)
Next

Smaže všechny vybrané položky z ListView.

nahlásit spamnahlásit spam 0 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