views:

47

answers:

2

I have a ViewModel that encapsulates some properties that are being edited in an options dialog. I can't actually save them to the settings until they hit the Ok button, which will end up calling Commit on this particular ViewModel.

A single property in my ViewModel looks like this:

public bool SomeProperty
{
    get
    {
        return m_SomeProperty;
    }
    set
    {
        if (m_SomeProperty != value)
        {
            m_SomeProperty = value;
            NotifyPropertyChanged("SomeProperty");
        }
    }
}
private bool m_SomeProperty = Properties.Settings.Default.SomeProperty;

So the normal implementation for Commit would be to do this:

public void Commit()
{
    Properties.Settings.Default.SomeProperty = m_SomeProperty;
    // Add other properties here...
}

This isn't so bad, but the reason I don't like this is that if you add a new property, you have to add code for it in two places. I try to avoid that when possible.

At first I thought I could declare a private event called OnCommit and have the Commit method raise that event, and have the code for each property add an event handler for the event and do the writing to the settings there, but I don't know how to do that without adding the event handlers in the constructor anyway, which doesn't help the situation.

Any ideas? Does anyone have an elegant way to do what I'm trying to do?

EDIT: Thanks to sixlettervariables for the answer. I took that idea and incorporated it into SoapBox Core and open sourced the result. Check out the Options Dialog to see how it works.

A: 

Could you use reflection to determine which properties your class has and iterate through them?

Gribbler
Not sure. Maybe if paired with some kind of lambda function...
Scott Whitlock
+4  A: 

Perhaps maintain a list of Actions to execute?

private List<Action> commitActions = new List<Action>();

public bool SomeProperty
{
    get
    {
        return m_SomeProperty;
    }
    set
    {
        if (m_SomeProperty != value)
        {
            m_SomeProperty = value;
            lock (commitActions)
            {
                commitActions.Add(
                    () => Properties.Settings.Default.SomeProperty = value);
            }
            NotifyPropertyChanged("SomeProperty");
        }
    }
}

Then update your Commit code to loop through the actions.

public void Commit()
{
    List<Action> commits;
    lock (commitActions)
    {
        commits = new List<Action>(commitActions);
        commitActions.Clear();
    }

    foreach (var commit in commits)
    {
        commit();
    }
}
sixlettervariables
+1 This sounds like the best way to me.
Gabe Moothart
This is a very cool idea. I realized I'm going to need it for the cancel actions as well (to reset them to the saved values). A question though, if I only access this from the GUI, do I need the lock?
Scott Whitlock
If you truly will only have one thread accessing `commitActions`, then no you do not need the lock. The lock was merely a suggestion for if the code gets more involved. Moreover, you may look into creating your own Undo/Redo classes which contain an `Action` for Undoing and Redoing whatever happened.
sixlettervariables
True, but this is only for app settings, so I don't really need undo and redo. I'm actually handling undo and redo at the model level because they're all implemented as Immutable classes, so I can just keep a stack/queue of the last X world objects. Thanks though, this is perfect.
Scott Whitlock