Jeffrey Zhao
Jeffrey Zhao

Reputation: 5023

Why WPF uses control's view model when binding value to the control?

I'm writing a value input control can be used everywhere. The control itself has a view model which set to its DataContext as usual. But when I use the control in a parent control like:

<UserControl x:Class="X.Y.Z.ParentControl">
    ...
    <local:ValueInput Value="{Binding Path=MyValue}" />
    ...
</UserControl>

I'm going to bind the MyValue property of ParentControl's DataContext to the ValueInput control, but WPF tell me it cannot find the MyValue property in ValueInputViewModel class, which is the view model of ValueInput control itself. Why WPF is looking for the value from child's DataContext?

I just want to write a control which can be used like this:

<telerik:RadNumericUpDown Value="{Binding Path=NumberValue}" />

The NumberValue property is defined in in the parent's DataContext, not in the control's. This pattern works for teleriks control but not for my control.

What should I do?

Upvotes: 0

Views: 1330

Answers (2)

Jeffrey Zhao
Jeffrey Zhao

Reputation: 5023

My friend told me not to use DataContext as the view model in a standalone control since DataContext would be easily overridden - define a ViewModel property and bind in the XAML could solve the problem. Here's an example:

View model class:

public class MyValueInputViewModel
{
    public string MyText { get; set; }
}

Code behind:

public partial class MyValueInput : UserControl
{
    public MyValueInput()
    {
        InitializeComponent();

        this.ViewModel = new MyValueInputViewModel
        {
            MyText = "Default Text"
        };
    }

    public static readonly DependencyProperty ViewModelProperty =
        DependencyProperty.Register("ViewModel", typeof(MyValueInputViewModel), typeof(MyValueInput));

    public MyValueInputViewModel ViewModel
    {
        get
        {
            return (MyValueInputViewModel)this.GetValue(ViewModelProperty);
        }
        private set
        {
            this.SetValue(ViewModelProperty, value);
        }
    }

    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register("Value", typeof(string), typeof(MyValueInput), new PropertyMetadata(OnValuePropertyChanged));

    private static void OnValuePropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs args)
    {
        var input = (MyValueInput)o;
        input.ViewModel.MyText = input.Value;
    }

    public string Value
    {
        get { return (string)this.GetValue(ValueProperty); }
        set { this.SetValue(ValueProperty, value); }
    }
}

XAML:

<UserControl x:Class="..." x:Name="Self" ...>
    <Grid>
        <TextBox Text="{Binding ViewModel.MyText, ElementName=Self, UpdateSourceTrigger=PropertyChanged}" />
    </Grid>
</UserControl>

Upvotes: 0

Tilak
Tilak

Reputation: 30698

For any FrameworkElement, there can be only 1 DataContext.

If UserControl has its own DataContext, it cannot use parent's DataContext.

However you can walk up to parent and get its DataContext (each time you need to reference Parent's DataContext) using RelativeSource

Binding="{Binding RelativeSource={RelativeSource FindAncestor, 
AncestorType={x:Type Window}}, Path=DataContext.NumberValue}"

For this example to work, Parent (root at any level) should be Window. If it is a UserControl,

Binding="{Binding RelativeSource={RelativeSource FindAncestor, 
AncestorType={x:Type UserControl}}, Path=DataContext.NumberValue}"

The code is from this link provided by fiq

Upvotes: 1

Related Questions