MvvmBlazor icon indicating copy to clipboard operation
MvvmBlazor copied to clipboard

Cascading BindingContext

Open hillin opened this issue 3 years ago • 7 comments

Is there a way to have cascading BindingContext? Say we have two components:

class ParentComponent: MvvmComponentBase<ParentViewModel> {
    ChildComponent Child;
}
class ChildComponent: MvvmComponentBase<ChildViewModel> { }
class ParentViewModel: ViewModelBase {
    ChildViewModel Child { get; }
}
class ChildViewModel: ViewModelBase {}

Now I would like to bind the BindingContext of the Child component in the ParentComponent to the Child property of the ParentViewModel instance, which is automatically resolved for the ParentComponent. However it seems the view model is always resolved in the component's constructor via DI, and we don't have a public BindingContext property to override.

hillin avatar Jun 04 '21 15:06 hillin

One way is to register the view models as Scoped, which is generally not a good idea because it forces all the component instances of the same type to share a single view model instance. I also can always create a new property on the ChildViewModel and bind to that property instead of the BindingContext, but this kind of use case is very common and I think it should be supported by any MVVM framework.

One possible approach is make the BindingContext property public and [Parameter], and make its auto-resolve lazy, so the parent component have a chance to specify the child component's BindingContext as a parameter, before it's being used for the first time.

hillin avatar Jun 04 '21 15:06 hillin

One possible approach is make the BindingContext property public and [Parameter], and make its auto-resolve lazy, so the parent component have a chance to specify the child component's BindingContext as a parameter, before it's being used for the first time.

Sounds good to me, would love to see a PR with this. Otherwise, I'm gonna implement this.

klemmchr avatar Jun 04 '21 16:06 klemmchr

Right now I'm playing with this idea, but it raises some questions:

  • Should the auto-resolve be lazy or should we keep the SetBindingContext call in the OnInitialized method (i.e. the BindingContext can only be set externally before the component is initialized)?
  • If we set the BindingContext after the component is initialized, should the OnInitialized and OnInitializedAsync be called on the new BindingContext? Same goes to all other lifecycle methods.
  • Is the BindingContext bindable? If it's changed, should we update all the bindings in the component immediately?

hillin avatar Jun 04 '21 16:06 hillin

Should the auto-resolve be lazy or should we keep the SetBindingContext call in the OnInitialized method (i.e. the BindingContext can only be set externally before the component is initialized)?

As of now, the BindingContext is only resolved via DI if there is no binding context present. That would mean that making the property public should be enough to resolve this problem. This way, the lifecycle methods also get called on the right context right away.

Is the BindingContext bindable? If it's changed, should we update all the bindings in the component immediately?

I would argue against the possibility to change the binding context since it would open some potential loop holes. The binding context should be read only once set.

klemmchr avatar Jun 06 '21 11:06 klemmchr

As of now, the BindingContext is only resolved via DI if there is no binding context present. That would mean that making the property public should be enough to resolve this problem. This way, the lifecycle methods also get called on the right context right away.

If I'm not mistaken, the view model is set right in the constructor, so it's always present.

I would argue against the possibility to change the binding context since it would open some potential loop holes. The binding context should be read only once set.

For example WPF allows you to change DataContext, you can even bind something to it. But I agree it's not a must-have and for simplicity it's a reasonable design choice to not allow changing the view model.

hillin avatar Jun 06 '21 14:06 hillin

If I'm not mistaken, the view model is set right in the constructor, so it's always present.

This only applies for the internal constructor that is used for testing. This constructor serves no purpose besides that. When inheriting from MvvmComponentBase<T> the public constructor will be used. And then the BindingContext will be resolved on init, if not present.

For example WPF allows you to change DataContext, you can even bind something to it. But I agree it's not a must-have and for simplicity it's a reasonable design choice to not allow changing the view model.

It would be a nice to have thingie, however upon changing the BindingContext all bindings would need to be redone which could be a bit messy (but doable IMO).

klemmchr avatar Jun 06 '21 15:06 klemmchr

This only applies for the internal constructor that is used for testing. This constructor serves no purpose besides that. When inheriting from MvvmComponentBase<T> the public constructor will be used. And then the BindingContext will be resolved on init, if not present.

Right, I misread the code. So yeah, I agree for now making BindingContext public could have the job done (and maybe a little protection mechanism to prevent it from changing after OnInitialized).

hillin avatar Jun 07 '21 03:06 hillin