0

I have dependency property in SliderViewModel where this view model implements DependencyObject and is set as the data context for BRSliderUserControl. How can I bind to the dependency property in the view model from AmplitudeOptionsUserControl. Is it possible to do so. My guess is I need to create an other dependency property in BRSliderUserControl and then send the update value to the view model. Is this the right way though?

SliderViewModel.cs

public Class SliderViewModel:DependencyObject
{
    public AnalysisViewType AnalysisTypeValue
            {
                get { return (AnalysisViewType)GetValue(AnalysisTypeDependencyProperty); }
                set { SetValue(AnalysisTypeDependencyProperty, value); }
            }


            public static readonly DependencyProperty AnalysisTypeDependencyProperty =
                DependencyProperty.Register("AnalysisTypeValue", typeof(AnalysisViewType), typeof(SliderViewModel),
                    new PropertyMetadata(AnalysisViewType.Unassigned, OnAnalysisTypeChanged));

            private static void OnAnalysisTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                //Do something here
            }
}

BRSliderUserControl.xaml.cs

  public BRSliderUserControl()
            {
                InitializeComponent();
                SliderViewModel sliderViewModel = new SliderViewModel();
                this.DataContext = sliderViewModel;
            }

Now how can I bind to that dependency property from another user control?

AmplitudeOptionsControl.xaml

//This does not work..

    <lib:BRSliderUserControl
                    Grid.Row="5"
                    Grid.Column="0"
                    Grid.ColumnSpan="3"
                    AnalysisTypeValue="{Binding AmplitudeOptionsVM.AnalysisType,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"
                 />
nikhil
  • 1,578
  • 3
  • 23
  • 52
  • maybe this helps: https://stackoverflow.com/search?q=mvvm+bind+to+viewmodel – Dbl Sep 07 '17 at 23:13
  • 1
    Your viewmodels should not be dependency objects and there's no need for them to be. Use INotifyPropertyChanged instead, it's more lightweight and more appropriate. – 15ee8f99-57ff-4f92-890c-b56153 Sep 07 '17 at 23:15
  • What is `AmplitudeOptionsVM`? The name of a property of `AmplitudeOptionsControl`? – 15ee8f99-57ff-4f92-890c-b56153 Sep 07 '17 at 23:17
  • AmplitudeOptionsVM is the name of the class and AnalysisType is the property name inside that class. – nikhil Sep 07 '17 at 23:21
  • Well, the type of the property isn't the name of the property. If you want the length of a string you don't say `"foo".int`, you say `"foo".Length`. Does `AmplitudeOptionsControl` own a copy of that vm? If it's the `DataContext`, you want `AnalysisTypeValue="{Binding DataContext.AnalysisType,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"` – 15ee8f99-57ff-4f92-890c-b56153 Sep 07 '17 at 23:33
  • Yes you are right the AmplitudeOptionsVM is the DataContext for AmplitudeOptionsControl. My only question is I can't say AnalysisTypeValue={Binding blah blah} because AnalysisTypeValue is not a dependency property in the BRSliderUserControl.xaml.cs. Can I say DataContext.AnalysisTypeValue={Binding whatever} because AnalysisTypeValue is a dependency property in the BRUserControl's data context . Probably not. – nikhil Sep 07 '17 at 23:41
  • Standard practice here would be to make AnalysisTypeValue a dependency property of BRSliderUserControl itself -- for just this reason. If it's just a regular ui input kind of control it really doesn't need a viewmodel. It sounds like AmplitudeOptions is however the kind of UserControl that ought to have a viewmodel. – 15ee8f99-57ff-4f92-890c-b56153 Sep 08 '17 at 02:21
  • Update: See mm8's answer; I don't know what I was thinking when I gave you that baroque RelativeSource binding -- it should work, but the DataContext of the UserControl should *already* be the source for the binding, so `{Binding AnalysysType}` should do the same exact thing as the horror I gave you. Sorry. – 15ee8f99-57ff-4f92-890c-b56153 Sep 08 '17 at 14:29

2 Answers2

2

You don't need dependency property in your View Models. Your ViewModel should implement INotifyPropertyChanged interface, and your properties should raise NotifyPropertyChanged event when the value changes. There are many helpers around which makes this a bit easier.

You can use Dependency property if you want, but it makes your view models dependent on WPF, although binding to Dependency properties seems to be much faster (see here: https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/optimizing-performance-data-binding)

You can see a discussion here: INotifyPropertyChanged vs. DependencyProperty in ViewModel

Also, since your DataContext is of type SliderViewModel, which has a public property named AnalysisTypeValue, in your XAML you should bind like this

... AnalysisTypeValue = {Binding AnalysisTypeValue}
  • Yea I know that but how do I get the actual value for Analysis Type from AmplitudeOptionsControl. Problem is I need to get the value from a different user control to this one and databind it. There should be a dependency property in the code behind for BRSliderUserControl and I can bind this value from AmplitudeOptionsControl. But each time I need to propogate this value to the ViewModel. – nikhil Sep 07 '17 at 23:27
  • 1
    If you implement your View model as INPC, then the changed value is in your AnalysisTypeValue setter. –  Sep 07 '17 at 23:28
1

Move the dependency property to the code-behind of the UserControl class:

public class BRSliderUserControl
{
    public AnalysisViewType AnalysisTypeValue
    {
        get { return (AnalysisViewType)GetValue(AnalysisTypeDependencyProperty); }
        set { SetValue(AnalysisTypeDependencyProperty, value); }
    }

    public static readonly DependencyProperty AnalysisTypeDependencyProperty =
        DependencyProperty.Register("AnalysisTypeValue", typeof(AnalysisViewType), typeof(BRSliderUserControl),
            new PropertyMetadata(AnalysisViewType.Unassigned, OnAnalysisTypeChanged));

    private static void OnAnalysisTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        //Do something here
    }

    public BRSliderUserControl()
    {
        InitializeComponent();
        SliderViewModel sliderViewModel = new SliderViewModel();
        this.DataContext = sliderViewModel;
    }
}

Add a plain CLR property to the view model:

public class SliderViewModel : INotifyPropertyChanged
{
    private AnalysisViewType _analysisTypeValue;
    public AnalysisViewType AnalysisTypeValue
    {
        get { return _analysisTypeValue; }
        set { _analysisTypeValue = value; NotifyPropertyChanged(); }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

Bind the dependency property in the view to the source property of the view model:

<lib:BRSliderUserControl
                Grid.Row="5"
                Grid.Column="0"
                Grid.ColumnSpan="3"
                AnalysisTypeValue="{Binding AnalysisTypeValue}" />
mm8
  • 163,881
  • 10
  • 57
  • 88
  • Just a small correction the NotifyPropertyChanged(); should be NotifyPropertyChanged("AnalysisTypeValue"); – nikhil Sep 08 '17 at 18:06
  • 1
    @nikhil the `[CallerMemberName]` attribute on the method signature's arguments means you can shortcut passing in the property name – James Love May 29 '19 at 11:33