3

I started learning WPF few days ago and have been creating some test projects to evaluate my understanding of the materials as I learn them. According to this article, "a property can only be bound if it is a dependency property." To test this, I used two different patterns:

1. Using MVVM pattern (sort of)

I created the Movie model:

public class MovieModel
{
    private string _movieTitle;
    private string _rating;

    public string MovieTitle
    {
        get { return _movieTitle; }
        set { _movieTitle = value; }
    }

    public string Rating
    {
        get { return _rating; }
        set { _rating = value; }
    }
}

and the MovieViewModel view model:

public class MovieViewModel
{
    MovieModel _movie;

    public MovieViewModel()
    {
        _movie = new MovieModel { MovieTitle = "Inception (Default)" };
    }

    public MovieModel Movie
    {
        get { return _movie; }
        set { _movie = value; }
    }

    public string MovieTitle
    {
        get { return Movie.MovieTitle; }
        set { Movie.MovieTitle = value; }
    }
}

And finally in the my main View, I set the DataContext to an instance of the MovieViewModel class:

<Window x:Class="MVVMTestProject.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:MVVMTestProject.ViewModels"
    Name="MainWindowElement"
    Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
    <local:MovieViewModel/>
</Window.DataContext>
<Grid>
    <StackPanel Orientation="Vertical">
        <TextBox Width="150" Text="{Binding Path=MovieTitle}"></TextBox>
    </StackPanel>
</Grid>

This works fine, when I run the application I see Inception (Default) in the textbox, even though none of the properties in the Model or ViewModel are DependencyProperties. Now the second try was by:

2. Using a property in the code behind of MainView

In the code behind of my main view, I put:

private string _myText;

public MainWindow()
{
    InitializeComponent();
    MyText = "Some Text";
}

public string MyText
{
    get { return _myText; }
    set { set _myText = value; }
}

And then I changed my view to:

<Window x:Class="MVVMTestProject.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:MVVMTestProject.ViewModels"
    Name="MainWindowElement"
    Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
    <local:MovieViewModel/>
</Window.DataContext>
<Grid>
    <StackPanel Orientation="Vertical">
        <TextBox Width="150" Text="{Binding ElementName=MainWindowElement, Path=MyText}"></TextBox>
    </StackPanel>
</Grid>

But that didn't work. When I run the application, the textbox is blank. So I changed the property to a dependency property, and oddly enough, it worked. So I don't understand why the properties on the Model and ViewModel can be regular properties, but the code-behind properties have to be dependency properties in order to bind to? Is it because the ViewModel is set to the DataContext of the view and the parser is smart enough to realize this or is there another reason?

Also a not-very-relevant question: I noticed in almost every singe article on WPF people use a local variable and then define a getter and setter for it, even though we all know in .NET 2.5 (I believe?) and above, we can just use the get; set; syntax without having to define a local member variable. So is there a specific reason for this?

Thanks in advance.

Arian Motamedi
  • 7,123
  • 10
  • 42
  • 82
  • Do you see any Binding errors in Output window when you debug second example (one that doesent work, when binding to Code behind property)? – Jurica Smircic Apr 29 '13 at 21:04
  • No I don't see any error in the output window. – Arian Motamedi Apr 29 '13 at 21:06
  • I'm not sure, never did have need for binding to regular code behind properties. It's true that usually new properties on UIElement objects are implemented as DependecyProperty. But it should work as regular property, it's a object as is every other. Im currious now, maybe for experiment you colud try to add INotifyPropertyChanged to your code behind class and raise PropertyChanged after value is changed in the setter. – Jurica Smircic Apr 29 '13 at 21:14
  • I did this few minutes ago, and it worked. – Arian Motamedi Apr 29 '13 at 21:17
  • Interesting find...I can't say to know why the binding didn't evaluate in the first place, if you set the value before InitializeComponent(). What's even more of a mistery, you didn't get any binding errors what implies that binding evaluated and returned null...But if you care, there are methods to debug bindings: http://blogs.msdn.com/b/wpfsldesigner/archive/2010/06/30/debugging-data-bindings-in-a-wpf-or-silverlight-application.aspx – Jurica Smircic Apr 29 '13 at 21:30
  • Yes it was strange, I was almost positive that placing it before InitializeComponent() would work. It makes me think that maybe the XAML code is executed even before the InitializeComponent() call? – Arian Motamedi Apr 29 '13 at 21:33
  • I dont think thats the case. InitializeComponent does nothing more than a call to Application.LoadComponent with URI of a XAML that is compiled as assembly resource. (You can see that in .cs files generated inside your \obj folder). So if there is no LoadComponent, there is also no object initialization from XAML. I suspect that ElementName as a source of binding does something differently than a binding to DataContext object. – Jurica Smircic Apr 29 '13 at 21:41

