Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF MVVM textBox Text Binding

I am just getting started with MVVM so apologies if I've done something really stupid. I tried writing a very simple test to see if I could remember everything, and for the life of me I can't see why its not working.

In my view I have a textBox where its text property is bound to a value in the ViewModel. Then when pressing a button the value should be altered and the textBox update.

I can see the value does alter (I have added a MessageBox.Show() line in the buttom press command) however the textBox does not update.

I assume that this means I have not properly implemented the INotifyPropertyChanged event properly but am unable to see my mistake.

Could anyone point me in the right direction?

Here is the code:

View

<Window x:Class="Mvvm.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">

<StackPanel Orientation="Horizontal" VerticalAlignment="Top">
    <TextBox Height="40" Width="200" Text="{Binding helloWorld.Message, UpdateSourceTrigger=PropertyChanged}"/>
    <Button Command="{Binding UpdateTimeCommand}">Update</Button>
</StackPanel>
</Window>

Behind View

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new ViewModel.MainWindowViewModel();
    }
}

ViewModel

namespace Mvvm.ViewModel
{
internal class MainWindowViewModel
{
    private HelloWorld _helloWorld;

    /// <summary>
    /// Creates a new instance of the ViewModel Class
    /// </summary>
    public MainWindowViewModel()
    {
        _helloWorld = new HelloWorld("The time is " + DateTime.Now.ToString("HH:mm:ss"));
        UpdateTimeCommand = new Commands.UpdateTimeCommand(this);
    }

    /// <summary>
    /// Gets the HellowWorld instance
    /// </summary>
    public HelloWorld helloWorld
    {
        get
        {
            return _helloWorld;
        }
        set
        {
            _helloWorld = value;
        }
    }

    /// <summary>
    /// Updates the time shown in the helloWorld 
    /// </summary>
    public void UpdateTime()
    {
        helloWorld = new HelloWorld("The time is " + DateTime.Now.ToString("HH:mm:ss"));
    }

    public ICommand UpdateTimeCommand
    {
        get;
        private set;
    }
}

Model

namespace Mvvm.Model
{
    class HelloWorld : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public HelloWorld(string helloWorldMessage)
        {
            Message = "Hello World! " + helloWorldMessage;
        }

        private string _Message;
        public string Message
        {
            get
            {
                return _Message;
            }
            set
            {
                _Message = value;
                OnPropertyChanged("Message");
            }
        }

        private void OnPropertyChanged(string p)
        {
            PropertyChangedEventHandler handler = PropertyChanged;

            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(p));
            }
        }
    }
}

Commands

namespace Mvvm.Commands
{
    internal class UpdateTimeCommand : ICommand
    {
        private ViewModel.MainWindowViewModel _viewModel;
        public UpdateTimeCommand(ViewModel.MainWindowViewModel viewModel)
        {
            _viewModel = viewModel;
        }

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

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public void Execute(object parameter)
        {
            _viewModel.UpdateTime();
        }
    }
}

Sorry for such a long post and it being a spot my mistake post but I've looked at it for so long and I don't know what I'm doing wrong

Thanks!

like image 685
Nick Williams Avatar asked Dec 06 '12 14:12

Nick Williams


3 Answers

The Problem that you have is that you are changing the wrong Property. Instead of changing the HelloWorld.Message Property, you are changing MainWindowViewModel.HelloWorld property. Your code will work OK if you change this line:

public void UpdateTime()
{
    helloWorld = new HelloWorld("The time is " + DateTime.Now.ToString("HH:mm:ss"));
}

For this one

public void UpdateTime()
{
    helloWorld.Message = "The time is " + DateTime.Now.ToString("HH:mm:ss");
}

If you want to keep your original code, then you need to implement INotifyPropertyChanged for your ViewModel, and rise the event when you change helloWorld object.

Hope this helps

like image 192
Agustin Meriles Avatar answered Sep 23 '22 17:09

Agustin Meriles


I think you need to implement PropertyChanged notification on your ViewModel. You are creating a new HelloWorld in the UpdateTime method, but the UI doesn't know it.

Edit

I have a ViewModel base class which I derive all of my ViewModels from. It implements INotifyPropertyChanged, and has references to my relay command classes, and some other common stuff. I recommend always having INotifyPropertyChanged implemented on the ViewModel. The ViewModel is there to expose data to the UI, and it cant do that for data that changes without that interface.

like image 42
CodeWarrior Avatar answered Sep 21 '22 17:09

CodeWarrior


i think your ViewModel needs to implement INotifyPropertyChanged too, or you can set the DataContext before you call InitializeComponents(), if you do that you should change your code to NOT create a new instance every update like Agustin Meriles said.

like image 40
user1064519 Avatar answered Sep 23 '22 17:09

user1064519