Změna vlastnosti několika prvků najednou za běhu   zodpovězená otázka

VB.NET

Dobrý den, mám formulář, na němž se v ListBoxu zobrazují záznamy s připojené databáze. Výběr zobrazených záznamů provádím pomocí CheckBoxů. Rád bych se dověděl, jak se dá změnit např. vlastnost CheckBox.Enabled za běhu u např. desíti CheckBoxů najednou. Jde mi o to, aby pokud zatrhnu některý CheckBox, nešlo několik dalších zatrhnout. Vypisuju to u každého Checkboxu zvlášť a je to pracné a časově náročné. Vím, že je to pro vás banální záležitost, ale já v podstatě začínám a uvítám každou radu. Díky. Honza. (VB.NET 2010)

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

Předpokládám, že pracujete s Windows Forms a ne s WPF?

Jestli tomu správně rozumím, máte nějakou stromovou strukturu CheckBoxů, kdy při zaškrtnutí rodiče chcete zaškrtnout také všechny potomky. Může to vypadat třeba takhle:

× CheckBox1      × CheckBox2      × CheckBox3
  × CheckBox1.1    × CheckBox2.1    × CheckBox3.1 
  × CheckBox1.2    × CheckBox2.2      × CheckBox3.1.1
  × CheckBox1.3                       × CheckBox3.1.2
                                    × CheckBox3.2

Napadá mě například takové řešení, kdy všechny dceřinné CheckBoxy dáte do kontejneru typu Panel, či GroupBox a při zaškrtnutí rodičovského CheckBoxu budete iterovat kolekcí CheckBoxů v asociovaném GroupBoxu (Panelu, ...).

V příkladě výše tedy budou v jednom GroupBoxu CheckBoxy 1.1, 1.2 a 1.3, v druhém 2.1 a 2.2 a ve třetím budou dva CheckBoxy 3.1 a 3.2 a další GroupBox (který ale nebude součástí iterované množiny), který bude asociovaný s 3.1.

Hromadné zaškrtnutí všech CheckBoxů pak bude vypadat takto:

Private Sub Zaskrtej(checkBox As CheckBox, groupBox As GroupBox, checked As Boolean)
  For Each checkBox In groupBox.Controls.OfType(Of CheckBox)
    checkBox.Checked = checked
  Next
End Sub

Tuto metodu zavoláte na každém CheckBoxu, který má asociovaný nějaký GroupBox.

Private Sub CheckBox1_CheckedChanged(sender As Object, e As EventArgs) Handles CheckBox1.CheckedChanged
  Dim checkBox = DirectCast(sender, CheckBox)
  Zaskrtej(checkBox, GroupBox1, checkBox.Checked)
End Sub

Alternativně (pokud je CheckBoxů mnoho) můžete také použít místo handlerů zaškrtnutí jednotlivých CheckBoxů (Sub CheckBoxX_CheckChanged) toto v události Load formuláře:

  Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    ' Definujeme asociace mezi CheckBoxy a GroupBoxy
    Dim associations = New Dictionary(Of CheckBox, GroupBox)
    associations.Add(CheckBox1, GroupBox1)
    associations.Add(CheckBox2, GroupBox2)
    associations.Add(CheckBox3, GroupBox3)
    associations.Add(CheckBox9, GroupBox4)
    Dim checkBoxStack = New Stack(Of CheckBox)
    Controls.OfType(Of CheckBox).ToList().ForEach(Sub(cb) checkBoxStack.Push(cb))
    ' Rekurzivně projíždíme všechny CheckBoxy na formuláři, které mají definovanou asociaci
    While checkBoxStack.Peek() IsNot Nothing
      Dim checkBox = checkBoxStack.Pop()
      If associations.ContainsKey(checkBox) Then
        ' Přidáme handler při zaškrtnutí CheckBoxu
        AddHandler checkBox.CheckedChanged,
          Sub(sender2, e2)
            Dim checkBox2 = DirectCast(sender2, CheckBox)
            Zaskrtej(checkBox2, associations(checkBox2), checkBox2.Checked)
          End Sub
        ' Přidáme na Stack další CheckBoxy v GroupBoxu
        associations(checkBox).Controls.OfType(Of CheckBox).ToList().ForEach(Sub(cb) checkBoxStack.Push(cb))
      End If
    End While
  End Sub

