I have 2 classes: ParentClass
and ChildClass
and both are used as datacontext for UserControl with similar hierarchy.
I want to have parent class be notified when it's custom class property has a change in one of it's variables.
public class ParentClass
{
private ChildClass _child;
private int _value;
public ChildClass Child
{
get
{
if (_child == null)
Child = new ChildClass();
return _child;
}
set
{
_child = value;
Value = _child.Score;
OnPropertyChanged(nameof(Child));
}
}
public int Value
{
get { return _value; }
set
{
_value = value;
OnPropertyChanged(nameof(Value));
}
}
}
public class ChildClass
{
private int _score;
public int Score
{
get { return _score; }
set
{
_score = value;
OnPropertyChanged(nameof(Score));
}
}
}
What I want is that on change of Score
, set
part of Parent class's Child
property is executed and Value
updated.
How do I do that?
Edit: Code
PropertyChangedNotify
internal class PropertyChangedNotify : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged(string? property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
Proficiency_Counter - xaml
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text=""/>
<Viewbox Grid.Column="1">
<ComboBox ItemsSource="{Binding ProficiencyList}" SelectedItem="{Binding Proficiency}" Margin="1">
<ComboBox.ItemTemplate>
<DataTemplate>
<Viewbox>
<TextBlock Text="{Binding}"/>
</Viewbox>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Viewbox>
</Grid>
Proficiency_Counter_Model - aka ChildClass
internal class Proficiency_Counter_Model: PropertyChangedNotify
{
#region private
private int _proficiencyScore;
private List<char> _proficiencyList;
private char _proficiency;
private int _level;
#endregion
#region public
public int ProficiencyScore
{
get { return _proficiencyScore; }
set
{
_proficiencyScore = value;
OnPropertyChanged(nameof(ProficiencyScore));
}
}
public List<char> ProficiencyList
{
get
{
if (_proficiencyList == null)
{
ProficiencyList = new List<char>();
ProficiencyList.Add('U');
ProficiencyList.Add('T');
ProficiencyList.Add('E');
ProficiencyList.Add('M');
ProficiencyList.Add('L');
Proficiency = 'U';
}
return _proficiencyList;
}
set
{
_proficiencyList = value;
OnPropertyChanged(nameof(ProficiencyList));
}
}
public char Proficiency
{
get { return _proficiency; }
set
{
_proficiency = value;
if (Proficiency == 'U')
{
ProficiencyScore = 0;
}
else if (Proficiency == 'T')
{
ProficiencyScore = 2 _level;
}
else if (Proficiency == 'E')
{
ProficiencyScore = 4 _level;
}
else if (Proficiency == 'M')
{
ProficiencyScore = 6 _level;
}
else if (Proficiency == 'L')
{
ProficiencyScore = 8 _level;
}
OnPropertyChanged(nameof(Proficiency));
}
}
public int Level
{
get { return _level; }
set
{
_level = value;
Proficiency = _proficiency;
OnPropertyChanged(nameof(Level));
}
}
#endregion
}
General_Skill_Counter_Model - aka ParentClass
internal class General_Skill_Counter_Model: PropertyChangedNotify
{
#region private
private string _skillName;
private int _abilityMod;
private Proficiency_Counter_Model _proficiency;
private int _proficiencyMod;
private int _itemMod;
#endregion
#region public
public string SkillName
{
get
{
if (_skillName == null)
SkillName = "Lorem Impsum";
return _skillName;
}
set
{
_skillName = value;
OnPropertyChanged(nameof(SkillName));
}
}
public int SkillScore
{
get
{
return (AbilityMod Proficiency.ProficiencyScore ItemMod);
}
}
public int AbilityMod
{
get { return _abilityMod; }
set
{
_abilityMod = value;
OnPropertyChanged(nameof(AbilityMod));
OnPropertyChanged(nameof(SkillScore));
}
}
public Proficiency_Counter_Model Proficiency
{
get
{
if (_proficiency == null)
{
_proficiency = new Proficiency_Counter_Model();
}
return _proficiency;
}
set
{
_proficiency = value;
ProficiencyMod = _proficiency.ProficiencyScore;
OnPropertyChanged(nameof(Proficiency));
}
}
public int ProficiencyMod
{
get { return _proficiencyMod; }
set
{
_proficiencyMod = value;
OnPropertyChanged(nameof(ProficiencyMod));
OnPropertyChanged(nameof(SkillScore));
}
}
public int ItemMod
{
get { return _itemMod; }
set
{
_itemMod = value;
OnPropertyChanged(nameof(ItemMod));
OnPropertyChanged(nameof(SkillScore));
}
}
#endregion
}
Proficiency_Counter_Model
is set as Proficiency_Counter
's DataContext, same for Genereal_Skill_Counter
, which I believe is irrelevant to this.
CodePudding user response:
Ok, I figured it out. Instead of notifying the parent, I made the child part of the parent.
Here's the code:
internal class Proficiency_Counter_Model: PropertyChangedNotify
{
#region private
protected int _proficiencyScore;
private List<char> _proficiencyList;
private char _proficiency;
protected int _level;
#endregion
#region public
public virtual int ProficiencyScore
{
get { return _proficiencyScore; }
set
{
_proficiencyScore = value;
OnPropertyChanged(nameof(ProficiencyScore));
}
}
public List<char> ProficiencyList
{
get
{
if (_proficiencyList == null)
{
ProficiencyList = new List<char>();
ProficiencyList.Add('U');
ProficiencyList.Add('T');
ProficiencyList.Add('E');
ProficiencyList.Add('M');
ProficiencyList.Add('L');
Proficiency = 'U';
}
return _proficiencyList;
}
set
{
_proficiencyList = value;
OnPropertyChanged(nameof(ProficiencyList));
}
}
public char Proficiency
{
get { return _proficiency; }
set
{
_proficiency = value;
if (Proficiency == 'U')
{
ProficiencyScore = 0;
}
else if (Proficiency == 'T')
{
ProficiencyScore = 2 _level;
}
else if (Proficiency == 'E')
{
ProficiencyScore = 4 _level;
}
else if (Proficiency == 'M')
{
ProficiencyScore = 6 _level;
}
else if (Proficiency == 'L')
{
ProficiencyScore = 8 _level;
}
OnPropertyChanged(nameof(Proficiency));
}
}
public int Level
{
get { return _level; }
set
{
_level = value;
Proficiency = _proficiency;
OnPropertyChanged(nameof(Level));
}
}
#endregion
}
I made the ProficiencyScore
property virtual so that I could override it.
internal class General_Skill_Counter_Model : Proficiency_Counter_Model
{
#region private
private string _skillName;
private int _skillScore;
private int _abilityMod;
private int _itemMod;
#endregion
#region public
public string SkillName
{
get
{
if (_skillName == null)
SkillName = "Lorem Impsum";
return _skillName;
}
set
{
_skillName = value;
OnPropertyChanged(nameof(SkillName));
}
}
public int SkillScore
{
get { return _skillScore; }
set
{
_skillScore = value;
OnPropertyChanged(nameof(SkillScore));
}
}
public int AbilityMod
{
get { return _abilityMod; }
set
{
_abilityMod = value;
OnPropertyChanged(nameof(AbilityMod));
}
}
public int ItemMod
{
get { return _itemMod; }
set
{
_itemMod = value;
OnPropertyChanged(nameof(ItemMod));
}
}
#endregion
#region override
public override int ProficiencyScore
{
get { return _proficiencyScore; }
set
{
SkillScore = _skillScore value - _proficiencyScore;
_proficiencyScore = value;
OnPropertyChanged(nameof(ProficiencyScore));
}
}
#endregion
}
CodePudding user response:
The general implementation principle is to listen for the PropertyChanged event of the child object. When replacing a child object, you need to unsubscribe from the event of the old object and subscribe to the event of the new object. In the event handler, the name of the changed property is checked and the necessary actions are taken.
The implementation depends on how you have implemented the base class for INotifyPropertyChanged. For example, this implementation of BaseInpc uses the "Set" method to set a property, which returns true if the value of the property has changed. For such an implementation, you can write the following code:
public class ChildClass : BaseInpc
{
private int _score;
public int Score { get => _score; set => Set(ref _score, value); }
}
public class ParentClass : BaseInpc
{
private ChildClass _child = new ChildClass();
private int _value;
public ChildClass Child
{
get => _child;
set
{
var old = _child;
if (Set(ref _child, value ?? new ChildClass()))
{
old.PropertyChanged -= OnChildPropertyChanged;
_child.PropertyChanged = OnChildPropertyChanged;
Value = Child.Score * 2;
}
}
}
private void OnChildPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(ChildClass.Score) ||
string.IsNullOrEmpty(e.PropertyName))
{
Value = Child.Score * 2;
}
}
public int Value { get => _value; set => Set(ref _value, value); }
public ParentClass() => Child = new ChildClass();
}
Also in this implementation, you can lighten the logic of the property by using an override of the OnPropertyChanged virtual method instead:
public class ParentClass : BaseInpc
{
private ChildClass _child = new ChildClass();
private int _value;
public ChildClass Child { get => _child; set => Set(ref _child, value ?? new ChildClass()); }
public int Value { get => _value; set => Set(ref _value, value); }
public ParentClass() => Child = new ChildClass();
protected override void OnPropertyChanged(string propertyName, object oldValue, object newValue)
{
base.OnPropertyChanged(propertyName, oldValue, newValue);
if (propertyName == nameof(Child))
{
ChildClass oldChild = (ChildClass)oldValue;
oldChild.PropertyChanged -= OnChildPropertyChanged;
ChildClass newChild = (ChildClass)newValue;
newChild.PropertyChanged = OnChildPropertyChanged;
Value = newChild.Score * 2;
}
}
private void OnChildPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(ChildClass.Score) ||
string.IsNullOrEmpty(e.PropertyName))
{
Value = Child.Score * 2;
}
}
}