How do I change two observable properties, based on each other, but prevent a deadlock?
For the sake of this example I’m keeping it very simple:
I’m counting two things, (1) items and (2) containers for these items. Every container can contain 3 items. The user can change both the amount of items and the amount of containers. Changing one will automatically change the other.
As you can imagine, this results in a complete deadlock.
Displaying values:
<Entry Text="{Binding Amount}"/>
<Entry Text="{Binding Sections}"/>
Setting values:
private int amount;
public int Amount
{
get => amount;
set
{
SetProperty(ref amount, value);
Sections = Convert.ToInt32(Math.Round(Amount / 3));
}
}
private int sections;
public int Sections
{
get => sections;
set
{
SetProperty(ref sections, value);
Amount = Sections * 3;
}
}
How do I prevent a deadlock, e.g. changing the properties only once when it is user-invoked?
2
You can implement a flag so that only the property you are setting triggers the setting of the other. You set sections, it updates sections and amount, but amount does not trigger the sections update.
public class TestClass
{
private bool _updating = false;
private int _amount;
public int Amount
{
get => _amount;
set
{
_amount = value;
if (!_updating)
{
_updating = true;
Sections = Convert.ToInt32(Math.Round(Convert.ToDouble(Amount / 3)));
_updating = false;
}
}
}
private int _sections;
public int Sections
{
get => _sections;
set
{
_sections = value;
if (!_updating)
{
_updating = true;
Amount = _sections * 3;
_updating = false;
}
}
}
}
You can then call the setters without having to worry about the infinite loop:
static void Main()
{
var tc = new TestClass();
Console.WriteLine("Set sections = 5");
tc.Sections = 5;
Console.WriteLine($"Sections: {tc.Sections}; Amount: {tc.Amount}");
Console.WriteLine("Set amount = 10");
tc.Amount = 10;
Console.WriteLine($"Sections: {tc.Sections}; Amount: {tc.Amount}");
}
Yields this result:
Set sections = 5
Sections: 5;
Amount: 15
Set amount = 10
Sections: 3;
Amount: 10
create two methods to update the properties
void SetAmount(int amount)
{
this.Amount = amount;
Sections = Convert.ToInt32(Math.Round(Amount / 3));
}
void SetSections(int sections)
{
this.Sections = sections;
Amount = Sections * 3;
}
this way the properties setters are not recursively calling each other
Rather than having two separate values and trying to maintain data integrity between them, just have only one stored value and let one of the properties be an entirely computed value. This not only means that there’s no recursion to address, but there’s no possible state for the object that’s “invalid”.
private int amount;
public int Amount
{
get => amount;
set
{
SetProperty(ref amount, value);
}
}
public int Sections
{
get => (int)Math.Round(Amount / 3);
set => Amount = Sections * 3;
}