-2

I am struggling to bind the value of a slider to a value in a model using MVVM. Returning an error "A TwoWay or OneWayToSource binding cannot work on the read-only property 'Gravity' of type 'SliderTest.ViewModel'.' Although the propertu in question is public all the way back to the model.

I have replicated the problem in a simple test environment

MainWindow.xaml

<Window x:Class="SliderTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:SliderTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Slider x:Name="GravitySlider" Height="25" Panel.ZIndex="-1" SmallChange="0" IsSnapToTickEnabled="True" Value="{Binding Gravity, Mode=TwoWay}" />
    </Grid>
</Window>

MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace SliderTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new ViewModel();
        }
    }
}

ViewModel.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SliderTest
{
    class ViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;

            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        private void GravityUpdated(object sender, EventArgs e)
        {
            OnPropertyChanged("Gravity");
        }
        private Model model;
        public ViewModel()
        {
            model = new Model();
            model.GravityUpdated += GravityUpdated;
        }
        public double Gravity => model.Gravity;
    }
}

Model.cs

using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SliderTest
{
    public class Model
    {

        public Model()
        {
            Item.Gravity = 1;
        }

        public double Gravity
        {
            set
            {
                Item.Gravity = value;
                GravityUpdated(this, new EventArgs());
            }
            get
            {
                return Item.Gravity;
            }
        }

        public event EventHandler GravityUpdated;
    }
}

Item.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SliderTest
{
    class Item
    {
        private static double gravity;

        public static double Gravity { get => gravity; set => gravity = value; }
    }
}
Stuart Maher
  • 23
  • 1
  • 4
  • 1
    Note that the error message does not complain about the property (in the view model) being non-public, but about it being read-only. – Klaus Gütter Apr 19 '20 at 19:19

1 Answers1

0

You're binding to the property Gravity in the ViewModel which is a read-only property.

You should change this code:

public double Gravity => model.Gravity;

to this one:

public double Gravity
{
    set
    {
        model.Gravity = value;
        GravityUpdated(this, new EventArgs());
    }
    get
    {
        return model.Gravity;
    }
}
kmatyaszek
  • 19,016
  • 9
  • 60
  • 65
  • Thank you for the answer. I had thought the "=>" operator effectively mapped the property in the View Model to the property in the model, which was 2 way. Am I missing an aspect of how the lambda operator works here? – Stuart Maher Apr 20 '20 at 08:22