1
0
Fork 0
mirror of https://github.com/Nioux/AideDeJeu.git synced 2025-10-31 15:36:07 +00:00

Chargement en async + indicateur d'activité

This commit is contained in:
yassine benabbas 2018-05-30 16:57:48 +02:00
parent bfe8f0d81f
commit 5d28b9b469
6 changed files with 145 additions and 130 deletions

View file

@ -1,11 +1,11 @@
#pragma warning disable 1591 #pragma warning disable 1591
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// <auto-generated> // <auto-generated>
// Ce code a été généré par un outil. // This code was generated by a tool.
// Version du runtime :4.0.30319.42000 // Runtime Version:4.0.30319.42000
// //
// Les modifications apportées à ce fichier peuvent provoquer un comportement incorrect et seront perdues si // Changes to this file may cause incorrect behavior and will be lost if
// le code est régénéré. // the code is regenerated.
// </auto-generated> // </auto-generated>
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View file

@ -7,8 +7,12 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<LangVersion>Latest</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<LangVersion>Latest</LangVersion>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Remove="Resources\AdJTheme.xaml" /> <EmbeddedResource Remove="Resources\AdJTheme.xaml" />
</ItemGroup> </ItemGroup>

View file

@ -8,13 +8,15 @@ using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Windows.Input; using System.Windows.Input;
using System.Threading;
using System.Threading.Tasks;
namespace AideDeJeu.ViewModels namespace AideDeJeu.ViewModels
{ {
public abstract class FilterViewModel : BaseViewModel public abstract class FilterViewModel : BaseViewModel
{ {
public ICommand LoadItemsCommand { get; protected set; } public ICommand LoadItemsCommand { get; protected set; }
public abstract IEnumerable<Item> FilterItems(IEnumerable<Item> items); public abstract Task<IEnumerable<Item>> FilterItems(IEnumerable<Item> items, CancellationToken token = default);
public abstract IEnumerable<Filter> Filters { get; } public abstract IEnumerable<Filter> Filters { get; }
private string _SearchText = ""; private string _SearchText = "";
public string SearchText public string SearchText
@ -77,7 +79,7 @@ namespace AideDeJeu.ViewModels
{ {
get get
{ {
if(Index >= 0 && Index < KeyValues.Count) if (Index >= 0 && Index < KeyValues.Count)
{ {
return KeyValues[Index].Key; return KeyValues[Index].Key;
} }
@ -94,7 +96,7 @@ namespace AideDeJeu.ViewModels
{ {
get get
{ {
if(_Filters == null) if (_Filters == null)
{ {
_Filters = new List<Filter>() _Filters = new List<Filter>()
{ {
@ -111,29 +113,31 @@ namespace AideDeJeu.ViewModels
} }
public override IEnumerable<Item> FilterItems(IEnumerable<Item> items) public override async Task<IEnumerable<Item>> FilterItems(IEnumerable<Item> items, CancellationToken token = default)
{ {
var classe = Filters.SingleOrDefault(filter => filter.Key == FilterKeys.Class).SelectedKey ?? ""; return await Task.Run(() =>
var niveauMin = Filters.SingleOrDefault(filter => filter.Key == FilterKeys.MinLevel).SelectedKey ?? "0"; {
var niveauMax = Filters.SingleOrDefault(filter => filter.Key == FilterKeys.MaxLevel).SelectedKey ?? "9"; var classe = Filters.SingleOrDefault(filter => filter.Key == FilterKeys.Class).SelectedKey ?? "";
var ecole = Filters.SingleOrDefault(filter => filter.Key == FilterKeys.School).SelectedKey ?? ""; var niveauMin = Filters.SingleOrDefault(filter => filter.Key == FilterKeys.MinLevel).SelectedKey ?? "0";
var rituel = Filters.SingleOrDefault(filter => filter.Key == FilterKeys.Ritual).SelectedKey ?? ""; var niveauMax = Filters.SingleOrDefault(filter => filter.Key == FilterKeys.MaxLevel).SelectedKey ?? "9";
var source = Filters.SingleOrDefault(filter => filter.Key == FilterKeys.Source).SelectedKey ?? ""; var ecole = Filters.SingleOrDefault(filter => filter.Key == FilterKeys.School).SelectedKey ?? "";
var rituel = Filters.SingleOrDefault(filter => filter.Key == FilterKeys.Ritual).SelectedKey ?? "";
var source = Filters.SingleOrDefault(filter => filter.Key == FilterKeys.Source).SelectedKey ?? "";
token.ThrowIfCancellationRequested();
return items.Where(item =>
{
var spell = item as Spell;
return (int.Parse(spell.Level) >= int.Parse(niveauMin)) &&
(int.Parse(spell.Level) <= int.Parse(niveauMax)) &&
spell.Type.ToLower().Contains(ecole.ToLower()) &&
spell.Source.Contains(source) &&
spell.Source.Contains(classe) &&
spell.Type.Contains(rituel) &&
spell.NamePHB.ToLower().Contains(SearchText.ToLower());
}).OrderBy(spell => spell.NamePHB)
.AsEnumerable();
}, token);
return items
.Where(item =>
{
var spell = item as Spell;
return (int.Parse(spell.Level) >= int.Parse(niveauMin)) &&
(int.Parse(spell.Level) <= int.Parse(niveauMax)) &&
spell.Type.ToLower().Contains(ecole.ToLower()) &&
spell.Source.Contains(source) &&
spell.Source.Contains(classe) &&
spell.Type.Contains(rituel) &&
spell.NamePHB.ToLower().Contains(SearchText.ToLower());
})
.OrderBy(spell => spell.NamePHB)
.ToList();
} }
public abstract List<KeyValuePair<string, string>> Classes { get; } public abstract List<KeyValuePair<string, string>> Classes { get; }
@ -343,19 +347,30 @@ namespace AideDeJeu.ViewModels
} }
} }
public override IEnumerable<Item> FilterItems(IEnumerable<Item> items) public override async Task<IEnumerable<Item>> FilterItems(IEnumerable<Item> items, CancellationToken token = default)
{ {
var powerComparer = new PowerComparer(); return await Task.Run(() =>
{
var powerComparer = new PowerComparer();
//var category = Filters.SingleOrDefault(filter => filter.Key == FilterKeys.Category).SelectedKey ?? ""; //var category = Filters.SingleOrDefault(filter => filter.Key == FilterKeys.Category).SelectedKey ?? "";
var type = Filters.SingleOrDefault(filter => filter.Key == FilterKeys.Type).SelectedKey ?? ""; var type = Filters.SingleOrDefault(filter => filter.Key == FilterKeys.Type).SelectedKey ?? "";
var minPower = Filters.SingleOrDefault(filter => filter.Key == FilterKeys.MinPower).SelectedKey ?? "0 (0 PX)"; token.ThrowIfCancellationRequested();
var maxPower = Filters.SingleOrDefault(filter => filter.Key == FilterKeys.MaxPower).SelectedKey ?? "30 (155000 PX)";
var size = Filters.SingleOrDefault(filter => filter.Key == FilterKeys.Size).SelectedKey ?? "";
//var legendary = Filters.SingleOrDefault(filter => filter.Key == FilterKeys.Legendary).SelectedKey ?? "";
var source = Filters.SingleOrDefault(filter => filter.Key == FilterKeys.Source).SelectedKey ?? "";
return items.Where(item => var minPower = Filters.SingleOrDefault(filter => filter.Key == FilterKeys.MinPower).SelectedKey ?? "0 (0 PX)";
token.ThrowIfCancellationRequested();
var maxPower = Filters.SingleOrDefault(filter => filter.Key == FilterKeys.MaxPower).SelectedKey ?? "30 (155000 PX)";
token.ThrowIfCancellationRequested();
var size = Filters.SingleOrDefault(filter => filter.Key == FilterKeys.Size).SelectedKey ?? "";
token.ThrowIfCancellationRequested();
//var legendary = Filters.SingleOrDefault(filter => filter.Key == FilterKeys.Legendary).SelectedKey ?? "";
var source = Filters.SingleOrDefault(filter => filter.Key == FilterKeys.Source).SelectedKey ?? "";
token.ThrowIfCancellationRequested();
return items.Where(item =>
{ {
var monster = item as Monster; var monster = item as Monster;
return monster.Type.Contains(type) && return monster.Type.Contains(type) &&
@ -365,8 +380,10 @@ namespace AideDeJeu.ViewModels
powerComparer.Compare(monster.Challenge, maxPower) <= 0 && powerComparer.Compare(monster.Challenge, maxPower) <= 0 &&
monster.NamePHB.ToLower().Contains(SearchText.ToLower()); monster.NamePHB.ToLower().Contains(SearchText.ToLower());
}) })
.OrderBy(monster => monster.NamePHB) .OrderBy(monster => monster.NamePHB)
.ToList(); .AsEnumerable();
}, token);
} }
public abstract List<KeyValuePair<string, string>> Categories { get; } public abstract List<KeyValuePair<string, string>> Categories { get; }

View file

@ -8,15 +8,18 @@ using System.Diagnostics;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Input; using System.Windows.Input;
using Xamarin.Forms; using Xamarin.Forms;
using System.Threading;
namespace AideDeJeu.ViewModels namespace AideDeJeu.ViewModels
{ {
public abstract class ItemsViewModel : BaseViewModel public abstract class ItemsViewModel : BaseViewModel
{ {
CancellationTokenSource cancellationTokenSource;
public ItemsViewModel(ItemSourceType itemSourceType) public ItemsViewModel(ItemSourceType itemSourceType)
{ {
this.ItemSourceType = itemSourceType; this.ItemSourceType = itemSourceType;
LoadItemsCommand = new Command(() => ExecuteLoadItemsCommand()); LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommandAsync());
} }
public ICommand LoadItemsCommand { get; protected set; } public ICommand LoadItemsCommand { get; protected set; }
//public abstract void ExecuteLoadItemsCommand(); //public abstract void ExecuteLoadItemsCommand();
@ -66,26 +69,28 @@ namespace AideDeJeu.ViewModels
} }
} }
public void ExecuteLoadItemsCommand() async Task LoadItemsAsync(CancellationToken token = default)
{ {
if (IsBusy)
return;
IsBusy = true; IsBusy = true;
Main.IsLoading = true;
try try
{ {
Main.Items.Clear();
var filterViewModel = Main.GetFilterViewModel(ItemSourceType); var filterViewModel = Main.GetFilterViewModel(ItemSourceType);
var items = filterViewModel.FilterItems(AllItems); var items = await filterViewModel.FilterItems(AllItems, token);
await Task.Run(() =>
foreach (var item in items)
{ {
Main.Items.Add(item); Main.Items.Clear();
} foreach (var item in items)
{
token.ThrowIfCancellationRequested();
Main.Items.Add(item);
}
});
//On arrete le loading ici car on annule toujours avant de lancer une nouvelle opération
Main.IsLoading = false;
} }
catch (Exception ex) catch (OperationCanceledException ex)
{ {
Debug.WriteLine(ex); Debug.WriteLine(ex);
} }
@ -95,6 +100,14 @@ namespace AideDeJeu.ViewModels
} }
} }
public async Task ExecuteLoadItemsCommandAsync()
{
if (cancellationTokenSource != null)
{
cancellationTokenSource.Cancel();
}
cancellationTokenSource = new CancellationTokenSource();
await LoadItemsAsync(cancellationTokenSource.Token);
}
} }
} }

View file

@ -52,6 +52,13 @@ namespace AideDeJeu.ViewModels
} }
} }
private bool _isLoading;
public bool IsLoading
{
get => _isLoading;
set => SetProperty(ref _isLoading, value);
}
public Dictionary<ItemSourceType, Lazy<ItemsViewModel>> AllItemsViewModel = new Dictionary<ItemSourceType, Lazy<ItemsViewModel>>() public Dictionary<ItemSourceType, Lazy<ItemsViewModel>> AllItemsViewModel = new Dictionary<ItemSourceType, Lazy<ItemsViewModel>>()
{ {
{ ItemSourceType.SpellVF, new Lazy<ItemsViewModel>(() => new SpellsViewModel(ItemSourceType.SpellVF)) }, { ItemSourceType.SpellVF, new Lazy<ItemsViewModel>(() => new SpellsViewModel(ItemSourceType.SpellVF)) },
@ -116,21 +123,21 @@ namespace AideDeJeu.ViewModels
public MainViewModel() public MainViewModel()
{ {
LoadItemsCommand = new Command(() => LoadItemsCommand = new Command(async () =>
{ {
GetItemsViewModel(ItemSourceType).ExecuteLoadItemsCommand(); await GetItemsViewModel(ItemSourceType).ExecuteLoadItemsCommandAsync();
}); });
GotoItemCommand = new Command<Item>(async (item) => await GetItemsViewModel(ItemSourceType).ExecuteGotoItemCommandAsync(item)); GotoItemCommand = new Command<Item>(async (item) => await GetItemsViewModel(ItemSourceType).ExecuteGotoItemCommandAsync(item));
SwitchToSpells = new Command(() => ItemSourceType = (ItemSourceType & ~ ItemSourceType.Monster) | ItemSourceType.Spell); SwitchToSpells = new Command(() => ItemSourceType = (ItemSourceType & ~ItemSourceType.Monster) | ItemSourceType.Spell);
SwitchToMonsters = new Command(() => ItemSourceType = (ItemSourceType & ~ItemSourceType.Spell) | ItemSourceType.Monster); SwitchToMonsters = new Command(() => ItemSourceType = (ItemSourceType & ~ItemSourceType.Spell) | ItemSourceType.Monster);
SwitchToVF = new Command(() => ItemSourceType = (ItemSourceType & ~ItemSourceType.VO & ~ItemSourceType.HD) | ItemSourceType.VF); SwitchToVF = new Command(() => ItemSourceType = (ItemSourceType & ~ItemSourceType.VO & ~ItemSourceType.HD) | ItemSourceType.VF);
SwitchToVO = new Command(() => ItemSourceType = (ItemSourceType & ~ItemSourceType.VF & ~ItemSourceType.HD) | ItemSourceType.VO); SwitchToVO = new Command(() => ItemSourceType = (ItemSourceType & ~ItemSourceType.VF & ~ItemSourceType.HD) | ItemSourceType.VO);
SwitchToHD = new Command(() => ItemSourceType = (ItemSourceType & ~ItemSourceType.VF & ~ItemSourceType.VO) | ItemSourceType.HD); SwitchToHD = new Command(() => ItemSourceType = (ItemSourceType & ~ItemSourceType.VF & ~ItemSourceType.VO) | ItemSourceType.HD);
AboutCommand = new Command(async() => await Main.Navigator.GotoAboutPageAsync()); AboutCommand = new Command(async () => await Main.Navigator.GotoAboutPageAsync());
SearchCommand = new Command<string>((text) => SearchCommand = new Command<string>(async (text) =>
{ {
GetFilterViewModel(ItemSourceType).SearchText = text; GetFilterViewModel(ItemSourceType).SearchText = text;
GetItemsViewModel(ItemSourceType).ExecuteLoadItemsCommand(); await GetItemsViewModel(ItemSourceType).ExecuteLoadItemsCommandAsync();
}); });
} }
} }

