0

I have been trying to understand and get something working with dependency properties.

I have read loads of descriptions and explanations about DP's, and I also think I understand where they should be used.

I have read this and the links / references contain therein.

However, im still having problems. These maybe just mental blockages, maybe im clinging onto some idea or belief that i cant let go of.. anyway..

What im trying to do is create a control with a DP on it. Use that control in xaml and bind to the DP.

When i specify a value for the DP in the xaml the value is diaplayed on the user control as expected.

<DpTestProj:UserControl1 MyName="Steve"

However when i try to bind to it the value is not set, and i get

System.Windows.Data Error: 40 : BindingExpression path error: 'PersonName' property not found on 'object' ''UserControl1' (Name='')'. BindingExpression:Path=PersonName; DataItem='UserControl1' (Name=''); target element is 'UserControl1' (Name=''); target property is 'MyName' (type 'String')

Which suggests i have done something wrong with the binding or datacontext somewhere. But i cant see what.

My code is as follows.

UserControl1 has the following DP

public const string MyNamePropertyName = "MyName";
public string MyName
{
    get
    {
        return (string)GetValue(MyNameProperty);
    }
    set
    {
        SetValue(MyNameProperty, value);
    }
}

public static readonly DependencyProperty MyNameProperty = DependencyProperty.Register(
    MyNamePropertyName,
    typeof(string),
    typeof(UserControl1),
    new UIPropertyMetadata("No Name"));

Its used on the MainWindow like this

<StackPanel x:Name="LayoutRoot">
    <DpTestProj:UserControl1 MyName="{Binding PersonName}" MyList="{Binding SomeListItems}" />
</StackPanel>

I'm using MVVM Light si the data context of the MainWindow is

DataContext="{Binding Main, Source={StaticResource Locator}}">

PersonName is a normal CLR (string) property that lives on the MainViewModel

If its easier I have posted the entire solution here

Community
  • 1
  • 1
S Rosam
  • 375
  • 1
  • 3
  • 16

3 Answers3

0

Your DependencyProperty isn't the issue here; the issue is the data you're binding it to. The binding isn't finding a property called Main.PersonName from whatever the Locator resource is.

Dan Puzey
  • 33,626
  • 4
  • 73
  • 96
  • No and thats whats confusing. I tried – S Rosam Apr 01 '11 at 19:45
  • Well, if `{Binding PersonName}` works in a label, it'll work in the usercontrol too. If you put an `OnPropertyChanged` callback on your dependency property, is it being hit? And/or: are there any binding errors in the output window? – Dan Puzey Apr 02 '11 at 08:16
  • that was meant to be: Well yes. it should. doing this. .. gets me the binding expression error. System.Windows.Data Error: 40 : BindingExpression path error: 'PersonName' property not found on 'object' ''UserControl1' (Name='')'. BindingExpression:Path=PersonName; DataItem='UserControl1' (Name=''); target element is 'UserControl1' (Name=''); target property is 'MyName' (type 'String') – S Rosam Apr 04 '11 at 09:12
  • 1
    Are you overriding the `DataContext` of the `UserControl`? Say, in the constructor of the `UserControl`? – Dan Puzey Apr 04 '11 at 09:54
  • yes, i have it set to 'this' on the constructor on the usercontrol. without that it would be bound directly to the main window. and since the DP im using is defined on the usercontrol, i thought it appropriate to bind to the usercontrol its self. – S Rosam Apr 04 '11 at 10:21
  • Well, that's your problem: you're trying to bind to the property `PersonName` on your UserControl, not on your window, because you're overriding the data context. My personal suggestion is *never* to forecefully override the datacontext on a usercontrol for that exact reason - there are cleaner ways of doing this in markup that don't break the use of your control. – Dan Puzey Apr 04 '11 at 10:25
  • okay. i think i did it this way because i was looking for a reason to use a dependency property. if i bind directly, without going through the DP, then i wont need the DP. – S Rosam Apr 04 '11 at 10:52
0

And the answer is...

you cant bind to the clr wrapper of a dp directly with a vanilla binding (even if you have set the dataContext of the userControl as this (i.e. the code behind of the userControl))

You can do one of two things to get changes to the dependency property reflected in the UI elements.

1) Use a RelativeSource Binding in FindAncestor mode

{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=DpWrapperProp}"