Pokud netrváte vyloženě na použití CheckBoxů a jde Vám spíše o funkcionalitu, pro mnoho voleb by možná bylo lepší použít TreeView, kterému nastavíte vlastnost CheckBoxes na True a tím pádem přidáte ke všem položkám zaškrtávací pole. Pak už stačí jen:

  Private Sub TreeView1_AfterCheck(sender As Object, e As TreeViewEventArgs) Handles TreeView1.AfterCheck
    ' Ujistíme se, že událost vyvolal uživatel
    If e.Action.HasFlag(TreeViewAction.ByKeyboard) OrElse e.Action.HasFlag(TreeViewAction.ByMouse) Then
      ' True pokud zaškrtnout všechny potomky
      ' False pokud zaškrtnout pouze potomky první úrovně
      Zaskrtej2(e.Node, e.Node.Checked, False)
    End If
  End Sub

  Private Sub Zaskrtej2(node As TreeNode, checked As Boolean, recursive As Boolean)
    For Each childNode As TreeNode In node.Nodes
      childNode.Checked = checked
      'If recursive Then Zaskrtej2(childNode, checked, True)
    Next
  End Sub
nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Děkuju za poskytnuté informace, nevím, jestli to zvládnu zpracovat, je to na mě hodně složité. Ještě bych osvětlil ten problém. Jsem mykolog a pokouším se udělat klíč k určování hub. Pomocí checkboxů, které jsou na formulářích vedle vyobrazených vlastností hub, určuju, které informace se mi zobrazí v listboxu z databáze. Pokud je přípustných více vlastností, může být zatrženo více checkboxů najednou. V hodně případech však může být zatržen jen jeden (když je něco červené nemůže to být modré). Mám to vyřešeno humpolácky (v rámci mých možností) následovně:


Private Sub CheckBox1_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CheckBox1.CheckedChanged
        'Jestli je CheckBox1 zatržený, tak se zobrazí data s filtrem Lupenaté, nastaveným v TableAdapteru. V tomto případě je to filtr, který vybere
        'z databáze pouze rody, které patří mezi lupenaté.

        If CheckBox1.Checked = True Then
            frmMykolog.Přehled_rodů_MycokeyTableAdapter.Lupenaté(frmMykolog.Databáze1DataSet.Přehled_rodů_Mycokey)
            'Ostatní checkboxy se znepřístupní, protože při jeho zatržení jsou  zobrazeny pouze lupenaté houby a žádná další možnost není.
            CheckBox2.Enabled = False
            CheckBox3.Enabled = False
            CheckBox4.Enabled = False
            CheckBox5.Enabled = False
            CheckBox6.Enabled = False
            CheckBox7.Enabled = False
        Else
            'Pokud CheckBox1 zatržený není, zobrazí se celý seznam rodů (filtr Všechny_rody) a ostatní checkboxy se zpřístupní, aby mohly být pro 
            'výběr použity.
            CheckBox2.Enabled = True
            CheckBox3.Enabled = True
            CheckBox4.Enabled = True
            CheckBox5.Enabled = True
            CheckBox6.Enabled = True
            CheckBox7.Enabled = True
            frmMykolog.Přehled_rodů_MycokeyTableAdapter.Všechny_rody(frmMykolog.Databáze1DataSet.Přehled_rodů_Mycokey)
        End If

    End Sub

    Private Sub CheckBox2_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CheckBox2.CheckedChanged
        'Jestli je CheckBox2 zatržený, tak se zobrazí data s filtrem Hřibovité, nastaveným v TableAdapteru. V tomto případě je to filtr, který vybere
        'z databáze pouze rody, které patří mezi hřibovité.

        If CheckBox2.Checked = True Then
            frmMykolog.Přehled_rodů_MycokeyTableAdapter.Hřibovité(frmMykolog.Databáze1DataSet.Přehled_rodů_Mycokey)
            'Ostatní checkboxy se znepřístupní, protože při jeho zatržení jsou  zobrazeny pouze hřibovité houby a žádná další možnost není.
            CheckBox1.Enabled = False
            CheckBox3.Enabled = False
            CheckBox4.Enabled = False
            CheckBox5.Enabled = False
            CheckBox6.Enabled = False
            CheckBox7.Enabled = False
        Else
            'Pokud CheckBox2 zatržený není, zobrazí se celý seznam rodů (příkaz Všechny_rody) a ostatní checkboxy se zpřístupní, aby mohly být pro 
            'výběr(použity.
            CheckBox1.Enabled = True
            CheckBox3.Enabled = True
            CheckBox4.Enabled = True
            CheckBox5.Enabled = True
            CheckBox6.Enabled = True
            CheckBox7.Enabled = True
            frmMykolog.Přehled_rodů_MycokeyTableAdapter.Všechny_rody(frmMykolog.Databáze1DataSet.Přehled_rodů_Mycokey)
        End If
    End Sub

