Reputation: 682
I have a class, KeyCombos
that contains a Modifier Keys
and Keys
property:
private ModifierKeys _modifierKeys;
private Key _key;
public ModifierKeys ModifierKeys
{
get { return _modifierKeys; }
set
{
if (_modifierKeys == value)
return;
_modifierKeys = value;
RaisePropertyChanged(nameof(ModifierKeys));
}
}
public Key Key
{
get { return _key; }
set
{
if (_key == value)
return;
_key = value;
RaisePropertyChanged(nameof(Key));
}
}
public KeyCombo(ModifierKeys modifierKeys, Key key)
{
Key = key;
ModifierKeys = modifierKeys;
}
where RaisePropertyChanged
is used to implement INotifyPropertyChanged
.
In my ViewModel
, I have properties that bind to various key combinations, for example:
private KeyCombo _firstKeyCombo;
public KeyCombo FirstKeyCombo
{
get { return _firstKeyCombo; }
set
{
if (_firstKeyCombo == value)
return;
_firstKeyCombo = value;
// Validation method called here
RaisePropertyChanged(nameof(FirstKeyCombo));
}
}
And each part of the KeyCombo
is set in the ViewModel
, i.e.:
FirstKeyCombo = new KeyCombo(ModifierKeys.Alt, Key.T);
The ViewModel also defines:
public IEnumerable<Key> Keys => _availableKeys;
ObservableCollection<Key> _availableKey; // set to all available keys
public IEnumerable<ModifierKeys> Modifiers => _modifierKeys;
ObservableCollection<ModifierKeys> _modifierKeys; // set to all modifier keys
Then bound in the View
to ComboBoxs
:
<StackPanel Orientation="Horizontal" Grid.Row="13" Grid.Column="2">
<ComboBox ItemsSource="Keys"
SelectedItem="{Binding FirstKeyCombo.Key,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"/>
<ComboBox ItemsSource="Modifiers"
SelectedItem="{Binding FirstKeyCombo.ModifierKey,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
This works fine, the appropriate Key
and ModifierKeys
display properly. I need update the FirstKeyCombo
via the setter when either the Key
or ModiferKeys
is changed (validation over all of the KeyCombo
properties in the ViewModel). Obviously, my attempt to use INotifyPropertyChanged
does not successfully do this. What should I do to ensure that if either property (ModiferKeys
or Keys
) is changed via the Combobox
, FirstKeyCombo
gets set?
Upvotes: 0
Views: 77
Reputation: 982
In your ViewModel you shoud "listen" to the PropertyChanged of your Properties and set a new KeyCombo.
So after creating your FirstKeyCombo, add the PropertyChangedEventhandler
FirstKeyCombo.PropertyChanged += OnFirstKeyComboPropertyChanged;
which should look like this:
private void OnFirstKeyComboPropertyChanged(object sender, PropertyChangedEventArgs e)
{
//remove the handler
FirstKeyCombo.PropertyChanged -= OnFirstKeyComboPropertyChanged;
//create new object because of referential equlity check in WPF
FirstKeyCombo = new KeyCombo(FirstKeyCombo.ModifierKeys, FirstKeyCombo.Key);
//add the handler to the new object
FirstKeyCombo.PropertyChanged += OnFirstKeyComboPropertyChanged;
}
Just calling
RaisePropertyChanged(nameof(FirstKeyCombo));
in the event handler would not suffice, because there is a check for referential equality, which would have resulted in no new object.
As to the comment, this is how it would be done with Reflection. KeyCombo.Name must be the name of the Property.
private void OnFirstKeyComboPropertyChanged(object sender, PropertyChangedEventArgs e)
{
KeyCombo combo = sender as KeyCombo;
//remove the handler
combo.PropertyChanged -= OnFirstKeyComboPropertyChanged;
//create new object because of referential equlity check in WPF
combo = new KeyCombo(combo.ModifierKeys, combo.Key, combo.Name);
//add the handler to the new object
combo.PropertyChanged += OnFirstKeyComboPropertyChanged;
//Get the ViewModel-Type
Type t = ViewModel.GetType();
//Get the property with the name
PropertyInfo pi = t.GetProperty(combo.Name);
//set the value of the property
pi.SetValue(ViewModel,combo);
}
or with a dictionary. Also KeyCombo.Name must be the name of the Property.
private void OnFirstKeyComboPropertyChanged(object sender, PropertyChangedEventArgs e)
{
KeyCombo combo = sender as KeyCombo;
//remove the handler
combo.PropertyChanged -= OnFirstKeyComboPropertyChanged;
//create new object because of referential equlity check in WPF
combo = new KeyCombo(combo.ModifierKeys, combo.Key, combo.Name);
//add the handler to the new object
combo.PropertyChanged += OnFirstKeyComboPropertyChanged;
dictionaryWithAllCombos[combo.Name] = combo;
RaisePropertyChanged(combo.Name);
}
in the ViewModel
public FirstKeyCombo FirstKeyCombo
{
get { return dictionary["FirstKeyCombo"]; }
set
{
dictionaryWithAllCombos["FirstKeyCombo"] = value;
RaisePropertyChanged(nameof(FirstKeyCombo));
}
}
Upvotes: 2