XAML Data Binding not updating UI when property changes
Asked Answered
P

2

6

I'm having trouble getting a simple data bound label to update when an integer field is changed. I've implemented INotifyPropertChanged and this event gets fired when I chang my variables value. The UI does not update and the label does not change. I haven't done much data binding in the past so I'm probably missing something but I haven't been able to find it yet.

Here is what I have for my XAML:

         <Window x:Class="TestBinding.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:TestBinding"
                mc:Ignorable="d"
                Title="MainWindow" Height="350" Width="525">
            <StackPanel>
                <Button Command="{Binding TestCommand, Mode=OneWay}" >
                    <Button.DataContext>
                        <local:TestVM/>
                    </Button.DataContext> Add 1</Button>
                <Label Content="{Binding Count,
                 Mode=OneWay,UpdateSourceTrigger=PropertyChanged}">
                    <Label.DataContext>
                        <local:TestVM/>
                    </Label.DataContext>
                </Label>
            </StackPanel>
        </Window>

Here is my View Model C#:

class TestVM : INotifyPropertyChanged
    {
        private int _count;
        private RelayCommand _testCommand;

        public TestVM ()
        {
            Count=0;

        }

        public int Count
        {
            get { return _count; }
            set { _count = value; OnPropertyChanged(); }
        }

        public void Test()
        {
            Count++;
        }

        public ICommand TestCommand
        {
            get
            {
                if (_testCommand == null)
                {
                    _testCommand = new RelayCommand(param => this.Test(), param => true);
                }

                return _testCommand;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
        {
            if (this.PropertyChanged != null)
            {
                Console.WriteLine(propertyName);
                var e = new PropertyChangedEventArgs(propertyName);
                this.PropertyChanged(this, e);
            }
        }
    }

Here is my ICommand C# (In case you need it to replicate what I have):

public class RelayCommand : ICommand
    {
        #region Fields 
        readonly Action<object> _execute;
        readonly Predicate<object> _canExecute;
        #endregion // Fields 

        #region Constructors 
        public RelayCommand(Action<object> execute) : this(execute, null) { }
        public RelayCommand(Action<object> execute, Predicate<object> canExecute)
        {
            if (execute == null) throw new ArgumentNullException("execute");
            _execute = execute;
            _canExecute = canExecute;
        }
        #endregion // Constructors 

        #region ICommand Members [DebuggerStepThrough] 
        public bool CanExecute(object parameter)
        {
            return _canExecute == null ? true : _canExecute(parameter);
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public void Execute(object parameter)
        {
            _execute(parameter);
        }
        #endregion
        // ICommand Members 
    }

Any and all help is greatly appreciated.

EDIT: I updated my xaml to what toadflakz suggested and it worked.

<Window x:Class="TestBinding.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:TestBinding"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:TestVM/>
    </Window.DataContext>
    <StackPanel>
    <Button Command="{Binding TestCommand, Mode=OneWay}" >Add 1</Button>
    <Label Content="{Binding Count, Mode=OneWay,UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Window>
Putto answered 15/4, 2016 at 12:58 Comment(0)
E
11

Your issue is with the way you are binding the DataContext to the individual controls. Your ViewModel is not automatically a Singleton (single instance only object) so each time you specify it, you're actually creating a separate instance.

If you set the DataContext at the Window level, your code should work as expected.

Erenow answered 15/4, 2016 at 13:4 Comment(0)
S
6

Set DataContext for Window as:

<Window......
    <Window.DataContext>
         <local:TestVM/>
    </Window.DataContext>
</Window>

You have set the DataContext for Button and Label separately, so there will be two different objects of TestVM class. Command will be executed in first and change values in it, while your Label display the values from another object.

Sociality answered 15/4, 2016 at 13:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.