Nick Williams
Nick Williams

Reputation: 1267

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!

Upvotes: 5

Views: 19610

Answers (4)

CodeWarrior
CodeWarrior

Reputation: 7470

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.

Upvotes: 2

zzfima
zzfima

Reputation: 1565

  1. i think you mistake Model and VM: Model is MainWindowViewModel and VM is HelloWorld

  2. In your VM (class HelloWorld ) you need use your model

    So, your classes will look like:

        using System.ComponentModel;
    
    namespace WpfApplication1
    {
        public sealed class TextVM : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
            private TextInfo _info;
    
            public TextVM()
            {
                _info = new TextInfo();
            }
    
            public string MyText 
            {
                get { return _info.MyText; }
                set
                {
                    _info.MyText = value;
                    OnPropertyChanged("MyText");
                }
            }
    
            private void OnPropertyChanged(string p)
            {
                PropertyChangedEventHandler handler = PropertyChanged;
    
                if (handler != null)
                {
                    handler(this, new PropertyChangedEventArgs(p));
                }
            }
        }
    }
    
    
    using System;
    
    namespace WpfApplication1
    {
        public sealed class TextInfo
        {
            public TextInfo()
            {
                MyText = String.Empty;
            }
    
            public string MyText { get; set; }
        }
    }
    

inset inside your ICommands

Upvotes: 1

user1064519
user1064519

Reputation: 2190

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.

Upvotes: 1

Agustin Meriles
Agustin Meriles

Reputation: 4854

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

Upvotes: 6

Related Questions