Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Propagate bindings on ViewModel of UserControl #2596

Closed
JenWilson82653 opened this issue Feb 17, 2020 · 10 comments
Closed

Propagate bindings on ViewModel of UserControl #2596

JenWilson82653 opened this issue Feb 17, 2020 · 10 comments

Comments

@JenWilson82653
Copy link

I have a UserControl that uses a ViewModel. I want to be able to set a property on the ViewModel of the control and have the value propagate via bindings up to the ViewModel of the host page:

<MainWindow DataContext="{Binding MainWindowViewModel}">
	<MyControl ViewModel="{Binding MyControlViewModel}" SelectedTabIndex="{Binding SelectedTabIndex}" />
</MainWindow>

Both MainWindowViewModel and MyControlViewModel have a SelectedTabIndex property. I want to change that property in a private method on MyControlViewModel and let the binding change in on MainWindowViewModel.

I created a very simple working test harness that demonstrates the problem.

The only related question I could find on StackOverflow is this one which in my opinion does not provide a very useful answer (or perhaps I just don't understand it).

@weltkante
Copy link

weltkante commented Feb 17, 2020

Use SetCurrentValue instead of the property accessor when you want to update the value from within MyControl, the property accessor (which will be using SetValue) discards the binding if there was any.

@JenWilson82653
Copy link
Author

@weltkante

   public int SelectedTabIndex
    {
        get { return (int)GetValue(SelectedTabIndexProperty); }
        set { SetCurrentValue(SelectedTabIndexProperty, value); }
    }

I also tried in the change callback:

thisControl.SetCurrentValue(SelectedTabIndexProperty, e.NewValue);

Still not working sorry.

@weltkante
Copy link

No you are not supposed to use SetCurrentValue in the property accessor, that would break any external users of your control. SetCurrentValue is for your controls implementation.

For it to work someone must have bound to the control in the first place of course, and the bindings must be Mode=TwoWay (default for WPF, not for UWP). I'll have a look at your repo and submit a PR how to fix it. Otherwise feel free to search for SetCurrentValue examples/explanations because thats the API intended to write back values through bindings.

@JenWilson82653
Copy link
Author

@weltkante Thank you. I will research SetCurrentValue.

@weltkante
Copy link

weltkante commented Feb 17, 2020

Your problem is that your example repository makes no sense as far as your question is concerned, it is not testing what you are asking about:

  • You have multiple ViewModel instances each with their own SelectedTabIndex variable but no code to sync them. Note that you cannot use WPF bindings to sync two ViewModels based on INotifyPropertyChanged, one side must be a DependencyProperty for WPF bindings to work. (Not that it matters, you didn't even try to sync the view models in xaml or code at all.)
  • SetCurrentValue doesn't matter for your example at all because your command (on the button) is operating directly on the ViewModel, you don't have any code which wants to change SelectedTabProperty from within the control

I've made a PR adding a button to showcase usage of SetCurrentValue. Had to clean up your control implementation a bit:

  • don't implement INotifyPropertyChange on controls, you inherit from DependencyObject and can (and should) use DependencyProperties
  • use a Binding instead of implementing code to react on ViewModel changes

@weltkante
Copy link

weltkante commented Feb 17, 2020

I now see I was misreading your original question, it looks like you were hoping to bind multiple ViewModels SelectedTabIndex agains the control to magically keep them in sync, thats not possible, WPF only can have one binding per property, you'd be replacing the previous binding if you tried that.

@weltkante
Copy link

I created another PR which is probably what you initially intended to do, manually having the control sync the ViewModel and the SelectedTabIndex. Like said above cannot be done in pure XAML because you can only have one binding per property. (So the SetBinding in the control constructor was redundant because it was getting overwritten anways.)

@JenWilson82653
Copy link
Author

@weltkante Thanks for your PR, you've put me on the path to enlightenment. BTW You didn't misread my question and I'm not trying anything magical. I happen to have two instances of the child control in the example but I'm not trying to sync them. I am only trying to do what the question says I'm trying to do. Thanks again.

@weltkante
Copy link

The problem is that you were having multiple ViewModels with distinct SelectedTabIndex variables, which needs code to sync them. I hope you can work off these examples to find a way and figure out what you need.

@JenWilson82653
Copy link
Author

After thinking about it for minute I guess you could say I'm trying to sync the control ViewModels - but not directly. If they are in sync with MainWindowViewModel (my stated objective) they will be in sync with each other.

@ghost ghost locked as resolved and limited conversation to collaborators Apr 13, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants