Search code examples
c#.netwinformscheckedlistboxsettings.settings

Save CheckedListBox Items to Settings


I am trying to maintain a list of checked items in a CheckedListBox in User Settings and then reload them upon Application load.

In my Settings.settings file, I added the following:

<Setting Name="obj" Type="System.Windows.Forms.CheckedListBox.ObjectCollection" Scope="User">
    <Value Profile="(Default)" />
</Setting>

And, on chkList_ItemCheck, I am doing the following:

Properties.Settings.Default.obj = chkList.Items;
Properties.Settings.Default.Save()

But for some reason, when I exit the app, re-open, and check the value of Properties.Settings.Default.obj, it is null.

What am I doing wrong/missing?


Solution

  • Since CheckedListBox.CheckedItems property in not bindable to settings, you should add a string property and store checked items as comma separated string in settings and save settings when closing the form and set checked items of CheckedListBox at form Load.

    To save CheckedItems of a CheckedListBox:

    1. Add a Settings file to your project in Properties folder, or if you have that open it.
    2. Add a string setting property named CheckedItems
    3. In Load event of form, read checked items from settings, and set checked items in CheckedListBox using SetItemChecked.
    4. In FormClosing event, read CheckedItems of CheckedListBox and save in setting as comma separated string.

    Code:

    private void Form1_Load(object sender, EventArgs e)
    {
        if (!string.IsNullOrEmpty(Properties.Settings.Default.CheckedItems))
        {
            Properties.Settings.Default.CheckedItems.Split(',')
                .ToList()
                .ForEach(item =>
                {
                    var index = this.checkedListBox1.Items.IndexOf(item);
                    this.checkedListBox1.SetItemChecked(index, true);
                });
        }
    
    
    }
    
    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        var indices = this.checkedListBox1.CheckedItems.Cast<string>()
            .ToArray();
    
        Properties.Settings.Default.CheckedItems = string.Join(",", indices);
        Properties.Settings.Default.Save();
    }
    

    How do I know it is not bindable to settings?

    As first evidence, for each control in designer mode, in property grid, you can check +(ApplicationSettings) (PropertyBinding)[...] to see a list of properties that support property binding.

    Furthermore, you should know "Application settings uses the Windows Forms data binding architecture to provide two-way communication of settings updates between the settings object and components."[1]

    For example when you bind BackColor property of your CheckedListBox to a MyBackColor property, here is the code that the Designer generates for it:

    this.checkedListBox1.DataBindings.Add(
        new System.Windows.Forms.Binding("BackColor", 
        global::StackSamplesCS.Properties.Settings.Default, "MyBackColor", true,
        System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
    

    Now let's look at property definition [2]:

    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public CheckedListBox.CheckedItemCollection CheckedItems { get; }
    

    The property or indexer CheckedListBox.CheckedItems cannot be assigned to because it is read only

    Is there a better way?

    The short answer for this property is No.

    Suppose we have a property MyCheckedItems:

    [UserScopedSetting()]
    public CheckedListBox.CheckedItemCollection MyCheckedItems
    {
        get
        {
            return ((CheckedListBox.CheckedItemCollection)this["MyCheckedItems"]);
        }
        set
        {
            this["MyCheckedItems"] = (CheckedListBox.CheckedItemCollection)value;
        }
    }
    

    How can we or setting provider instantiate CheckedListBox.CheckedItemCollection?

    The type CheckedListBox.CheckedItemCollection has no public constructors defined

    So we can not instantiate it. The only constructor that CheckedListBox internally use it is [2]:

    internal CheckedItemCollection(CheckedListBox owner)
    

    Furthermore the only way to add an item to this collection is an internal method of CheckedItemCollection:

    internal void SetCheckedState(int index, CheckState value)
    

    So we can't do a better workaround for this property.

    More information:

    • For each control in designer mode, in property grid, you can check +(ApplicationSettings) (PropertyBinding)[...] to see a list of properties that support property binding.
    • Setting serialization works this way, it first attempts to call the ConvertToString or ConvertFromString on the type's associated TypeConverter. If this does not succeed, it uses XML serialization instead. [1] So For cases that you are not faced with read only properties or constructor-less classes, you can serialize value using a custom TypeConverter or a custom serializer.