Home > Enterprise >  Click on CheckBox Binding
Click on CheckBox Binding

Time:10-21

I have Car.xaml:

<CheckBox Name="carA" IsChecked="{Binding CarACheck, Mode=TwoWay, FallbackValue=true}" />

Then in Car.xaml.cs:

public Car()
{
 //...
 DataContext = this;
}

public bool CarACheck
{
 get => carA.IsChecked;
 set => carA.IsChecked = value;
}

When I run it and click on CheckBox, the app crashes with the error below:

set => carA.IsChecked = value;

System.StackOverflowException

An unhandled exception of type 'System.StackOverflowException' occurred in mscorlib.dll

CodePudding user response:

Cause of Error

The reason is that your setter is called over and over.

  1. The CheckBox is clicked.
  2. The IsChecked property of the CheckBox is set.
  3. The binding sets value on the bound property CarACheck.
  4. The setter of CarACheck sets the IsChecked property of the CheckBox.
  5. Go to 3.

In the long run this causes the stack to overflow and the application crashes.

An MVVM Solution

It seems that you are trying to build a view that displays data about cars. What I see from your sample is that you mix your data and most likely business logic with the user interface code. You should not do that, because it harms code quality and maintainability in the long run. A better and sustainable approach is to separate your view from data and business logic. This is what the MVVM pattern is about.

In your sample code, we have a view, which we would call CarView. This is where the CheckBox is defined along with the rest of the user interface to represent a car. The data is exposed to the view through a separate view model type called CarViewModel. This view model contains properties that are bound from the view.

When using simple properties, regardless of having a backing field or not, the bindings are not able to determine changes to properties from the view model side. This is why you have to implement the INotifyPropertyChanged interface, which provides an event for this purpose. The event is raised, whenever a property is changed. Usually this is in done in the setter.

public class CarViewModel : INotifyPropertyChanged
{
   private bool _carACheck;

   public bool CarACheck
   {
      get => _carACheck;
      set
      {
         if (_carACheck == value)
            return;

         _carACheck = value;
         OnPropertyChanged(nameof(_carACheck));
      }
   }

   // ...other code, maybe even setting CarACheck.

   public event PropertyChangedEventHandler PropertyChanged;

   protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
   {
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
   }
}

In the view, simply assign an instance of this view model to the DataContext in XAML or code-behind, e.g.:

public Car()
{
   // ...other code.
   DataContext = new CarViewModel();
}

In your view, you do not need a name for the CheckBox, as there is no explicit reference to it. The Mode=TwoWay declaration can be removed, too, because the IsChecked property binds two-way by default.

<CheckBox IsChecked="{Binding CarACheck, FallbackValue=true}"/>

Remarks about your original code: I hope that you can see the benefits of the MVVM pattern. Of course, after reviewing this solution, you could simply add a backing field and INotifyPropertyChanged to your current code to make it work, too, but the lesson to learn here is that this separation is valuable and worth investing although it might seem more verbose. See also:

  • Related