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?