-
Notifications
You must be signed in to change notification settings - Fork 402
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?
Changes from all commits
733028e
5416550
6a0ba42
dc97e91
f59357e
2f42fd0
2be91a1
6fa5dc0
0e9df40
ffb6dcc
0812a7d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
using CommunityToolkit.Mvvm.Input; | ||
|
||
namespace CommunityToolkit.Maui.Sample.ViewModels.Behaviors; | ||
|
||
public partial class ItemViewModel : BaseViewModel | ||
{ | ||
public string Title { get; } | ||
|
||
public ItemViewModel(string title) | ||
{ | ||
Title = title; | ||
} | ||
|
||
[RelayCommand] | ||
public Task TapAsync() | ||
{ | ||
return Application.Current?.MainPage?.DisplayAlert("Tap", Title, "OK") ?? Task.CompletedTask; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,19 +21,47 @@ 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) | ||
if (View is null) | ||
{ | ||
return false; | ||
} | ||
|
||
var parent = View.Parent; | ||
var assignBindingContext = behavior.IsSet(BindableObject.BindingContextProperty) is false; | ||
|
||
// If we have a BindingContext, the type is the same as the Views BindingContext and we are inside a CollectionView | ||
// then we can assume that we are recycling views and need to assign the BindingContext again. | ||
if (View.BindingContext?.GetType() == behavior.BindingContext?.GetType()) | ||
{ | ||
for (var i = 0; i < 10; i++) | ||
{ | ||
if (parent is null) | ||
{ | ||
break; | ||
} | ||
|
||
if (parent is CollectionView) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe 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 commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more. I see that we want to automatically inherit the There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 I think this happens because when setting the new value, they don't use the RemoveBinding method, just There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
{ | ||
assignBindingContext = true; | ||
break; | ||
} | ||
|
||
parent = parent.Parent; | ||
} | ||
} | ||
|
||
if (assignBindingContext is false) | ||
{ | ||
return false; | ||
} | ||
|
||
behavior.SetBinding(BindableObject.BindingContextProperty, new Binding | ||
{ | ||
Source = View, | ||
Path = BindableObject.BindingContextProperty.PropertyName | ||
}); | ||
|
||
return true; | ||
|
||
} | ||
|
||
internal bool TryRemoveBindingContext() | ||
|
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: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 inBaseBehavior
to look as follows: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.
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.
@tranb3r thank you for confirming.
@tranb3r and @pictos the changes that I have just pushed appear to solve the issue on Android based on my testing.
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.