-
Notifications
You must be signed in to change notification settings - Fork 39
Description
Feature name
Add RelativeBindingSource
Support to Typed Bindings
Link to discussion
None
Progress tracker
- Android Implementation
- iOS Implementation
- MacCatalyst Implementation
- Windows Implementation
- Tizen Implementation
- Unit Tests
- Samples
- Documentation
Summary
This Proposal improves support for RelativeBindingSource
to our TypedBinding extensions.
This is not a breaking change and improves our API surface to better match .NET MAUI's TypedBinding API for its TypedBinding.Source
property.
RelativeBindingSource
is the recommended way for .NET MAUI developers to creating a binding referencing themselves or an ancestor without needing to pass in an instance of that object
- Self Binding
RelativeBindingSource.Self
- Binding to an ancestor in the UI hierarchy
new RelativeBindingSource(RelativeBindingSourceMode.FindAncestor, typeof(Page))
)
- Binding to an ancestor in the BindingContext hierarchy
new RelativeBindingSource(RelativeBindingSourceMode.FindAncestorBindingContext, typeof(MyViewModel))
Current Workaround
The current workaround to use RelativeBindingSource
with our existing TypedBinding APIs is to first use .Assign()
to assign the control to a variable, then pass that variable in as the source
parameter.
.Assign(out CarouselView carouselView)
.Bind(CarouselView.CurrentItemChangedCommandParameterProperty, static (CarouselView view) => view.CurrentItem, source: carouselView)
Motivation
All of our TypedBinding APIs (example below) require the source
parameter type to match the TBindingContext
used in by the getter
and setter
parameter:
Expression<Func<TBindingContext, TSource>> getter
Action<TBindingContext, TSource>? setter = null
TBindingContext? source = default
// Cannot be of typeRelativeBindingSource
public static TBindable Bind<TBindable, TBindingContext, TSource>(
this TBindable bindable,
BindableProperty targetProperty,
Expression<Func<TBindingContext, TSource>> getter,
Action<TBindingContext, TSource>? setter = null,
BindingMode mode = BindingMode.Default,
string? stringFormat = null,
TBindingContext? source = default) where TBindable : BindableObject);
Detailed Design
The best way to support RelativeBindingSource
, is to lower the Type
of the API's source
parameter from TBindingContext?
to object?
.
Using object?
for the source
parameter matches the .NET MAUI API for the TypedBinding.Source
property.
Current Source Parameter
TBindingContext? source = default
Updated Source Parameter
object? source = null
Example Updated API
/// <summary>Bind to a specified property</summary>
public static TBindable Bind<TBindable, TBindingContext, TSource>(
this TBindable bindable,
BindableProperty targetProperty,
Expression<Func<TBindingContext, TSource>> getter,
Action<TBindingContext, TSource>? setter = null,
BindingMode mode = BindingMode.Default,
string? stringFormat = null,
object? source = null) // Lowered from TBindingContext? source = default
where TBindable : BindableObject;
Usage Syntax
.Bind(CarouselView.CurrentItemChangedCommandParameterProperty, static (CarouselView view) => view.CurrentItem, source: RelativeBindingSource.Self)
Drawbacks
I don't see any drawbacks.
We will need to update our Unit Tests to validate RelativeBindingSource
.
We will also need to update our Unit Tests with invalid source
parameters to ensure .NET MAUI throws an exception if a user provides an invalid source
parameter to our API now that it supports any object
type.
Alternatives
An alternative to lowering our existing API surface from TBindingContext? source = default
to object? source = null
would be to create new APIs specific to RelativeBindingSource
(example below).
I do not recommend this approach for two reasons:
- It doubles our API surface for TypedBinding support, increasing our maintenance cost
- Requires one TypedBinding API for
TBindingContext? source = default
and a second TypedBinding API forobject? source = null
- Requires one TypedBinding API for
- Lowering the parameter Type to
object?
better matches .NET MAUI's TypedBinding.Source API (public object? Source
)
Example API using RelativeBindingSource
public static TBindable Bind<TBindable, TBindingContext, TSource>(
this TBindable bindable,
BindableProperty targetProperty,
Expression<Func<TBindingContext, TSource>> getter,
Action<TBindingContext, TSource>? setter = null,
BindingMode mode = BindingMode.Default,
string? stringFormat = null,
RelativeBindingSource? source = null) where TBindable : BindableObject);
Unresolved Questions
None