Click here to Skip to main content
15,897,704 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi everyone,

I've been looking up tutorials for the past few weeks (and even more intensely during the past 3 days) about WPF with mvvm approach and have been missing something which I am not sure of. Cutting to the chase, I have created a test application with 4 classes including the MainWindow (during this tutorial I have purpossedly violated the MVVM principle by merging the model and view model classes just so I can browse my code quicker without having too many tabs in visual studio)

so here's my model and view model class which inherits from ObservableObject class
string _partnumber
int _price
Part _currpart

public string PartNumber
{
     get {return _partnumber;}
     set
      {
          if (value != _partnumber)
          {
              _partnumber = value;
              OnPropertyChanged("PartNumber");
          }
      }
}

//I also have 2 other properties which is written the same way so I'll just keep it short

public int Price
{
   //the usual get, written the same way as PartNumber property
     set
       {
          //just like PartNmber, checks for value and then set it
          OnPropertyChanged("Price");
       }
}

public Part CurrentPart
{
     //the usual get, written the same way as PartNumber property
     set
       {
          //just like PartNmber, checks for value and then set it
          OnPropertyChanged("Part");
       }
}

private ICommand _getpricecommand;

public ICommand GetPriceCommand
 {
            get
            {
                if (_getpricecommand == null)
                {
                    _getpricecommand = new RelayCommand(param => GetPrice(),param => true);
                }
                return _getpricecommand;
            }
}

private void GetPrice()
{
            Part p = new Part();
            p.PartNumber = "6H-4130-01";
            p.price = 7;
            CurrentPart = p;
}


ObservableObject class
public event PropertyChangedEventHandler PropertyChanged;
        
        protected virtual void OnPropertyChanged(string PropertyName)
        {
            PropertyChangedEventHandler hndl = this.PropertyChanged;
            if (hndl != null)
            {
                var e = new PropertyChangedEventArgs(PropertyName);
                hndl(this, e);
            }
        }

        public virtual void RaisePropertyChanged(string PropertyName)
        {
            OnPropertyChanged(PropertyName);
        }


and finally, RelayCommand class
readonly Action<object> _execute;
readonly Predicate<object> _canexecute;

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;

}

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

[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
    return _canexecute == null ? true : _canexecute(parameter);
}

public void Execute(object parameter)
{
    _execute(parameter);
}


for the view, which is the MainWindow, I override the OnStartUp with:
MainWindow app = new MainWindow();
Part p = new Part();
app.DataContext = p;
app.Show();


and finally
XML
<Window.Resources>
    <DataTemplate DataType="local:Part" />
</Window.Resources>

<StackPanel DataContext="{Binding CurrentPart}">
            <TextBlock Text="{Binding PartNumber}" Height="50" Width="150" />
            <TextBlock Text="{Binding Price}" Height="50" Width="150" />
            <Button Command="{Binding GetPriceCommand}" Height="50" Width="150" Content="Get Part" />
        </StackPanel>


So at the press of Get Part button, it should display the partnumber in the top textbox and price in bottom textbox, but nothing showed up. I placed breakpoints in GetPrice() and GetPriceCommand but they seem to pass on the properties as they should. What did I miss?
Posted
Comments
George Swan 18-Jul-15 11:38am    
It seems to me that your TextBlock bindings are wrong. You are binding to a public field CurrentPart.PartNumber. You should be binding to the property PartNumber in your view model. If you wish to use Current Part as your model, you need to set it and get it in your PartNumber property e.g. get{return.CurrentPart.PartNumber} set{ CurrentPart.PartNumber=value etc}.
Lyandor 18-Jul-15 23:29pm    
Hi George, thank you very much for the pointer. I'm afraid I am still confused at this point. I still haven't completely grasped the idea behind binding. As far as I know, what is bound is the PropertyName in OnPropertyChanged() which was why I wrote {Binding PartNumber}. Can you give me an example for the TextBlock binding?
Mahindra Shambharkar 19-Jul-15 8:11am    
First thing no getter for Binding properties?? how you didn't get any run time error 'XAML Parse exception'
Binding seems correct with ViewModel.
You can verify your binding error's in output window as well.
Lyandor 19-Jul-15 11:33am    
Hi Mahindra, I'm afraid I don't understand you. Which getter for which property have I missed?

In the xaml do something like this


C#
<window x:class="TestBindings.MainWindow" xmlns:x="#unknown">
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:TestBindings"
        Title="MainWindow" >
    <window.datacontext>
        <local:testbindingsvm xmlns:local="#unknown" />
    </window.datacontext>

    <grid>
    .......
      <textbox text="{Binding PartNumber}" .....="" />
         ..............
  
      <textbox text="{Binding Price}" ......="" />
        ..............................
      <button command="{Binding GetPriceCommand}" content="Get Part" />
       .................
    </grid>
</window>


In the view model's constructor, instantiate the Part class.


C#
namespace TestBindings
{
    using System.Windows.Input;

 public class TestBindingsVM : ViewModelBase
    {
        #region Constants and Fields

        private readonly Part model;

        #endregion

        #region Constructors and Destructors

        public TestBindingsVM()
        {
            this.model = new Part();
           .....
        }
//declare your properties like this

  public int Price
        {
            get
            {
                return this.model.Price;
            }
            set
            {
                if (value != this.model.Price)
                {
                    this.model.Price = value;
                    this.RaisePropertyChanged();
                }
            }
        }
 // This is the ViewModelBase class

    using System.ComponentModel;
    using System.Runtime.CompilerServices;

    public abstract class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void RaisePropertyChanged([CallerMemberName] string caller = "")
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(caller));
            }
        }

    }
 
Share this answer
 
The problem lies in the Set PartNumber property. Set PartNumber should also call for OnPropertyChanged("Price"). This way, when PartNumber is updated in the text box, the Price is also updated at the same time.

CurrPart is also not required in this case.

PartNumber should look like this

C#
public string PartNumber
{
     get {return _partnumber;}
     set
       {
           if (_partnumber != value)
             {
                  _partnumber = value;
                  OnPropertyChanged("PartNumber");
                  OnPropertyChanged("Price");
             }
}


Next to do is to change the GetPrice(). As said before, CurrPart is no longer necessary, so updating the _partnumber and _price should do the trick.

C#
private void GetPrice()
{
     _partnumber = /*part number value*/;
     _price = /*set price value*/;
}
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900