0

The thing is I always want to bind controls against DataContext property. Because I don't like specifying Source in Binding expression. However, I do not want to bind DataContext property of my controls explicitly, but use another designated and self-descriptive properties, like Items or Plugins or whatever the purpose of control is. Now, I need a way to set DataContext property whenever that self-descriptive property truly determining the context of a control is set. Moreover, I want DataContext to always same as this property. How this can be done?

I tried specifying PropertyMetadata with callback that would set DataContext on newly received value. It doesn't work. Firstly, because this callback isn't being called for default value of a property. And secondly, and that is most interesting, I observed very strange behavior. Let me explain this using a callstack:

This is my PropertyMetadata with breakpoint in the callback:

new PropertyMetadata((d, e) => ((SearchSettings)d).DataContext = e.NewValue)

And this is the callstack (there are comments at the ends of the lines):

3. Bla-bla-bla.SearchSettings..cctor.AnonymousMethod__7(DependencyObject d, DependencyPropertyChangedEventArgs e) /* Setting property back to null. Why??? */
2. [External Code]  
1. Bla-bla-bla.SearchSettings..cctor.AnonymousMethod__7(DependencyObject d, DependencyPropertyChangedEventArgs e) /* This is where the breakpoint hit first. Settings a new value to a property as a result of databinding. */

So, how do I do it? Or should I want to do it at all? BTW, I am new to WPF, so I could get it all wrong.

Eugene Strizhok
  • 1,145
  • 2
  • 12
  • 19
  • What are you trying to achieve with your dependancy property? Why set the page DataContext to the new value of some property vs setting some property on the page with that same value and binding to that? – fatty Aug 29 '11 at 21:00
  • I do like `Name={Binding Path=Name}` much more than `Items={Binding ElementName=me, Path=User.Name}`. As far as I know, this is only possible to if `DataContext` property is equal to `User` property. Thus, I want the `DataContext` property to be equal `User`. – Eugene Strizhok Aug 30 '11 at 07:11
  • The reason this may be getting reset back to null is probably because the `DataContext` property is changing. This will flow down to the control that the Binding is set on. then assuming that the Binding is specified as `TwoWay`, will then tell the control to update it's `Binding source`, which will then flow back up to the `PropertyChangedCallback` specified in the `PropertyMetadata` object. The reason this doesn't throw you in to an infinite loop is because of the value checking that goes on behind the scenes. – fatty Aug 30 '11 at 07:16
  • You would only use ElementName to bind to the property of a control in your XAML. Is this what you're trying to do? Example, one control is trying to set one of it's properties to the value of another control? – fatty Aug 30 '11 at 07:22
  • I just don't get it. What are you trying to do? Can you show some more code/XAML to clarify? – Daniel Aug 30 '11 at 07:27
  • In the some window: `` Here, I am setting `Settings` property to a value of interface `ISearchSettings` that has property `WithSubdirs`. In the control I want to write: `` – Eugene Strizhok Aug 30 '11 at 17:33

2 Answers2

1

As per the answers in your latest question, the DataContext is the object that your Bindings will use as their data source.

When I suggested setting the DataContext to the page or control (below, in original answer), this was so you didn't have to specify the Source property in your Binding as stated in your original question.

Example - Rather than specifying your Binding like:

{Binding Path=PropertyName, RelativeSource={RelativeSource AncestorType={x:Type ns:ControlName}}}

you could have just used

{Binding Path=PropertyName}

as the containers DataContext property was set to the instance of ControlName (this is only the case if you specify DataContext = this, not DataContext = this.DataContext as that will just set it to itself and be pointless).

Now for Page or Window objects, PropertyName can be either be a register DependencyProperty instance, or it can be a stock standard Property that raised the INotifyPropertyChanged.PropertyChanged event to notify the children in the XAML that the property has changed and they update themselves as required.

For Control objects, you can still use either method - however, if you want to be able to bind to these properties, they will need to be defined as DependencyPropertys.

Are you able to update your post with any source for us to look at for you?


Original Answer:

I'm not entirely sure I understand your final goal, but the laziest way to specify a DataContext for your controls would be to set the page's DataContext property in the constructor to itself.

this.DataContext = this;

This eliminates the need to set the Source and RelativeSource properties in your Binding, while still allowing you to bind directly to the page properties.

Example:

public class MyControl : UserControl
{
    public static readonly DependencyProperty SomeStringProperty = DependencyProperty.Register("SomeString", typeof(string), typeof(ownerclass), new UIPropertyMetadata(0));

