eye_am_groot
eye_am_groot

Reputation: 682

Custom Class Setter Not Updating

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

Answers (1)

Andreas
Andreas

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

Related Questions