View file

@ -1,45 +1,30 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8"?>
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms" <MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:AideDeJeu.Views" xmlns:tools="clr-namespace:AideDeJeu.Tools" x:Class="AideDeJeu.Views.MainPage" x:Name="This" IsPresented="False" Title="">
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:AideDeJeu.Views"
xmlns:tools="clr-namespace:AideDeJeu.Tools"
x:Class="AideDeJeu.Views.MainPage"
x:Name="This"
IsPresented="False"
Title="">
<MasterDetailPage.Resources> <MasterDetailPage.Resources>
<ResourceDictionary> <ResourceDictionary>
<tools:ItemSourceTypeToStringConverter <tools:ItemSourceTypeToStringConverter x:Key="ItemSourceTypeToTitleConverter" SpellVF="Sorts" SpellVO="Spells" SpellHD="Sorts (H&amp;D)" MonsterVF="Monstres" MonsterVO="Monsters" MonsterHD="Créatures (H&amp;D)" />
x:Key="ItemSourceTypeToTitleConverter" <tools:ItemSourceTypeToItemsConverter x:Key="ItemSourceTypeToItemsConverter" />
SpellVF="Sorts" <tools:ItemSourceTypeToFilterConverter x:Key="ItemSourceTypeToFilterConverter" />
SpellVO="Spells"
SpellHD="Sorts (H&amp;D)"
MonsterVF="Monstres"
MonsterVO="Monsters"
MonsterHD="Créatures (H&amp;D)"
/>
<tools:ItemSourceTypeToItemsConverter
x:Key="ItemSourceTypeToItemsConverter" />
<tools:ItemSourceTypeToFilterConverter
x:Key="ItemSourceTypeToFilterConverter" />
</ResourceDictionary> </ResourceDictionary>
</MasterDetailPage.Resources> </MasterDetailPage.Resources>
<MasterDetailPage.Master> <MasterDetailPage.Master>
<ContentPage Title=" "> <ContentPage Title=" ">
<ListView ItemsSource="{Binding ItemSourceType, Converter={StaticResource ItemSourceTypeToFilterConverter}}" HasUnevenRows="True" RowHeight="-1" SeparatorVisibility="None" IsPullToRefreshEnabled="False" > <AbsoluteLayout>
<ListView.ItemTemplate> <ListView ItemsSource="{Binding ItemSourceType, Converter={StaticResource ItemSourceTypeToFilterConverter}}" HasUnevenRows="True" RowHeight="-1" SeparatorVisibility="None" IsPullToRefreshEnabled="False">
<DataTemplate> <ListView.ItemTemplate>
<ViewCell> <DataTemplate>
<ViewCell.View> <ViewCell>
<StackLayout Margin="10,5,10,0" Padding="0" Spacing="0"> <ViewCell.View>
<Label BindingContext="{Binding}" Text="{Binding Name}" Style="{StaticResource Key=subsubsection}" /> <StackLayout Margin="10,5,10,0" Padding="0" Spacing="0">
<Picker HorizontalOptions="FillAndExpand" ItemsSource="{Binding KeyValues, Mode=OneWay}" ItemDisplayBinding="{Binding Value, Mode=OneWay}" SelectedIndex="{Binding Index}" /> <Label BindingContext="{Binding}" Text="{Binding Name}" Style="{StaticResource Key=subsubsection}" />
</StackLayout> <Picker HorizontalOptions="FillAndExpand" ItemsSource="{Binding KeyValues, Mode=OneWay}" ItemDisplayBinding="{Binding Value, Mode=OneWay}" SelectedIndex="{Binding Index}" />
</ViewCell.View> </StackLayout>
</ViewCell> </ViewCell.View>
</DataTemplate> </ViewCell>
</ListView.ItemTemplate> </DataTemplate>
</ListView> </ListView.ItemTemplate>
</ListView>
</AbsoluteLayout>
</ContentPage> </ContentPage>
</MasterDetailPage.Master> </MasterDetailPage.Master>
<MasterDetailPage.Detail> <MasterDetailPage.Detail>
@ -54,40 +39,29 @@
<ToolbarItem Name="VO" Text="VO AideDD/SRD" Order="Secondary" Command="{Binding SwitchToVO}" /> <ToolbarItem Name="VO" Text="VO AideDD/SRD" Order="Secondary" Command="{Binding SwitchToVO}" />
<ToolbarItem Name="HD" Text="VF H&amp;D" Order="Secondary" Command="{Binding SwitchToHD}" /> <ToolbarItem Name="HD" Text="VF H&amp;D" Order="Secondary" Command="{Binding SwitchToHD}" />
</ContentPage.ToolbarItems> </ContentPage.ToolbarItems>
<StackLayout Orientation="Vertical"> <AbsoluteLayout>
<SearchBar x:Name="SearchBar" SearchCommand="{Binding SearchCommand}" SearchCommandParameter="{Binding Text, Source={x:Reference SearchBar}}"> <StackLayout Orientation="Vertical">
<SearchBar.Behaviors> <SearchBar x:Name="SearchBar" SearchCommand="{Binding SearchCommand}" SearchCommandParameter="{Binding Text, Source={x:Reference SearchBar}}">
<tools:TextChangedBehavior /> <SearchBar.Behaviors>
</SearchBar.Behaviors> <tools:TextChangedBehavior />
</SearchBar> </SearchBar.Behaviors>
</SearchBar>
<ListView <ListView x:Name="ItemsListView" ItemsSource="{Binding Items}" VerticalOptions="FillAndExpand" HasUnevenRows="true" IsRefreshing="{Binding IsBusy, Mode=OneWay}" CachingStrategy="RecycleElement" SelectedItem="{Binding SelectedItem}" ItemTapped="ItemsListView_ItemTapped">
x:Name="ItemsListView" <ListView.ItemTemplate>
ItemsSource="{Binding Items}" <DataTemplate>
VerticalOptions="FillAndExpand" <ViewCell>
HasUnevenRows="true" <StackLayout Padding="10">
IsRefreshing="{Binding IsBusy, Mode=OneWay}" <Label Text="{Binding NamePHB}" LineBreakMode="NoWrap" Style="{DynamicResource subsubsection}" FontSize="16" />
CachingStrategy="RecycleElement" </StackLayout>
SelectedItem="{Binding SelectedItem}" </ViewCell>
ItemTapped="ItemsListView_ItemTapped"> </DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemTemplate> </ListView>
<DataTemplate> </StackLayout>
<ViewCell> <ActivityIndicator AbsoluteLayout.LayoutBounds="0.5,0.5" AbsoluteLayout.LayoutFlags="XProportional, YProportional" IsRunning="{x:Binding IsLoading}" Color="Olive" />
<StackLayout Padding="10"> </AbsoluteLayout>
<Label
Text="{Binding NamePHB}"
LineBreakMode="NoWrap"
Style="{DynamicResource subsubsection}"
FontSize="16" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage> </ContentPage>
</x:Arguments> </x:Arguments>
</NavigationPage> </NavigationPage>
</MasterDetailPage.Detail> </MasterDetailPage.Detail>
</MasterDetailPage> </MasterDetailPage>