CheckedListBox Text ändern.



  • tjaa..weiss auch nicht...

    private void checkedListBox1_SelectedIndexChanged(object sender, EventArgs e)
    {
                int index = checkedListBox1.SelectedIndex;
                checkedListBox1.Items[index] = "text";   
    }
    

    ArgumentOutOfRangeExcepion wurde nicht behandelt:

    InvalidArgument=Value mit dem Wert -1 ist für index ungültig.
    Parametername: index



  • Dann pack doch ne Prüfung rein das SelectedIndex auch ein Valider Wert ist.
    Laut MSDN kann es verschiedene gründe geben das SelectedIndexChanged aufgerufen wird, zb auch beim ändern der Liste.
    Also musst du prüfen das es auch ein SelectedItem gibt.



  • private void checkedListBox1_SelectedIndexChanged(object sender, EventArgs e)
     {
                int index = checkedListBox1.SelectedIndex;
                if( index >= 0 )
                    checkedListBox1.Items[index] = "text";   
    }
    

    Gut, das funktioniert aber nur genau ein mal. Beim zweiten Klick kommt

    Eine nicht behandelte Ausnahme des Typs "System.StackOverflowException" ist in
    System.Windows.Forms.dll aufgetreten.
    

    😕



  • Rekursion ohne Abbruchkriterium:

    checkedListBox1.Items[index] = ... ruft checkedListBox1_SelectedIndexChanged(..) auf und dadrin checkedListBox1.Items[index] = ... ruft checkedListBox1_SelectedIndexChanged(..) auf und dadrin...



  • Das ist aber ein ganz schön zickiges Steuerelement. Eigentlich möchte ich bloß in Abhängigkeit davon, ob der Haken drin ist oder nicht, einen Text zuweisen.

    So geht es zwar,

    private void checkedListBox1_SelectedIndexChanged(object sender, EventArgs e)
      {
                int index = 0;
                if (checkedListBox1.SelectedIndex < 0)
                    return;
                index = checkedListBox1.SelectedIndex;
                if (true == checkedListBox1.GetItemChecked(index) && checkedListBox1.Items[index].ToString() == "on")
                    return;
                if (false == checkedListBox1.GetItemChecked(index) && checkedListBox1.Items[index].ToString() == "off")
                    return;
                if (true == checkedListBox1.GetItemChecked(index) && checkedListBox1.Items[index].ToString() != "on")
                    checkedListBox1.Items[index] = "on";
                if (false == checkedListBox1.GetItemChecked(index) && checkedListBox1.Items[index].ToString() != "off")
                    checkedListBox1.Items[index] = "off";
    }
    

    es lahmt aber ganz schön, wenn ich zu schnell dasselbe Kästchen anklicke, dann kommt die Textänderung nicht mit. 😃



  • Benutze eine private Membervariable als Flag:

    private void checkedListBox1_SelectedIndexChanged(object sender, EventArgs e)
    {
        if(flag) return;
    
        int index = checkedListBox1.SelectedIndex;
        if( index >= 0 )
        {
            flag = true;
            checkedListBox1.Items[index] = "text";
            flag = false;
        }
    }
    
    private bool flag = false;
    

    Warum sich allerdings der SelectedIndex beim Ändern eines Textes ändert? Hast du evtl. noch andere Events für die CheckedListBox abonniert?

    Schau dir mal im Debugger genau den 'StackTrace' an...



  • äh stimmt, hatte irgendwie sowas wie selected text changed gelesen. Ist natürlich blödsinn... also jedefalls die Hälfte. 🙄

    Edit:
    So, habs mir mal angeguckt. Natürlich tut ändern des Texts nicht den SelectedIdexChanged event abfeuern, jedoch tut das die Zuweisung via Indexer.
    Das muss man, weil string immutable ist. Abhilfe schafft z.B. folgende Lösung:
    (Zurückspeichern via Indexer nicht mehr nötig, da Item ein mutable Referenztyp ist).

    using System;
    using System.Windows.Forms;
    
    namespace Test
    {
        public partial class Form1 : Form
        {
            class Item
            {
                public Item() : this(string.Empty) { }
                public Item(string text)
                {
                    Text = text;
                }
    
                public string Text { get; set; }
    
                public override string ToString()
                {
                    return Text;
                }
            }
    
            public Form1()
            {
                InitializeComponent();
    
                checkedListBox1.Items.Add(new Item("item 1"));
                checkedListBox1.Items.Add(new Item("item 2"));
                checkedListBox1.Items.Add(new Item("item 2"));
            }
    
            private void checkedListBox1_SelectedIndexChanged(object sender, EventArgs e)
            {
                int index = checkedListBox1.SelectedIndex;
                if (index != -1)
                {
                    Item item = (Item)checkedListBox1.Items[index];
                    item.Text += " - changed";
                }
            }
        }
    }
    


  • @Th69
    Nein, der CheckedListBox ist nur ein Event zugeordnet.
    Deine Lösung funktioniert, aber das Problem der Trägheit bleibt. Bei schnellen Klicks kommt die Textänderung nicht hinterher. Um sicher zu gehen, dass kein anderer Code bremst, habe ich ein eigentändiges Projekt mit einem Form und einer CheckedListBox erstellt. Auch hier besteht das Problem der Trägheit bei schnellen Klicks auf dasselbe Element.

    namespace CheckedListBox
    {
        public partial class Form1 : Form
        {
            bool flag = false;
            public Form1()
            {
                InitializeComponent();
                checkedListBox1.Items.Add("off");
                checkedListBox1.Items.Add("off");
                checkedListBox1.Items.Add("off");
                checkedListBox1.Items.Add("off");
                checkedListBox1.Items.Add("off");
                checkedListBox1.Items.Add("off");
            }
    
            private void checkedListBox1_SelectedIndexChanged(object sender, EventArgs e)
            {
                if (flag == true) return;
    
                int index = checkedListBox1.SelectedIndex;
    
                if (index > -1)
                {
                    flag = true;
                    if (true == checkedListBox1.GetItemChecked(index))
                        checkedListBox1.Items[index] = "on";
                    else
                        checkedListBox1.Items[index] = "off";
                    flag = false;
                } 
            }
        }
    }
    

    Ich vermute mal, an der Trägheit lässt sich nichts ändern. Da muss ich mir wohl eine andere Möglichkeit suchen.

    @theta
    Danke für deine Mühe, aber die Zeile

    Item item = (Item)checkedListBox1.Items[index];
    

    löst bei mir ne InvalidCastException aus

    Das Objekt des Typs "System.String" kann nicht in Typ "Item" umgewandelt werden.



  • Das gesamte Konzept gefällt mir überhaupt nicht.
    Besser ist es ein Objekt in die Liste zu packen, und dann nur mit diesen zu arbeiten.

    Meine erste Grobe Idee wäre folgende (nur hier im Forum getippt, nichts Probiert)

    public class Item
    {
        public bool IsEnabled { get; set; }
        public string Title
        {
            get
            {
                return IsEnabled ? "On" : "Off";
            }
        }
    }
    
    public partial class Form1 : Form
    {
        public List<Item> Items { get; set; }
    
        public Form1()
        {
            InitializeComponent();
    
            Items = new List<Item>();
    
            GenerateItems(10, false);
    
            checkedListBox1.DataSource = Items;
            checkedListBox1DisplayMember = "Title";
        }
    
        private void GenerateItems(int count, bool initState)
        {
            Items.Clear();
            for (int i = 0; i < count; ++i)
            {
                Items.Add(new Item { IsEnabled = initState });
            }
        }
    
        private void checkedListBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            int index = checkedListBox1.SelectedIndex;
            InverseCheckedItemState(index);
        }
    
        private void InverseCheckedItemState(int index)
        {
            if (index >= 0 &&
                index < Items.Count)
            {
                Items[index].IsEnabled = !Items[index].IsEnabled;
            }
        }
    }
    

    Das Inverse habe ich in einer separaten Methoden damit es testbar ist 😉 Dem Single Responsibility Principle folge dadurch auch.

    Ob es funktioniert kann man ja mal testen

    [TestMethod]
    public void GenerateItems_20DisabledItemsGenerated()
    {
        Form1_Accessor form = new Form1_Accessor();
        form.GenerateItems(20, false);
    
        IEnumerable<bool> states = form.Items.Select(i => i.IsEnabled).Distinct();
        Assert.IsTrue(states.Count() == 1, "Not all items are disabled properly");
    
        Assert.IsTrue(form.Items.Count == 20, "More or less then 20 items are generated");
    }
    
    [TestMethod]
    public void InverseCheckedItemState_Item10IsChanged()
    {
        Form1_Accessor form = new Form1_Accessor();
        form.GenerateItems(20, false);
        form.InverseCheckedItemState(9);
    
        Assert.IsTrue(form.Items[9].IsEnabled, "The state isn't changed");
    }
    

    PS.
    In WPF würde die Lösung vermutlich ähnlich aussehen, nur das ich dort dann "Title" vermutlich nicht habe sondern das im DataTemplate trigger lösen würde.



  • Meine WPF Lösung wäre vermutlich:

    public class Item : INotifyPropertyChanged
    {
        public bool IsEnabled
        {
            get { return _isEnabled; }
            set
            {
                _isEnabled = value;
                OnPropertyChanged("IsEnabled");
            }
        }
        private bool _isEnabled;
    }
    
    public partial class MainWindow : Window
    {
        public ObservableCollection<Item> Items { get; set; }
    
        public MainWindow()
        {
            InitializeComponent();
            DataCOntext = this;
    
            Items = new ObservableCollection<Item>();
    
            GenerateItems(10, false);
        }
    
        private void GenerateItems(int count, bool initState)
        {
            Items.Clear();
            for (int i = 0; i < count; ++i)
            {
                Items.Add(new Item { IsEnabled = initState });
            }
        }
    }
    
    <ListBox ItemsSource="{Binding Items}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <CheckBox x:Name="Checkbox" IsChecked="{Binding IsEnabled}" Margin="1" Content="Off" />
                <DataTemplate.Triggers>
                    <DataTrigger Binding="{Binding IsEnabled}" Value="True">
                        <Setter TargetName="Checkbox" Property="Content" Value="On" />
                    </DataTrigger>
                </DataTemplate.Triggers>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
    

    Und natürlich:

    [TestMethod]
    public void GenerateItems_20DisabledItemsGenerated()
    {
        MainWindow_Accessor window = new MainWindow_Accessor();
        window.GenerateItems(20, false);
    
        IEnumerable<bool> states = window.Items.Select(i => i.IsEnabled).Distinct();
        Assert.IsTrue(states.Count() == 1, "Not all items are disabled properly");
    
        Assert.IsTrue(window.Items.Count == 20, "More or less then 20 items are generated");
    }
    

    //Dazu:
    Wobei man sich überlegen sollte ob ein "On" und "Off" als Item Beschreibung aus reicht 😃 War ja nur ne Demo, richtiger Text sollte natürlich auch noch dazu.



  • WPF kommt für mein Vorhaben z.Zt. nicht in Frage. Hat das Vorteile gegenüber der Forms?
    Deinen Forms Teil habe ich mal ausprobiert, aber wie kann ich jetzt den Text ändern?
    Die Metode SetTextItemState funktioniert nicht

    Argument Exception
    Die Items-Auflistung kann nicht geändert werden, wenn die DataSource-Eigenschaft festgelegt ist.

    namespace CheckedListBox
    {
        public partial class Form1 : Form
        {
            public List<Item> Items { get; set; }
    
            public Form1()
            {
                InitializeComponent();
                Items = new List<Item>();
                GenerateItems(10, false);
                checkedListBox1.DataSource = Items;
            }
    
            private void GenerateItems(int count, bool initState)
            {
                Items.Clear();
                for (int i = 0; i < count; ++i)
                {
                    Items.Add(new Item { IsEnabled = initState });
                }
            }
    
            private void checkedListBox1_SelectedIndexChanged(object sender, EventArgs e)
            {
                int index = checkedListBox1.SelectedIndex;
                InverseCheckedItemState(index);
                SetTextItemState(index);
            }
    
            private void SetTextItemState(int index)
            {
                if (index >= 0 && index < Items.Count)
                {
                    checkedListBox1.Items[index] = Items[index].Title;
                }
            } 
    
            private void InverseCheckedItemState(int index)
            {
                if (index >= 0 &&
                    index < Items.Count)
                {
                    Items[index].IsEnabled = !Items[index].IsEnabled;
                }
            }
        }
    
        public class Item
        {
            public bool IsEnabled { get; set; }
            public string Title
            {
                get
                {
                    return IsEnabled ? "On" : "Off";
                }
            }
        }
    }
    

  • Administrator

    Wieso um alles in der Welt geht ihr alle über das Event SelectedIndexChanged ? Dabei gibt es doch extra ein Event ItemCheck .

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
    
            checkedListBox1.Items.Add("unchecked");
            checkedListBox1.Items.Add("unchecked");
            checkedListBox1.Items.Add("unchecked");
        }
    
        private void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            int index = e.Index;
    
            if(e.NewValue == CheckState.Checked)
            {
                checkedListBox1.Items[index] = "checked";
            }
            else
            {
                checkedListBox1.Items[index] = "unchecked";
            }
        }
    }
    

    Geht bei mir ohne Probleme. Ob man nun noch einen Item-Wrapper braucht kommt ganz darauf an, was man eigentlich in die ListBox reinsetzt und wie man damit arbeiten möchte.

    Grüssli



  • Funktioniert perfekt und lahmt nicht! 👍

    Dravere schrieb:

    Wieso um alles in der Welt geht ihr alle über das Event SelectedIndexChanged ?

    Ja, ich sollte ausführlicher die Doku in der MSDN lesen und mich nicht gleich mit der Funktion zufrieden geben, die beim Doppelklick auf ein Steuerelement von der IDE automatisch generiert wird.
    Vielen Dank für deine Hilfe!



  • -com- schrieb:

    @theta
    Danke für deine Mühe, aber die Zeile

    Item item = (Item)checkedListBox1.Items[index];
    

    löst bei mir ne InvalidCastException aus

    Das Objekt des Typs "System.String" kann nicht in Typ "Item" umgewandelt werden.

    Äh, ja, war mir nicht bewusst, dass Du ja nix von dem Ganzen hier verstehst.. vergebene Liebesmüh also. Tja, was solls... ev hilfts wem mit mehr was auch immer im ...



  • theta schrieb:

    Äh, ja, war mir nicht bewusst, dass Du ja nix von dem Ganzen hier verstehst.. vergebene Liebesmüh also. Tja, was solls... ev hilfts wem mit mehr was auch immer im ...

    Das siehst du alles viel zu düster! 🙂


Anmelden zum Antworten