views:

328

answers:

2

I'm attempting to utilize the .NET 2.0 ApplicationSettings feature for the first time, and find it a bit... puzzling in some ways. I'm hoping someone can help me figure out where i'm going wrong.

I have a generic settings class which i've implemented that is a subclass of ApplicationSettingsBase. I've implemented a property, and tagged it. This seems to work

I've then tried to bind a listbox control to the property, and this also seems to work. When I open the form, it loads the properties just fine.

The problem i'm running into is that if I want to update the settings and databinding without reloading the form, it doesn't seem to work. There are no methods to refresh the binding, and from what i'm reading, it should just auto-update (ApplicationSettingsBase implements IPropertyChangeNotify, which DataBindings should subscribe to.. but it doesn't seem to be working). You also can't manually update the listbox if you have a DataBinding.

So here's my code:

Settings.cs

public class Settings : ApplicationSettingsBase
{
    public Settings()
    {
        if (FixedBooks == null) FixedBooks = new List<string>();
    }

    [UserScopedSetting()]
    public List<string> FixedBooks
    {
        get { return (List<string>)this["FixedBooks"]; }
        protected set { this["FixedBooks"] = value; }
    }
}

SettingsForm.cs

Settings _settings = new Settings();

private void SettingsForm_Load(object sender, EventArgs e)
{
    lbFixedColumns.DataBindings.Add(new Binding("DataSource", _settings,
        "FixedBooks", false, DataSourceUpdateMode.OnPropertyChanged));
}

private void DoSomething()
{
    _settings.FixedBooks.Add("Test");
}

My understanding is that adding something to the ApplicationSettings should fire the IPropertyChangedNotify to alert the control binding that the property has changed, and force it to reload.. but this doesn't seem to be happening.

What am I missing?

EDIT:

I believe I know what the problem is. The problem is that i'm altering the contentes of the collection in the Settings class, but not changing the actual property itself (which is the collection itself). I think i would have to actually add or remove the entire collection to get the IPropertyChangedNotify to fire, which isn't happening.

I'm not sure what the solution to this problem is.

+1  A: 

You need to save the setting and reload it:

private void DoSomething()
{
    _settings.FixedBooks.Add("Test");
    _settings.Save();
    _settings.Reload();
}

I agree with your edit and your comment. Try using a BindingList instead.

Example:

public class Settings : ApplicationSettingsBase
{
    public Settings()
    {
        if (FixedBooks == null) FixedBooks = new BindingList<string>();
        FixedBooks.ListChanged += FixedBooks_ListChanged;
    }


    void FixedBooks_ListChanged(object sender, ListChangedEventArgs e)
    {
        this["FixedBooks"] = FixedBooks;
    }

    [UserScopedSetting()]
    public BindingList<string> FixedBooks
    {
        get { return (BindingList<string>)this["FixedBooks"]; }
        protected set { this["FixedBooks"] = value; }
    }
}
drs9222
That is not a good option, because it destroys the ability to cancel without saving settings. I only want to call Save() when I hit Apply or OK.
Mystere Man
Doh! This is one of the problems with .NET, it's so huge that sometimes you just can't find this stuff... 4 hours of coming up with workarounds down the toilet with the solution that this is deisgned for...
Mystere Man
A: 

Ok, not a great solution, and I'm hoping someone has a better one, but this works:

private void DoSomething()
{
    _settings.FixedBooks.Add("Test");    
    var old = _settings.FixedBooks;
    _settings["FixedBooks"] = new List<string>();
    _settings["FixedBooks"] = old;
}

I tried simply reassigning itself to itself, but there is apparently some optimziation in there that detects this and fails to fire the PropertyChanged.

I'd be more than happy to give an answer to someone that comes up with a better solution.

EDIT:

Ok, I think i've found an acceptable solution that's only a little kludgey, and doesn't require the creation of new objects like the above for no reason.

Ok, first, I add a new method to my Settings class:

public void FirePropertyChanged(string propertyName)
{
    PropertyChangedEventArgs args2 = new PropertyChangedEventArgs(propertyName);
    OnPropertyChanged(this, args2);
}

This allows me to trigger a PropertyChanged event without actually changing the property. However, the Binding class is still a little too smart for me, because it knows that the property hasn't actually changed.. so it does nothing.

private void DoSomething()
{
    _settings.FixedBooks.Add("Test");
    lbFixedColumns.DataSource = null;
    _settings.FirePropertyChanged("FixedBooks");
}

So, this code fakes out the Binding class, by clearing the DataSource it now can't tell that the property hasn't changed, so it has to pull the latest version of the data. I can live with this, even though it's still a little stinky.

EDIT2:

As drs points out, I should have been using a BindingList rather than a List, using a BindingList is the correct solution.

Mystere Man