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>
Timothy
  • 135
  • 1
  • 9

2 Answers2

10

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.

toadflakz
  • 7,764
  • 1
  • 27
  • 40
5

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.

Kylo Ren
  • 8,551
  • 6
  • 41
  • 66