Atd...

Celý můj problém je v tom, že je těch formulářů hodně možností výběru rovněž, takže tento způsob je kvůli psaní časově náročný. Takže by se mi hodil nějaký způsob, kterým bych třeba vlastnost Enabled mohl přiřadit několika checkboxům najednou.

Jestli by to byl nějaký složitý postup, tak to raději udělám takhle pracně (nic jiného mi nezbude) a považujte otázku za zodpovězenou. Anebo tu na tom blogu něco objevím (pilně studuju). Díky za snahu.

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

Pro začátek můžete použít například toho:

With New List(Of CheckBox) From {CheckBox1, CheckBox2, CheckBox3}
  .ForEach(Sub(cb) cb.Checked = True)
End With

Klauzule From znamená zavolej Add na každém prvku ve složených závorkách, takže v postatě vytvoříte seznam, List(Of CheckBox), který bude sdružovat reference na všechny CheckBoxy, které dáte do složených závorek. To by samo o sobě bylo k ničemu, ale List(Of T) má metodu ForEach, která si jako parametr bere Action(T) a provede tuto akci na každém prvku v Listu.

Šlo by to nahradit tímto:

For Each item In New List(Of CheckBox) From {CheckBox1, CheckBox2, CheckBox3}
  item.Checked = True ' /False
Next

Funkčně je to úplně to samé.

Pokud se ještě vrátíme k mému prvnímu příspěvku, stále můžete patičné CheckBoxy sdružit do Panelu nebo GroupBoxu a míst vytváření seznamu referencí použít ten Panel/GroupBox:

For Each item In GroupBox1.Controls.OfType(Of CheckBox)
  item.Checked = True ' /False
Next
' nebo
GroupBox1.Controls.OfType(Of CheckBox).ToList().ForEach(Sub(cb) cb.Checked = True)

Jak vidíte z těch ukázek, vždycky je to jenom o tom vytvořit kolekci těch prvků a pak na nich všech provést nějakou akci. Můžete si jí vytvořit sám v kódu a nebo si jí nechat vytvořit designerem tak, že ty CheckBoxy nataháte do nějakého Panelu/GroupBoxu, protože tím naplňujete kolekci Controls toho daného prvku a vlastně tak už získáte kolekci CheckBoxů, které chcete, akorát si je pak musíte vyfiltrovat, aby Vás tam nenaskákali další prvky jako tlačítka a podobně (to je to .OfType(Of CheckBox)). Jakmile takovouto kolekci máte (sám vytvořenou, nebo získanou z nějaké komponenty), stačí na ní provést kýženou akci a to už zase lze udělat různě, buď tou kolekcí iterovat pomocí For Each, jak jsem uvedl, a nebo jí převést na List a zavolat ForEach s inline procedurou. Jde o to, co se Vám líbí víc.

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

Ahoj, děkuji za vysvětlení, tohle je přesně ono a pochopil jsem to i já. Už jsem zkoušel i kolekci Controls a funguje to. Učím se pomalu, ale jistě. Díky.

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

Takže pokud to chápu správně, je požadavek v UI vybrat pouze jednu možnost z několika existujících?

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