2) Use the Dp's callback to update your UI element. (If youre using the callback to do some validation anyway, then this might be the route for you). Because the Callback is Static you need to create a non static method to do the update, and call that from the static method. The ui element (label textBlock whatever) also need to be named.

    private static void ChangeText(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        (d as MyUserControl).UpdateText(e.NewValue.ToString());
    }

    private void UpdateText(string newText)
    {
        MyLabel.Content = newText; //set the content of the named label 
                                   //defined in the userControl XAML
    }

*the above examples are generic and not nesecarily related to my original code.

fyi i arrived at these solutons with the help of this and this

S Rosam
  • 375
  • 1
  • 3
  • 16
-1
<DpTestProj:UserControl1 MyName="{Binding MyName}" ... />

?

Update:

Ok I took a closer look at your solution. In your UserControl1 you're basically overwriting your original DataContext.

To make things work, just remove that line:

    public UserControl1()
    {
        InitializeComponent();
        DataContext = this; // this makes you lose your MainViewModel
    }

And edit your UserControl1.xaml accordingly to bind to your original MainViewModel:

<Label Content="{Binding PersonName}"/>
<ListBox ItemsSource="{Binding SomeListItems}" MinHeight="100" MinWidth="100"/>

Now I don't know your original scenario of why you wanted to set your UserControl1's view model to itself. That's a very unusual way of doing things that is not very MVVM-like. If you really want to go that route, you'd have to create a new ViewModel that stores the original MainViewModel in one property and the UserControl1 in another.

herzmeister
  • 11,101
  • 2
  • 41
  • 51
  • Well, that removes the binding error but what your'e doing is binding the DP to its self. So the default value is displayed in the usercontrol – S Rosam Apr 01 '11 at 19:44
  • okay, yes that gets the value from the mainViewModel into the control on UserControl1. But it negates the need for the Dependency Property. Basically i was trying to do something with a DP thats all. – S Rosam Apr 04 '11 at 15:45
  • @Cha0sEngine I'd say in use cases like this (binding to domain model data, auto-passing the datacontext through the control hierarchy) you don't need to deal with Dependency Properties. They are part of the WPF/Silverlight infrastructure to make certain UI concepts work. Dependency Properties become interesting and necessary when implementing your own general controls or framework elements. In such cases you won't work with view models in contrast then. – herzmeister Apr 04 '11 at 16:05
  • @herzmeister der welten as i said, i was experimenting with dependency properties. this was meant to be an extremely simple user control. If we forget about my MainWindow (test harness) for now, how would you implement my "control"? would you use DP's to enable binding? would you use MVVM on the user control? or is it not possible to do so? thanks. – S Rosam Apr 04 '11 at 16:30
  • Maybe we'd need a more meaningful example, otherwise it's a bit difficult to clarify things in a better way. Yes, the guideline is to use DPs on your own controls and user controls to make them bindable, whereas you'd use MVVM (INotifyPropertyChanged) on your view models. So are you trying to implement a `PersonInfoControl` on which you can set the name and some list from outside declaratively? I now *believe* what you are *really* looking for here is TemplateBinding (or Relative Source TemplatedParent binding): http://www.google.com/search?q=templatebinding+relativesource – herzmeister Apr 04 '11 at 17:02
  • I will investigate using a lookless control and have a template. However I did some more research and found that this i.e. using ElementName binding works. Normal binding still dosent work though. – S Rosam Apr 05 '11 at 11:02
  • Sorry.. The above code is from a different solution, but the idea is the same. – S Rosam Apr 05 '11 at 11:09
  • Yes, using WPF's binding engine against other framework elements, be it by `ElementName`, `TemplateBinding`s or whatever, is the correct approach for your scenario. "Normal" binding with the `DataContext` would of course work too if you create appropriate view models that store both data and controls, but that would make things much more complicated and, as said, is unusual and not what MVVM is intended for. – herzmeister Apr 05 '11 at 12:12
  • I think I have the answer, but before i post it as the answer I thought id see what you think... It seems that the problem is when i try to use binding on the userControl, so from my original example I was binding to MyName in the usercontrol, my name is defined on the usercontrols code behind and is the wrapper for the DP. When I use the DP callback to update the UI element, it works as expected. – S Rosam Apr 07 '11 at 10:57