开发者

Linking DataContext with another property in WPF

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, DependencyPropert开发者_Go百科yChangedEventArgs 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.


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}" />


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