1
votes

I've decided to experiment a little with bound dependency properties in view models. For that I've set up a simple test project, consisting of a MainWindow.xaml:

<Window x:Class="MVVM_Test.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MVVM_Test"
        xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <ListBox Margin="6" Grid.RowSpan="2" Name="PART_LISTBOX">
            <sys:String>String 1</sys:String>
            <sys:String>String 2</sys:String>
            <sys:String>String 3</sys:String>
        </ListBox>
        <StackPanel Name="PART_spViewModel2" Grid.Column="1" Grid.Row="1">
            <StackPanel.DataContext>
                <local:ViewModel2 BoundString="{Binding ElementName=PART_LISTBOX,
                                  Path=SelectedItem,
                                  diag:PresentationTraceSources.TraceLevel=High}" />
            </StackPanel.DataContext>
            <TextBox Margin="6" Text="{Binding BoundString}" />
        </StackPanel>
    </Grid>
</Window>

and backed up by a simple view model object:

public class ViewModel2 : DependencyObject
{
    public string BoundString
    {
        get { return (string)GetValue(BoundStringProperty); }
        set { SetValue(BoundStringProperty, value); }
    }

    // Using a DependencyProperty as the backing store for BoundString.
    public static readonly DependencyProperty BoundStringProperty =
        DependencyProperty.Register("BoundString", typeof(string), typeof(ViewModel2), 
            new FrameworkPropertyMetadata(default(string),
                FrameworkPropertyMetadataOptions.BindsTwoWayByDefault ));
}

Now I expected the Content of the Textbox to change once I select something within the ListBox. However, when I run the program, it seems the ListBox cannot be found by the DataBinding:

System.Windows.Data Warning: 56 : Created BindingExpression (hash=61931053) for Binding (hash=40205895)

System.Windows.Data Warning: 58 : Path: 'SelectedItem'

System.Windows.Data Warning: 60 : BindingExpression (hash=61931053): Default mode resolved to TwoWay

System.Windows.Data Warning: 61 : BindingExpression (hash=61931053): Default update trigger resolved to PropertyChanged

System.Windows.Data Warning: 62 : BindingExpression (hash=61931053): Attach to MVVM_Test.ViewModel2.BoundString (hash=64815892)

System.Windows.Data Warning: 64 : BindingExpression (hash=61931053): Use Framework mentor

System.Windows.Data Warning: 67 : BindingExpression (hash=61931053): Resolving source

System.Windows.Data Warning: 69 : BindingExpression (hash=61931053): Framework mentor not found

System.Windows.Data Warning: 65 : BindingExpression (hash=61931053): Resolve source deferred

The deferred lookup goes on a little until it hits this point:

System.Windows.Data Warning: 67 : BindingExpression (hash=61931053): Resolving source (last chance)

System.Windows.Data Warning: 69 : BindingExpression (hash=61931053): Framework mentor not found

System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=SelectedItem; DataItem=null; target element is 'ViewModel2' (HashCode=64815892); target property is 'BoundString' (type 'String')

If I do the binding the other way round, e.g. from the ListBox.SelectedItem property towards the ViewModel it works.

<ListBox Margin="6" Grid.RowSpan="2" Name="PART_LISTBOX" 
         SelectedItem="{Binding ElementName=PART_spViewModel2,
         Path=DataContext.BoundString}">

So my guess is that I've not set up the DependencyProperty correctly, but where exactly did I make a mistake?

1
You bind a target property in the view to a source property of a DataContext. The view model is the DataContext, i.e. it defines the source properties. It doesn't get added to the visual tree so you cannot use your BoundString dependency property as a target property for the SelectedItem property of the ListBox.mm8
So in order to bind to a property, an object has to be part of the visual tree, because the BindingExpression is actually looking for the reference's parents to correctly identify the actual FrameworkElement referenced, which it can't for the DataContext because the DataContext never gets its parent assigned? Which in turn means that I would never need a DependencyProperty on the view model in the first place and simply could do with NotifyPropertyChanged properties on the view model in the first place?Adwaenyth
Yes, the view model doesn't need any dependency properties. See my answer.mm8

1 Answers

1
votes

You bind a target property in the view to a source property of a DataContext. The view model is the DataContext, i.e. it defines the source properties. It doesn't get added to the visual tree so you cannot use your BoundString dependency property as a target property for the SelectedItem property of the ListBox.

In general source properties in view models are defined as CLR properties whereas target properties, i.e. properties that you bind something to, must be defined as dependency properties. You can refer to the following question for more information about this:

Can somone give example of Dependency Property in ViewModel

INotifyPropertyChanged vs. DependencyProperty in ViewModel