3 Answers3

3
  1. DependencyProperty is only needed on the FrameworkElement (DependencyObject) that will be used as the target of binding, meaning the property on which you will apply {Binding} extension through XAML or set the Binding through code. In your example only TextBox.Text is required to be DependencyProperty. In your second example Binding didn't work because you don't have mechanism in place that will send notification from binding source that the property value has changed. Usually that is done by implementing INotifyPropertyChanged interface and raising PropertyChanged event inside property setter. When you changed the property to a DependencyProperty you also got the notification mechanism so the property change is propagated to the target (TextBox). So you could make it work by raising PropertyChanged event without the need for DependencyProperty. Note that there are other uses that require DependencyProperty other than bindings (Styles, animations...)

  2. PropertyChanged event is probably the main reason for implementing properties with backing field (and not as Auto properties). As you need PropertyChanged event in the property setter.

Muzib
  • 2,412
  • 3
  • 21
  • 32
Jurica Smircic
  • 6,117
  • 2
  • 22
  • 27
  • You said "In your second example Binding didn't work because you don't have mechanism in place that will send notification from binding source that the property value has changed.", but I don't have that mechanism in my first example (MVVM-like pattern) either. – Arian Motamedi Apr 29 '13 at 21:05
  • 3
    Valid point, but with your MVVM like pattern you are setting the viewmodel object inside xaml as DataContext. And that is happening before the bindings will be evaluated so you have correct values. If you change the value of the property in ViewModel after the window is shown, you would not see the new value in textbox without PropertyChanged event. – Jurica Smircic Apr 29 '13 at 21:08
  • This makes perfect sense. Thanks you sir! – Arian Motamedi Apr 29 '13 at 21:16
1

Picking up a second question tucked in at the end of your post:

Also a not-very-relevant question: I noticed in almost every singe article on WPF people use a local variable and then define a getter and setter for it, even though we all know in .NET 2.5 (I believe?) and above, we can just use the get; set; syntax without having to define a local member variable. So is there a specific reason for this?

In your code you gave an example like this:

private string _movieTitle;

public string MovieTitle
{
    get { return _movieTitle; }
    set { _movieTitle = value; }
}

That code is exactly what the compiler generates when you write this instead:

public string MovieTitle { get; set; }

The reason you see something like the first form a lot in WPF, is because we want WPF bindings to react when property values change, and in order to achieve this we need to add change notification via INotifyPropertyChanged.

There are many ways of doing this, but in modern C# you can write something like this:

public class MovieViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private string _movieTitle;

    public string MovieTitle
    {
        get { return _movieTitle; }
        set
        {
            if (_movieTitle == value)
                return;
            _movieTitle = value;
            OnPropertyChanged();
        }
    }

    private void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

In the setter, we raise the PropertyChanged event with the string name of the property, MovieTitle, which the compiler passes in for us because of the [CallerMemberName] attribute, and default value of null so we don't have to pass anything when we call it.

Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
0

Using mvvm light you can bind properties and raise event.

    /// <summary>
    /// The <see cref="SelectedPriority" /> property's name.
    /// </summary>
    public const string SelectedPriorityPropertyName = "SelectedPriority";

    private string _selectedPriority = "normalny";

    /// <summary>
    /// Sets and gets the SelectedPriority property.
    /// Changes to that property's value raise the PropertyChanged event. 
    /// </summary>
    public string SelectedPriority
    {
        get
        {
            return _selectedPriority;
        }

        set
        {
            if (_selectedPriority == value)
            {
                return;
            }

            RaisePropertyChanging(SelectedPriorityPropertyName);
            _selectedPriority = value;
            RaisePropertyChanged(SelectedPriorityPropertyName);
        }
    }

this class extends ViewModelBase which has methods RaisePropertyChanged, RaisePropertyChanging. You can download framework from http://www.galasoft.ch/mvvm/ this site. After you install it you can use snippet to mvvminpc to create property that can be binded.

Your other question is answered Why use INotifyPropertyChanged with bindings in WPF?

To conclude, you need to implement RaisePropertyChanged() method and use this method in set;get to notify view about changing the property.

Implementing INotifyPropertyChanged - does a better way exist?

Community
  • 1
  • 1
Robert
  • 19,800
  • 5
  • 55
  • 85
  • That wasn't really my question though. MVVM is a pattern, so I don't understand why I can use a regular property in my ModelView but I HAVE to declare a dependency property in the code behind in order to bind to it. – Arian Motamedi Apr 29 '13 at 20:47
  • It is not dependency property, you need to use get; set; extended to raise an event that property has been changed. This event notifies view that property has been changed. Your question is answered here: http://stackoverflow.com/questions/10475130/why-use-inotifypropertychanged-with-bindings-in-wpf – Robert Apr 29 '13 at 20:49