Search code examples
c#wpfdata-binding2-way-object-databinding

Simple DataBinding in wpf application


Hi I am trying to study simple data binding in wpf. I tried and am not succeeding.. please suggest ways..

<Window x:Class="WpfTestApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="250" Width="350">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="70"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="50"/>
            <RowDefinition Height="50"/>
            <RowDefinition Height="50"/>
            <RowDefinition Height="50"/>
        </Grid.RowDefinitions>

        <Label Content="First Name" Grid.Row="0" Grid.Column="0" Height="25" HorizontalAlignment="Stretch"/>
        <Label Content="Last Name" Grid.Row="1" Grid.Column="0" Height="25" HorizontalAlignment="Stretch"/>
        <Label Content="Full Name" Grid.Row="2" Grid.Column="0" Height="25" HorizontalAlignment="Stretch"/>

        <TextBox x:Name="fName" Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged}" Grid.Row="0" Grid.Column="1" Height="25" HorizontalAlignment="Stretch"/>
        <TextBox x:Name="lName" Text="{Binding LastName, UpdateSourceTrigger=PropertyChanged}" Grid.Row="1" Grid.Column="1"  Height="25" HorizontalAlignment="Stretch"/>
        <Label x:Name="lblFullName" Content="{Binding FirstName}" Grid.Row="2" Grid.Column="1" Height="25" HorizontalAlignment="Stretch"/>
        <Button x:Name="cmdCommand" Content="Command" Grid.Row="3" Grid.Column="1" Margin="2" VerticalAlignment="Center" HorizontalAlignment="Center" Click="cmdCommand_Click"/>
    </Grid>
</Window>

Now you see, I want that the label lblFullName to be automatically updated as soon as I type name in textboxes.

Now the my codebehind file looks like this:

  public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private string _firstName;
        public string FirstName
        {
            get { return _firstName; }
            set
            {
                if (value != _firstName)
                {
                    _firstName = value;
                    OnPropertyChanged("FirstName");
                }
            }
        }

        private string _lastName;
        public string LastName
        {
            get { return _lastName; }
            set
            {
                if (value != _lastName)
                {
                    _lastName = value;
                    OnPropertyChanged("LastName");
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Notifies objects registered to receive this event that a property value has changed.
        /// </summary>
        /// <param name="propertyName">The name of the property that was changed.</param>
        protected virtual void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public MainWindow()
        {
            InitializeComponent();
        }

        private void cmdCommand_Click(object sender, RoutedEventArgs e)
        {
            lblFullName.Content = FirstName + " " + LastName;
        }

    }

needless to say, even clicking the command button is not working...

any suggestions?


Solution

  • Setting the DataContext to point to the window itself will solve the immediate problem:

    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = this;
    }
    

    The paths in the Binding expressions in the Xaml are all relative to the DataContext of the controls so the DataContext has to be set.

    However, I strongly suggest that you put the first name and last name in a separate class (e.g. PersonViewModel) That will make the code easier to read and to maintain.

    public class PersonViewModel : INotifyPropertyChanged
    {
        private string _firstName;
        public string FirstName
        {
            get { return _firstName; }
            set
            {
                if (value != _firstName)
                {
                    _firstName = value;
                    OnPropertyChanged("FirstName");
                }
            }
        }
    
        private string _lastName;
        public string LastName
        {
            get { return _lastName; }
            set
            {
                if (value != _lastName)
                {
                    _lastName = value;
                    OnPropertyChanged("LastName");
                }
            }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected virtual void OnPropertyChanged(string propertyName)
        {
            var handler = this.PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
    
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = new PersonViewModel();
        }
    
        private void cmdCommand_Click(object sender, RoutedEventArgs e)
        {
            lblFullName.Content = FirstName + " " + LastName;
        }
    }
    

    Now the last part of this code does not work anymore. There are two solutions.

    The quick and dirty solution:

        private void cmdCommand_Click(object sender, RoutedEventArgs e)
        {
             var person = this.DataContext as PersonViewModel;
             if(person == null) return;
             lblFullName.Content = string.Format("{0} {1}", person.FirstName, person.LastName);
        }
    

    A better way, when trying to do proper MVVM is to put an extra property in the ViewModel (note the change in the First and Last name properties!):

    public class PersonViewModel : INotifyPropertyChanged
    {
        public string FullName
        {
            get 
            {
                return string.Format("{0} {1}", FirstName, LastName);
            }
        }
    
        private string _firstName;
        public string FirstName
        {
            get { return _firstName; }
            set
            {
                if (value != _firstName)
                {
                    _firstName = value;
                    OnPropertyChanged("FirstName");
                    OnPropertyChanged("FullName");
                }
            }
        }
    
        private string _lastName;
        public string LastName
        {
            get { return _lastName; }
            set
            {
                if (value != _lastName)
                {
                    _lastName = value;
                    OnPropertyChanged("LastName");
                    OnPropertyChanged("FullName");
                }
            }
        }
        // remainder of the class remains the same
    }
    

    Remove the button and its click handler. Bind the label to this new property:

    <Label Content="{Binding FullName}" 
           Grid.Row="2" Grid.Column="1" 
           Height="25" HorizontalAlignment="Stretch"/>