Search code examples
c#wpfxamldata-binding

Property bound to WPF property gets updated, but WPF property does not change


I need to make a window change position each time the cursor gets over it. I had no problem creating an event trigger for "MouseEnter" which calls a method in the ViewModel. That method changes a double called "RecordingViewTop", and that double is bound to the "Window.Top" property of the xaml file. So it should change window position each time the event is fired, but that does not happen automatically and I don't know why.

Here's how the Window.Top property currently is in the .XAML file:

Top="{Binding RecordingViewTop, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"

And the double RecordingViewTop:

        private double _recordingViewTop;

        public double RecordingViewTop
        {
            get { return _recordingViewTop; }
            set
            {
                if (_recordingViewTop != value)
                {
                    _recordingViewTop = value;
                    OnPropertyChanged();
                }
            }
        }

And here's my implementation of OnPropertyChanged():

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

I can't figure it out. When I debug my code, I can see that "RecordingViewTop" does get updated correctly, but somehow that doesn't transfer to the binding I made on Window.Top. How do I fix that?

UPDATE: Here's the RecordingViewModel class:

public class RecordingViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private ICommand _moveCommand;
    public ICommand MoveCommand
    {
        get { return _moveCommand ??= new CommandHandler(() => Move(), () => true); }
    }

    private double _recordingViewTop;

    public double RecordingViewTop
    {
        get { return _recordingViewTop; }
        set
        {
            if (_recordingViewTop != value)
            {
                _recordingViewTop = value;
                OnPropertyChanged();
            }
        }
    }
    public RecordingViewModel() { }

    private void Move()
    {
        if (RecordingViewTop == 0)
        {
            _recordingViewTop = SystemParameters.PrimaryScreenHeight - 150;
            MessageBox.Show(RecordingViewTop.ToString());
        }
        else
        {
            _recordingViewTop = 0; 
            MessageBox.Show(RecordingViewTop.ToString());
        }
    }

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

And here's how DataContext is currently set:

    public partial class RecordingView : Window
{
    public RecordingView()
    {
        InitializeComponent();
        RecordingViewModel recordingViewModel = new RecordingViewModel();
        SetViewModel(recordingViewModel);
    }
    public RecordingView SetViewModel(RecordingViewModel recordingViewModel)
    {
        DataContext = recordingViewModel;
        return this;
    }
}

And this is my latest try for the XAML file:

    <i:Interaction.Triggers>
    <i:EventTrigger EventName="MouseEnter">
        <i:InvokeCommandAction Command="{Binding MoveCommand}"/>
        <i:ChangePropertyAction PropertyName="Top" Value="{Binding RecordingViewTop, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    </i:EventTrigger>
</i:Interaction.Triggers>

As you can see, there's a MessageBox just to see if Move() is being called and if RecordingViewTop is getting updated accordingly, and that works fine if I move the mouse over the window, but the window doesn't move at all. Curiously, if I set "ChangePropertyAction" Value directly to a specific value, like "500", the window does move when the event is fired.


Solution

  • That's because in the XAML file you are binding the value to the public property "RecordingViewTop", while in the "Move" method you are updating its private var "_recordingViewTop".

    Just change your "Move" method to:

    private void Move()
    {
        if (RecordingViewTop == 0)
        {
            RecordingViewTop = SystemParameters.PrimaryScreenHeight - 150;
            MessageBox.Show(RecordingViewTop.ToString());
        }
        else
        {
            RecordingViewTop = 0; 
            MessageBox.Show(RecordingViewTop.ToString());
        }
    }