    public string SomeString
    {
        get { return (string)GetValue(SomeStringProperty); }
        set { SetValue(SomeStringProperty, value); }
    }

    public MyControl()
    {
        InitializeComponent();
        DataContext = this;
    }
}

Then in your XAML:

<TextBlock Text="{Binding Path=SomeString}" />
Community
  • 1
  • 1
fatty
  • 2,453
  • 2
  • 25
  • 24
  • Thanks for your reply. Unfortunately, it doesn't work, and I don't know. Imagine a control, which declares a dependency property (for the sake of certainty, lets call it `Items`). The control has no mention of `DataContext` property anywhere in the code or xaml. If you run the application, the property is set to some value. Now I add `this.DataContext = this;` in the control's constructor right after `InitializeControl();` call, and that's all the changes. Run the application, do the same scenario. The result is that the property `Items` is **never** set! Weird. – Eugene Strizhok Aug 30 '11 at 07:01
  • Hi, could you post a quick repro for yr problem? I'm sure you know that DataContext can be set right in XAML like any other DP - in your Style smth like: or . –  Aug 30 '11 at 08:43
  • I can, but not right now. Don't have time. Will post it once I get home. Will also try the approach of setting `DataContext` you've described. – Eugene Strizhok Aug 30 '11 at 09:14
  • Ok, I am back. My control differs from the one **fatty** wrote as an example only in name of the class, names and types of properties, and he is using `UIPropertyMetadata` where I use `PropertyMetadata`. There is not word _DataContext_ in the XAML of the control itself, nor it is in the single place there is control is used. And yet, after adding `DataContext = this;` in the constructor, the `SomeString` property is never set. And by never set I mean that if you subscribe a handler to this property change event, it will never get called. How's that? – Eugene Strizhok Aug 30 '11 at 17:28
  • Because it hasn't changed? In your output window, do you have any binding errors? (Note: these will be logged only, not thrown as exceptions in the IDE). Perhaps the reason the property changed handler was being called earlier was due to a two-way binding, and now that you've changes the DataContext, the property you have bound to no longer exists at that same binding path? Without seeing your XAML it will be hard to say. – fatty Aug 30 '11 at 20:45
  • Ok, I just tried another thing: `this.DataContext = this.DataContext` in the same place in the contructor. After that the property `SomeString` no longer has a value. Setting `DataContext` to itself doesn't have much sense, but at least it must not change program behaviour, isn't it? – Eugene Strizhok Aug 31 '11 at 17:15
  • I even tried using `diag:PresentationTraceSources.TraceLevel="High"`. Nothing! Maybe something wrong with my setup? – Eugene Strizhok Aug 31 '11 at 17:27
  • At last I was able to see the cause: `System.Windows.Data Error: 40 : BindingExpression path error: 'SearchSettings' property not found on 'object' ''SearchSettings' (HashCode=30787823)'. BindingExpression:Path=SearchSettings; DataItem='SearchSettings' (HashCode=30787823); target element is 'SearchSettings' (Name='searchSettings'); target property is 'Settings' (type 'ISearchSettings')`. Looks like I don't understand whose DataContext is being used while binding - of a parent or of a control, whose properties we are binding. – Eugene Strizhok Aug 31 '11 at 18:14
  • Yeah, that last one was the Binding error that I was talking about, where it gets thrown to the Output window. I will see if I can clarify my answer any to help you. – fatty Aug 31 '11 at 21:37
1

I really don't see a reason to create a property that substitutes for the DataContext

The purpose of the DataContext is to refer to the actual data behind your control. Why would you create a 2nd property for one that already exists? To me that's like saying you want to rewrite the IsReadOnly property of a control so it reads DisableEditing - there's no purpose for it.

And furthermore, why would you want tie it to your application layer? The whole point of WPF is to separate business/application logic from the UI, and what you are trying to do is tie the two together. Use WinForms if you want that behavior.

If you want to be sure your UserControl is only used with a User DataContext, use a DataTemplate.

<DataTemplate DataType="{x:Type local:User}">
    <local:MyUserControl /> <!-- DataContext will be User -->
</DataTemplate>

<!-- This will display a User object, but because of the DataTemplate it will 
     draw it using your UserControl instead of the default User.ToString() -->
<ContentControl Content="{Binding CurrentUser}" /> 

If you really want to ensure that your UserControl only receives a User object as the DataContext, do some kind of validation check on initialization that this.DataContext is a User object. Don't create some kind of custom property that people need to remember to bind anytime they want to use your control.

Rachel
  • 130,264
  • 66
  • 304
  • 490