-
Notifications
You must be signed in to change notification settings - Fork 401
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
Fix TouchBehavior for CollectionView #1815
base: main
Are you sure you want to change the base?
Conversation
@@ -21,7 +21,12 @@ internal bool TrySetBindingContextToAttachedViewBindingContext() | |||
throw new InvalidOperationException($"{nameof(ICommunityToolkitBehavior<TView>)} can only be used for a {nameof(Behavior)}"); | |||
} | |||
|
|||
if (behavior.IsSet(BindableObject.BindingContextProperty) || View is null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What will happen in scenarios where a developer explicitly sets their own BindingContext for the behavior? I think this could would overwrite it with the BindingContext from the View?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. I added behavior.IsSet
when working on the TouchBehavior PR for two reasons here:
- To ensure we don't override a manually set BindingContext
- If a Behavior is reused and attached to a second View, it will
maintain the BindingContext of the first View it is attached to
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps the safer change is to remove the binding altogether and subscribe to the BindingContext changed event on the View and assign to the Behavior?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not being able to re-assing the BindingContext
will make all Behaviors un-usable on recycling/virtualization scenarios.
What do you think the correct approach would be?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have changed the OnAttachedTo
method in BaseBehavior
to look as follows:
protected override void OnAttachedTo(TView bindable)
{
base.OnAttachedTo(bindable);
bindable.PropertyChanging += (sender, args) =>
{
var currentViewBindingContext = ((TView)sender).BindingContext;
if (args.PropertyName == nameof(BindingContext) &&
ReferenceEquals(this.BindingContext, currentViewBindingContext))
{
this.BindingContext = null;
}
};
bindable.PropertyChanged += (sender, args) =>
{
if (args.PropertyName == nameof(BindingContext) &&
this.BindingContext is null)
{
this.BindingContext = ((TView?)sender)?.BindingContext;
}
};
}
It might not be the prettiest but I believe it should work. What does everyone think? Any ideas on how to make it nicer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have reverted the change and added in two examples from @tranb3r example repo. Perhaps @tranb3r you can test this sample and let us know how it compares to your expectation. The code works as I expect on iOS/macOS in our sample app
These are indeed the same cases as in my repro. Not working on android, unless using 8.0.0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry to ask again, but I'm still wondering if this BindingContext auto-set feature (introduced in 8.0.1) is really required?
It seems to me that 8.0.0 was fine. If the goal was only to simplify xaml code, I'm not sure it's worth it, considering the difficulty you have to figure out how to fix this regression.
I'm probably missing something, but could you please explain which bug did you try to fix in 8.0.1? Or at least post a code sample for the original issue?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I recall there was a lot of developers reporting an issue that their bindings didn't work. @brminnick Is this correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose their bindings didn't work because they were not setting the BindingContext manually.
…e sample application
…ommunityToolkit/Maui into pj/fix-binding-context-for-collections
…gContext and we are inside a CollectionView then we can assume that we are recycling views and need to assign the BindingContext again.
break; | ||
} | ||
|
||
if (parent is CollectionView) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe for testing this is enough for now... But collectionView isn't the only control that may have virtualization
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been looking for a suitable interface or base class but I don't think I have found one yet. I am thinking ItemsView
and possibly IItemsView
. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
still not enough, because devs can apply virtualization to any kind of control or layout... I'm still didn't get the reason to be so conservative on allowing changing the BindingContext. In other words, we shouldn't prevent the BindingContext to be mutable. You may have cases where the dev reuses the control across the page and wants the control's BindingContext to change when that happens
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see that we want to automatically inherit the BindingContext
from the View
it is attached to only if the BindingContext
is not already set. It appears there are issues around checking whether the BindingContext
is already set especially when the behavior is used inside something like the CollectionView
and the behavior is attached to recycled views.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, but the thing is that after the first set, that value will always be true
, and that's causing the issue. Because of that I removed the IsSet
check, as mentioned on the PR description.
I think this happens because when setting the new value, they don't use the RemoveBinding method, just BindinContext = someValue
, causing the IsSet
to never be false again
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't confirm the instances but I did discover that OnAttachedTo was being called before OnDetachedFrom when the recycling was happening. Which may explain why IsSet is always true after the first time.
Just simply push this solution. It is more performant than always set the binding context new:
|
Hello, what about the review? Isn't the merge already possible? |
Hello, this is really important for my app. Would it be possible to merge this or is there a problem left? |
@BusterIT I don't think that solutions works for all use cases. It won't work if you have a behavior attached to a view inside a CollectionView and it's bound to something outside of that view. It will work on the first pass but not if a view is recycled. Unless I am missing something |
For the others asking, I don't recall the state of this PR. I can try to take a look at it soon but have any of you tested that this solves your problem? |
While the concerns about handling behaviors in recycled views within a CollectionView are valid, the modifications I introduced aim to ensure the Behavior's BindingContext is initially synchronized with the View's BindingContext more reliably than before. The original code did not address changes to the BindingContext post-initialization, which is a limitation that remains consistent even after my changes. To address the concerns about recycling:
In summary, the modifications do not introduce new limitations but rather provide a foundation on which more robust handling of dynamic contexts and view recycling can be built. |
@BusterIT thanks for the detailed response. If you look back through the review comments you will see that the use case that I am referring to was a blocking reason why this PR didn't get merged therefore it is something I think we need to consider. I believe that we can find some common ground between your proposed changes and the desired use cases we need to support 👍 |
Description of Change
Remove the
IsSet
check onICommunityToolkitBehavior
, this check will fail for any control that recycle views, since the behavior on that particular view will be already set, causing it to not update. Removing this check will make sure we can update theBindingContext
.Linked Issues
PR Checklist
approved
(bug) orChampioned
(feature/proposal)main
at time of PRAdditional information