How can I simulate the effects of an observable collection in this situation?

Posted by MGSoto on Stack Overflow See other posts from Stack Overflow or by MGSoto
Published on 2010-04-15T21:13:21Z Indexed on 2010/04/15 23:43 UTC
Read the original article Hit count: 226

Filed under:
|
|
|
|

I am making a configuration editor for another application and am using reflection to pull editable fields from the configuration class. The following class is the base class for my various "DataTypeViewModels" and shows how I get and set the appropriate properties.

public abstract class DataTypeViewModel<T> : ViewModelBase
{
    Func<T> getFunction;

    Action<T> setAction;

    public const string ValuePropertyName = "Value";

    public string Label { get; set; }

    public T Value
    {
        get
        {
            return getFunction.Invoke();
        }

        set
        {
            if (getFunction.Invoke().Equals(value))
            {
                return;
            }

            setAction.Invoke(value);

            // Update bindings, no broadcast
            RaisePropertyChanged(ValuePropertyName);
        }
    }

     /// <summary>
    /// Initializes a new instance of the StringViewModel class.
    /// </summary>
    public DataTypeViewModel(string sectionName, string label)
    {
        if (IsInDesignMode)
        {
            // Code runs in Blend --> create design time data.
        }
        else
        {
            Label = label;

            getFunction = new Func<T>(() =>
                {
                    return (T)Settings.Instance.GetType().GetProperty(sectionName).PropertyType.
                        GetProperty(label).GetValue(Settings.Instance.GetType().GetProperty(sectionName).GetValue(Settings.Instance, null), null);
                });

            setAction = new Action<T>(value =>
                {
                    Settings.Instance.GetType().GetProperty(sectionName).PropertyType.GetProperty(label).
                        SetValue(Settings.Instance.GetType().GetProperty(sectionName).GetValue(Settings.Instance, null), value, null);
                });
        }
    }
}

This part works the way I want it to, the next part is a sample DataTypeViewModel on a list of strings.

public class StringListViewModel : DataTypeViewModel<ICollection<string>>
{
    /// <summary>
    /// The <see cref="RemoveItemCommand" /> property's name.
    /// </summary>
    public const string RemoveItemCommandPropertyName = "RemoveItemCommand";

    private RelayCommand<string> _removeItemCommand = null;

    public ObservableCollection<string> ObservableValue { get; set; }

    /// <summary>
    /// Gets the RemoveItemCommand property.
    /// TODO Update documentation:
    /// Changes to that property's value raise the PropertyChanged event. 
    /// This property's value is broadcasted by the Messenger's default instance when it changes.
    /// </summary>
    public RelayCommand<string> RemoveItemCommand
    {
        get
        {
            return _removeItemCommand;
        }

        set
        {
            if (_removeItemCommand == value)
            {
                return;
            }

            var oldValue = _removeItemCommand;
            _removeItemCommand = value;

            // Update bindings, no broadcast
            RaisePropertyChanged(RemoveItemCommandPropertyName);
        }
    }

    /// <summary>
    /// The <see cref="AddItemCommand" /> property's name.
    /// </summary>
    public const string AddItemCommandPropertyName = "AddItemCommand";

    private RelayCommand<string> _addItemCommand = null;

    /// <summary>
    /// Gets the AddItemCommand property.
    /// TODO Update documentation:
    /// Changes to that property's value raise the PropertyChanged event. 
    /// This property's value is broadcasted by the Messenger's default instance when it changes.
    /// </summary>
    public RelayCommand<string> AddItemCommand
    {
        get
        {
            return _addItemCommand;
        }

        set
        {
            if (_addItemCommand == value)
            {
                return;
            }

            var oldValue = _addItemCommand;
            _addItemCommand = value;

            // Update bindings, no broadcast

            RaisePropertyChanged(AddItemCommandPropertyName);
        }
    }

    /// <summary>
    /// Initializes a new instance of the StringListViewModel class.
    /// </summary>
    public StringListViewModel(string sectionName, string label) : base(sectionName, label)
    {
        ObservableValue = new ObservableCollection<string>(Value);
        AddItemCommand = new RelayCommand<string>(param =>
            {
                if (param != string.Empty)
                {
                    Value.Add(param);
                    ObservableValue.Add(param);
                }
            });

        RemoveItemCommand = new RelayCommand<string>(param =>
            {
                if (param != null)
                {
                    Value.Remove(param);
                    ObservableValue.Remove(param);
                }
            });
    }
}

As you can see in the constructor, I currently have "Value" mirrored into a new ObservableCollection called "ObservableValue", which is then bound to by a ListView in the XAML. It works well this way, but cloning the List seems like such a hacky way to do this. While bound to Value, I've tried adding:

RaisePropertyChanged("Value");

to the AddItemCommand and RemoveItemCommand, but this doesn't work, the ListView won't get updated. What is the proper way to do this?

© Stack Overflow or respective owner

Related posts about c#

Related posts about mvvm