@@ -667,6 +667,31 @@ public void OneWayToSource_Binding_Does_Not_Override_TwoWay_Binding()
667
667
Assert . Equal ( "TwoWay" , source . Foo ) ;
668
668
}
669
669
670
+ [ Fact ]
671
+ public void Target_Undoing_Property_Change_During_TwoWay_Binding_Does_Not_Cause_StackOverflow ( )
672
+ {
673
+ var source = new TestStackOverflowViewModel { BoolValue = true } ;
674
+ var target = new TwoWayBindingTest ( ) ;
675
+
676
+ source . ResetSetterInvokedCount ( ) ;
677
+
678
+ // The AlwaysFalse property is set to false in the PropertyChanged callback. Ensure
679
+ // that binding it to an initial `true` value with a two-way binding does not cause a
680
+ // stack overflow.
681
+ target . Bind (
682
+ TwoWayBindingTest . AlwaysFalseProperty ,
683
+ new Binding ( nameof ( TestStackOverflowViewModel . BoolValue ) )
684
+ {
685
+ Mode = BindingMode . TwoWay ,
686
+ } ) ;
687
+
688
+ target . DataContext = source ;
689
+
690
+ Assert . Equal ( 1 , source . SetterInvokedCount ) ;
691
+ Assert . False ( source . BoolValue ) ;
692
+ Assert . False ( target . AlwaysFalse ) ;
693
+ }
694
+
670
695
private class StyledPropertyClass : AvaloniaObject
671
696
{
672
697
public static readonly StyledProperty < double > DoubleValueProperty =
@@ -725,10 +750,24 @@ private class TestStackOverflowViewModel : INotifyPropertyChanged
725
750
726
751
public const int MaxInvokedCount = 1000 ;
727
752
753
+ private bool _boolValue ;
728
754
private double _value ;
729
755
730
756
public event PropertyChangedEventHandler PropertyChanged ;
731
757
758
+ public bool BoolValue
759
+ {
760
+ get => _boolValue ;
761
+ set
762
+ {
763
+ if ( _boolValue != value )
764
+ {
765
+ _boolValue = value ;
766
+ SetterInvokedCount ++ ;
767
+ PropertyChanged ? . Invoke ( this , new PropertyChangedEventArgs ( nameof ( BoolValue ) ) ) ;
768
+ }
769
+ } }
770
+
732
771
public double Value
733
772
{
734
773
get => _value ;
@@ -754,20 +793,38 @@ public double Value
754
793
}
755
794
}
756
795
}
796
+
797
+ public void ResetSetterInvokedCount ( ) => SetterInvokedCount = 0 ;
757
798
}
758
799
759
800
private class TwoWayBindingTest : Control
760
801
{
802
+ public static readonly StyledProperty < bool > AlwaysFalseProperty =
803
+ AvaloniaProperty . Register < StyledPropertyClass , bool > ( nameof ( AlwaysFalse ) ) ;
761
804
public static readonly StyledProperty < string > TwoWayProperty =
762
805
AvaloniaProperty . Register < TwoWayBindingTest , string > (
763
806
"TwoWay" ,
764
807
defaultBindingMode : BindingMode . TwoWay ) ;
765
808
809
+ public bool AlwaysFalse
810
+ {
811
+ get => GetValue ( AlwaysFalseProperty ) ;
812
+ set => SetValue ( AlwaysFalseProperty , value ) ;
813
+ }
814
+
766
815
public string TwoWay
767
816
{
768
817
get => GetValue ( TwoWayProperty ) ;
769
818
set => SetValue ( TwoWayProperty , value ) ;
770
819
}
820
+
821
+ protected override void OnPropertyChanged ( AvaloniaPropertyChangedEventArgs change )
822
+ {
823
+ base . OnPropertyChanged ( change ) ;
824
+
825
+ if ( change . Property == AlwaysFalseProperty )
826
+ SetCurrentValue ( AlwaysFalseProperty , false ) ;
827
+ }
771
828
}
772
829
773
830
public class Source : INotifyPropertyChanged
0 commit comments