提交 7f3c0a2e 编写于 作者: A Anran Zhang

调整视频视图的逻辑结构,替换横幅图片控件

上级 9368baef
......@@ -5,7 +5,6 @@ using Windows.System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
namespace Richasy.Bili.App.Controls
{
......@@ -18,7 +17,7 @@ namespace Richasy.Bili.App.Controls
/// <see cref="Source"/>的依赖属性.
/// </summary>
public static readonly DependencyProperty SourceProperty =
DependencyProperty.Register(nameof(Source), typeof(ImageSource), typeof(BannerItem), new PropertyMetadata(null));
DependencyProperty.Register(nameof(Source), typeof(object), typeof(BannerItem), new PropertyMetadata(null));
/// <summary>
/// <see cref="Uri"/>的依赖属性.
......@@ -43,9 +42,9 @@ namespace Richasy.Bili.App.Controls
/// <summary>
/// 图片源.
/// </summary>
public ImageSource Source
public object Source
{
get { return (ImageSource)GetValue(SourceProperty); }
get { return GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
......@@ -85,6 +84,23 @@ namespace Richasy.Bili.App.Controls
/// <inheritdoc/>
protected override void OnPointerReleased(PointerRoutedEventArgs e)
{
BackToNormal(e);
}
/// <inheritdoc/>
protected override void OnPointerCanceled(PointerRoutedEventArgs e)
{
BackToNormal(e);
}
/// <inheritdoc/>
protected override void OnPointerCaptureLost(PointerRoutedEventArgs e)
{
BackToNormal(e);
}
private void BackToNormal(PointerRoutedEventArgs e)
{
VisualStateManager.GoToState(this, "NormalState", true);
this.ReleasePointerCapture(e.Pointer);
......
......@@ -50,6 +50,12 @@ namespace Richasy.Bili.App.Controls
public static readonly DependencyProperty AdditionalContentProperty =
DependencyProperty.Register(nameof(AdditionalContent), typeof(object), typeof(VideoView), new PropertyMetadata(null));
/// <summary>
/// <see cref="IsAutoFillEnable"/>的依赖属性.
/// </summary>
public static readonly DependencyProperty IsAutoFillEnableProperty =
DependencyProperty.Register(nameof(IsAutoFillEnable), typeof(bool), typeof(VideoView), new PropertyMetadata(true));
private ScrollViewer _parentScrollViewer;
/// <summary>
......@@ -67,12 +73,6 @@ namespace Richasy.Bili.App.Controls
/// </summary>
public event EventHandler RequestLoadMore;
/// <summary>
/// 当有新的条目添加并准备好与用户交互时发生.
/// 会返回新条目的索引值与其实际高度的乘积,用以估算列表总高度.
/// </summary>
public event EventHandler<Microsoft.UI.Xaml.Controls.ItemsRepeaterElementPreparedEventArgs> NewItemAdded;
/// <summary>
/// 条目模板.
/// </summary>
......@@ -127,6 +127,15 @@ namespace Richasy.Bili.App.Controls
set { SetValue(AdditionalContentProperty, value); }
}
/// <summary>
/// 是否允许根据容器剩余空间自行计算视频条目容量,并主动发起请求填满整个容器.
/// </summary>
public bool IsAutoFillEnable
{
get { return (bool)GetValue(IsAutoFillEnableProperty); }
set { SetValue(IsAutoFillEnableProperty, value); }
}
private static void OnOrientationChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var instance = d as VideoView;
......@@ -208,7 +217,28 @@ namespace Richasy.Bili.App.Controls
if (args.Element != null && args.Element is VideoItem item)
{
item.Orientation = ItemOrientation;
NewItemAdded?.Invoke(this, args);
if (IsAutoFillEnable &&
ItemsSource is ObservableCollection<VideoViewModel> collectionSource &&
_parentScrollViewer != null &&
args.Index >= collectionSource.Count - 1)
{
var size = item.GetHolderSize();
var isNeedLoadMore = false;
if (double.IsInfinity(size.Width))
{
isNeedLoadMore = (args.Index + 1) * size.Height <= _parentScrollViewer.ViewportHeight;
}
else
{
var rowCount = args.Index / (_parentScrollViewer.ViewportWidth / size.Width);
isNeedLoadMore = rowCount * size.Height <= _parentScrollViewer.ViewportHeight;
}
if (isNeedLoadMore)
{
RequestLoadMore?.Invoke(this, EventArgs.Empty);
}
}
}
}
}
......
......@@ -5,6 +5,7 @@
xmlns:controls="using:Richasy.Bili.App.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:enums="using:Richasy.Bili.Models.Enums"
xmlns:icons="using:Fluent.Icons"
xmlns:loc="using:Richasy.Bili.Locator.Uwp"
xmlns:local="using:Richasy.Bili.App.Pages.Overlay"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
......@@ -91,7 +92,8 @@
x:Name="BannerView"
Margin="0,0,0,12"
VerticalAlignment="Top"
ItemsSource="{x:Bind ViewModel.CurrentSelectedSubPartition.BannerCollection, Mode=OneWay}" />
ItemsSource="{x:Bind ViewModel.CurrentSelectedSubPartition.BannerCollection, Mode=OneWay}"
Visibility="{x:Bind ViewModel.CurrentSelectedSubPartition.IsShowBanner, Mode=OneWay}" />
<controls:VideoView
x:Name="VideoView"
......@@ -101,19 +103,30 @@
ItemsSource="{x:Bind ViewModel.CurrentSelectedSubPartition.VideoCollection, Mode=OneWay}"
RequestLoadMore="OnVideoViewRequestLoadMoreAsync">
<controls:VideoView.AdditionalContent>
<ComboBox
x:Name="VideoSortComboBox"
Style="{StaticResource DefaultComboBoxStyle}"
ItemsSource="{x:Bind ViewModel.CurrentSelectedSubPartition.SortTypeCollection, Mode=OneWay}"
PlaceholderText="{loc:LocaleLocator Name=SelectSortType}"
SelectionChanged="OnVideoSortComboBoxSlectionChanged"
Visibility="Collapsed">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="enums:VideoSortType">
<TextBlock Text="{x:Bind Converter={StaticResource SortTypeTextConverter}}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Grid>
<Button
x:Name="RefreshButton"
VerticalAlignment="Center"
Click="OnRefreshButtonClickAsync">
<StackPanel Orientation="Horizontal" Spacing="8">
<icons:FluentIconElement VerticalAlignment="Center" Symbol="ArrowSyncCircle16" />
<TextBlock Text="{loc:LocaleLocator Name=Refresh}" />
</StackPanel>
</Button>
<ComboBox
x:Name="VideoSortComboBox"
Style="{StaticResource DefaultComboBoxStyle}"
ItemsSource="{x:Bind ViewModel.CurrentSelectedSubPartition.SortTypeCollection, Mode=OneWay}"
PlaceholderText="{loc:LocaleLocator Name=SelectSortType}"
SelectionChanged="OnVideoSortComboBoxSlectionChanged"
Visibility="Collapsed">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="enums:VideoSortType">
<TextBlock Text="{x:Bind Converter={StaticResource SortTypeTextConverter}}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
</controls:VideoView.AdditionalContent>
<controls:VideoView.ItemTemplate>
<DataTemplate x:DataType="uwp:VideoViewModel">
......
// Copyright (c) Richasy. All rights reserved.
using System.ComponentModel;
using Richasy.Bili.App.Controls;
using Richasy.Bili.Models.Enums;
using Richasy.Bili.ViewModels.Uwp;
using Windows.UI.Xaml;
......@@ -68,13 +67,11 @@ namespace Richasy.Bili.App.Pages.Overlay
private void OnUnloaded(object sender, RoutedEventArgs e)
{
ViewModel.PropertyChanged -= OnViewModelPropertyChanged;
VideoView.NewItemAdded -= OnVideoViewNewItemAddedAsync;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
ViewModel.PropertyChanged += OnViewModelPropertyChanged;
VideoView.NewItemAdded += OnVideoViewNewItemAddedAsync;
CheckCurrentSubPartition();
}
......@@ -97,15 +94,16 @@ namespace Richasy.Bili.App.Pages.Overlay
var vm = ViewModel.CurrentSelectedSubPartition;
if (vm != null)
{
BannerView.Visibility = vm.BannerCollection != null && vm.BannerCollection.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
var isShowSort = vm.SortTypeCollection != null && vm.SortTypeCollection.Count > 0;
if (isShowSort)
{
VideoSortComboBox.Visibility = Visibility.Visible;
RefreshButton.Visibility = Visibility.Collapsed;
VideoSortComboBox.SelectedItem = vm.CurrentSortType;
}
else
{
RefreshButton.Visibility = Visibility.Visible;
VideoSortComboBox.Visibility = Visibility.Collapsed;
}
......@@ -118,41 +116,12 @@ namespace Richasy.Bili.App.Pages.Overlay
private async void OnVideoViewRequestLoadMoreAsync(object sender, System.EventArgs e)
{
var currentSubPartition = ViewModel.CurrentSelectedSubPartition;
if (currentSubPartition != null && !currentSubPartition.IsDeltaLoading && !currentSubPartition.IsInitializeLoading)
{
await currentSubPartition.RequestDataAsync();
}
}
private async void OnVideoViewNewItemAddedAsync(object sender, Microsoft.UI.Xaml.Controls.ItemsRepeaterElementPreparedEventArgs e)
{
// 当视频条目列表加载完成之后计算这些视频条目是否足以填满整个显示区域,
// 如果不足,则再次请求,直到填满显示区域.
// 此法是滚动加载设计的前置条件,即先让显示区域能够滚动.
var currentSubPartition = ViewModel.CurrentSelectedSubPartition;
if (currentSubPartition != null &&
!currentSubPartition.IsDeltaLoading &&
!currentSubPartition.IsInitializeLoading &&
e.Index >= currentSubPartition.VideoCollection.Count - 1)
!currentSubPartition.IsInitializeLoading)
{
var videoItem = e.Element as VideoItem;
var size = videoItem.GetHolderSize();
var isNeedLoadMore = false;
if (double.IsInfinity(size.Width))
{
isNeedLoadMore = (e.Index + 1) * size.Height <= ContentScrollView.ViewportHeight;
}
else
{
var rowCount = e.Index / (ContentScrollView.ViewportWidth / size.Width);
isNeedLoadMore = rowCount * size.Height <= ContentScrollView.ViewportHeight;
}
if (isNeedLoadMore)
{
await ViewModel.CurrentSelectedSubPartition.RequestDataAsync();
}
await currentSubPartition.RequestDataAsync();
}
}
......@@ -164,5 +133,15 @@ namespace Richasy.Bili.App.Pages.Overlay
ViewModel.CurrentSelectedSubPartition.CurrentSortType = item;
}
}
private async void OnRefreshButtonClickAsync(object sender, RoutedEventArgs e)
{
if (ViewModel.CurrentSelectedSubPartition != null &&
!ViewModel.CurrentSelectedSubPartition.IsInitializeLoading &&
!ViewModel.CurrentSelectedSubPartition.IsDeltaLoading)
{
await ViewModel.CurrentSelectedSubPartition.InitializeRequestAsync();
}
}
}
}
......@@ -2,7 +2,10 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Richasy.Bili.App.Controls"
xmlns:local="using:Richasy.Bili.App">
xmlns:hn="using:HN.Controls"
xmlns:icons="using:Fluent.Icons"
xmlns:local="using:Richasy.Bili.App"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls">
<Style TargetType="controls:BannerItem">
<Setter Property="Template">
......@@ -28,13 +31,40 @@
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Image
<hn:ImageEx
x:Name="BannerImage"
AutomationProperties.Name="{TemplateBinding Title}"
MinWidth="300"
MinHeight="100"
MaxHeight="114"
LazyLoadingEnabled="True"
RetryCount="2"
RetryDelay="0:0:5"
Source="{TemplateBinding Source}"
Stretch="Uniform"
ToolTipService.ToolTip="{TemplateBinding Title}" />
ToolTipService.ToolTip="{TemplateBinding Title}">
<hn:ImageEx.LoadingTemplate>
<DataTemplate>
<Grid>
<muxc:ProgressRing
Style="{StaticResource PageProgressRingStyle}"
Width="28"
Height="28" />
</Grid>
</DataTemplate>
</hn:ImageEx.LoadingTemplate>
<hn:ImageEx.FailedTemplate>
<DataTemplate>
<Grid Background="{ThemeResource SystemControlForegroundBaseMediumBrush}" Opacity="0.5">
<icons:FluentIconElement
HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="{ThemeResource SystemControlForegroundBaseMediumBrush}"
Symbol="Image28Filled" />
</Grid>
</DataTemplate>
</hn:ImageEx.FailedTemplate>
</hn:ImageEx>
</Grid>
</ControlTemplate>
</Setter.Value>
......
......@@ -34,6 +34,18 @@ namespace Richasy.Bili.ViewModels.Uwp
/// </summary>
public bool IsRequested => _offsetId != 0 || _pageNumber > 1;
/// <summary>
/// 是否显示横幅.
/// </summary>
[Reactive]
public bool IsShowBanner { get; set; }
/// <summary>
/// 是否显示顶部标签组.
/// </summary>
[Reactive]
public bool IsShowTags { get; set; }
/// <summary>
/// 子分区名称.
/// </summary>
......
......@@ -88,13 +88,14 @@ namespace Richasy.Bili.ViewModels.Uwp
/// 执行初始请求.
/// </summary>
/// <returns><see cref="Task"/>.</returns>
internal async Task InitializeRequestAsync()
public async Task InitializeRequestAsync()
{
if (!IsInitializeLoading && !IsDeltaLoading)
{
IsInitializeLoading = true;
VideoCollection.Clear();
_offsetId = 0;
_pageNumber = 1;
_lastRequestTime = DateTimeOffset.MinValue;
await _controller.RequestSubPartitionDataAsync(SubPartitionId, _isRecommendPartition, 0, CurrentSortType, _pageNumber);
IsInitializeLoading = false;
......@@ -140,13 +141,15 @@ namespace Richasy.Bili.ViewModels.Uwp
{
if (e.SubPartitionId == SubPartitionId)
{
if (e.BannerList?.Any() ?? false)
IsShowBanner = e.BannerList?.Any() ?? false;
IsShowTags = e.TagList?.Any() ?? false;
if (IsShowBanner)
{
BannerCollection.Clear();
e.BannerList.ToList().ForEach(p => BannerCollection.Add(p));
}
if (e.TagList?.Any() ?? false)
if (IsShowTags)
{
TagCollection.Clear();
e.TagList.ToList().ForEach(p => TagCollection.Add(p));
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册