This repository has been archived by the owner on May 1, 2024. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ListView: avoid that disabling RefreshAllowed cancels refresh indicat…
…or on Android, fixes #8384 (#14816) * Add test for issue #8384 * ListView: avoid that disabling RefreshAllowed cancels refresh indicator on Android, fixes #8384 RefreshAllowed is bound to ListView.RefreshCommand.CanExecute(). Often an implementation of RefreshCommand might update its CanExecute status after execution starts. This caused ListViewRenderer to immediately disable the SwipeRefreshLayout, thereby cancelling the refresh/activity indicator on top of the list view. The solution is to NOT disable it while refreshing, but waiting for the next chance when current refresh activity/command is done. * Only enable Issue8384 constructor for XF controls app build (not for UI unit tests) Otherwise we get 'InitializeComponent' not found error when compiling UI unit test projects.
- Loading branch information
Showing
4 changed files
with
188 additions
and
1 deletion.
There are no files selected for viewing
24 changes: 24 additions & 0 deletions
24
Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue8384.xaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
<?xml version="1.0" encoding="utf-8" ?> | ||
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" | ||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | ||
xmlns:d="http://xamarin.com/schemas/2014/forms/design" | ||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | ||
mc:Ignorable="d" | ||
Title="Test 8384" | ||
x:Class="Xamarin.Forms.Controls.Issues.Issue8384"> | ||
|
||
<StackLayout> | ||
<Label Text="Test for issue #8384" /> | ||
<ListView IsPullToRefreshEnabled="True" ItemsSource="{Binding Items}" RefreshCommand="{Binding Refresh}" | ||
IsRefreshing="{Binding IsRefreshing}"> | ||
</ListView> | ||
<StackLayout> | ||
<Label Text="Test steps (Android):" /> | ||
<Label Text="1. Swipe to refresh the list (refresh takes 4 seconds)" /> | ||
<Label Text="2. Refresh/busy indicator appears at the top" /> | ||
<Label Text="3. Refresh/busy indicator should remain visible until list content changes (refresh is finished). | ||
due to issue #8384 the indicator vanishes too early if Command.CanExecute toggles (in this test after 1 second)." /> | ||
<Label Text="4. Repeat steps 1 to 3 multiple times" /> | ||
</StackLayout> | ||
</StackLayout> | ||
</ContentPage> |
145 changes: 145 additions & 0 deletions
145
Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue8384.xaml.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.ComponentModel; | ||
using System.Diagnostics; | ||
using System.Threading.Tasks; | ||
|
||
using Xamarin.Forms; | ||
using Xamarin.Forms.CustomAttributes; | ||
using Xamarin.Forms.Internals; | ||
|
||
namespace Xamarin.Forms.Controls.Issues | ||
{ | ||
// Learn more about making custom code visible in the Xamarin.Forms previewer | ||
// by visiting https://aka.ms/xamarinforms-previewer | ||
[DesignTimeVisible(false)] | ||
[Preserve(AllMembers = true)] | ||
[Issue(IssueTracker.Github, 8384, | ||
"[Bug] [5.0] [Android] [Bug] ListView RefreshCommand ActivityIndicator does disappear on Android if CanExecute is changed to false", | ||
PlatformAffected.Android)] | ||
public partial class Issue8384 : ContentPage | ||
{ | ||
public Issue8384() | ||
{ | ||
#if APP | ||
InitializeComponent(); | ||
BindingContext = new ViewModelIssue8384(); | ||
#endif | ||
} | ||
} | ||
|
||
class ViewModelIssue8384 : INotifyPropertyChanged | ||
{ | ||
public class MyCommand : Command | ||
{ | ||
private bool _allow; | ||
|
||
public MyCommand(Action<object> execute, Func<object, bool> canExecute) : base(execute, canExecute) | ||
{ | ||
Allow = true; | ||
} | ||
|
||
public bool Allow | ||
{ | ||
get | ||
{ | ||
return _allow; | ||
} | ||
set | ||
{ | ||
_allow = value; | ||
ChangeCanExecute(); | ||
} | ||
} | ||
} | ||
|
||
private List<string> _items; | ||
private bool _isRefreshing; | ||
private MyCommand _refresh; | ||
|
||
static readonly List<string> FIRST_LIST = new List<string>() { | ||
"one", "two", "three" | ||
}; | ||
|
||
static readonly List<string> SECOND_LIST = new List<string>() { | ||
"four", "five", "six" | ||
}; | ||
|
||
public bool IsRefreshing | ||
{ | ||
get | ||
{ | ||
return _isRefreshing; | ||
} | ||
set | ||
{ | ||
_isRefreshing = value; | ||
OnPropertyChanged("IsRefreshing"); | ||
} | ||
} | ||
|
||
public ViewModelIssue8384() | ||
{ | ||
Items = FIRST_LIST; | ||
|
||
Refresh = new MyCommand(Execute, CanExecute); | ||
} | ||
|
||
public event PropertyChangedEventHandler PropertyChanged; | ||
|
||
private void OnPropertyChanged(string propertyName) | ||
{ | ||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); | ||
} | ||
|
||
private async void Execute(object parameter) | ||
{ | ||
IsRefreshing = true; | ||
Debug.WriteLine("Refresh start"); | ||
await Task.Delay(1000).ConfigureAwait(false); | ||
|
||
// Side note: doing this off the main thread throws an exception | ||
Device.BeginInvokeOnMainThread(() => { _refresh.Allow = false; }); | ||
|
||
await Task.Delay(3000).ConfigureAwait(false); | ||
Items = (Items == FIRST_LIST) ? SECOND_LIST : FIRST_LIST; | ||
|
||
Debug.WriteLine("Refresh end"); | ||
IsRefreshing = false; | ||
|
||
Device.BeginInvokeOnMainThread(() => { _refresh.Allow = true; }); | ||
} | ||
|
||
private bool CanExecute(object parameter) | ||
{ | ||
return _refresh.Allow; | ||
} | ||
|
||
public List<string> Items | ||
{ | ||
get | ||
{ | ||
return _items; | ||
} | ||
|
||
set | ||
{ | ||
_items = value; | ||
OnPropertyChanged("Items"); | ||
} | ||
} | ||
|
||
public MyCommand Refresh | ||
{ | ||
get | ||
{ | ||
return _refresh; | ||
} | ||
set | ||
{ | ||
_refresh = value; | ||
OnPropertyChanged("Refresh"); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters