我正在尝试弄清楚如何在动画框架中正确显示 CollectionView 元素。
我给你看一些图片来理解面糊: 起始位置: 在这里我只想看到最上面的图片。这是侧栏关闭的地方,,。当我按下按钮时,我希望看到这样的内容: 正如你所看到的,我只能看到 CollectionView 中只有 3 个元素,但应该有 19 个元素。如图所示,元素未正确显示。我仍在学习 UI 部分,所以欢迎任何帮助。
这就是我到目前为止所做的:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
xmlns:converters="clr-namespace:Solution.MobileApp.Converters"
xmlns:viewModels="clr-namespace:Solution.MobileApp.ViewModels"
xmlns:component="clr-namespace:Solution.MobileApp.Components"
xmlns:models="clr-namespace:Solution.MobileApp.Models"
x:Class="Solution.MobileApp.Pages.Tabs.PlayerPage"
x:DataType="viewModels:PlayerPageViewModel"
Title="wPlayer"
BackgroundColor="Black"
Shell.NavBarIsVisible="False"
Unloaded="OnUnloaded">
<ContentPage.Resources>
<toolkit:TimeSpanToSecondsConverter x:Key="TimeSpanConverter" />
<converters:SecondsToStringConverter x:Key="SecondsToStringConverter" />
</ContentPage.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="5*"/>
</Grid.ColumnDefinitions>
<Frame Grid.Row="0" Grid.RowSpan="5" Grid.Column="0"
ZIndex="100"
Padding="5,5,5,5"
Margin="5,5,5,5"
x:Name="trackListSideBar"
HeightRequest="50" WidthRequest="50">
<StackLayout Orientation="Vertical" HorizontalOptions="FillAndExpand">
<ImageButton Source="song.png"
WidthRequest="40" HeightRequest="40"
Clicked="OnTrackListImageClicked"
VerticalOptions="Start"
HorizontalOptions="End"/>
<CollectionView x:Name="collectionView"
ItemsSource="{Binding Playlist}"
BackgroundColor="White"
VerticalOptions="Start"
HorizontalOptions="End">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical" ItemSpacing="5"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="models:TrackModel">
<component:TrackListItemComponent Track="{Binding .}" />
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</Frame>
<toolkit:MediaElement
Grid.Row="0"
Grid.Column="1"
x:Name="mediaElement"
ShouldAutoPlay="True"
Source="{Binding Source}"
MediaEnded="OnMediaEnded"
MediaFailed="OnMediaFailed"
MediaOpened="OnMediaOpened"
PositionChanged="OnPositionChanged"
StateChanged="OnStateChanged"
SeekCompleted="OnSeekCompleted" />
<HorizontalStackLayout Grid.Row="1" Grid.Column="1" Padding="0,0,0,15">
<Label HorizontalOptions="Center">
<Label.Text>
<MultiBinding StringFormat="Current State: {0}">
<Binding Path="CurrentState" Source="{x:Reference mediaElement}" />
</MultiBinding>
</Label.Text>
</Label>
</HorizontalStackLayout>
<Grid Grid.Row="2" Grid.Column="1" Padding="0,10,0,10" ColumnDefinitions="*,*,*,*" ColumnSpacing="5">
<ImageButton Grid.Column="0" Source="back.png" Clicked="OnPreviousClicked" HeightRequest="50" WidthRequest="50" />
<ImageButton Grid.Column="1" Source="play.png" Clicked="OnPlayOrPauseClicked" HeightRequest="50" WidthRequest="50">
<ImageButton.Triggers>
<DataTrigger TargetType="ImageButton"
Binding="{Binding CurrentState, Source={x:Reference mediaElement}}"
Value="Playing">
<Setter Property="Source" Value="pause.png" />
</DataTrigger>
<DataTrigger TargetType="ImageButton"
Binding="{Binding CurrentState, Source={x:Reference mediaElement}}"
Value="Paused">
<Setter Property="Source" Value="play.png" />
</DataTrigger>
<DataTrigger TargetType="ImageButton"
Binding="{Binding CurrentState, Source={x:Reference mediaElement}}"
Value="None">
<Setter Property="Source" Value="play.png" />
</DataTrigger>
</ImageButton.Triggers>
</ImageButton>
<ImageButton Grid.Column="2" Source="stop.png" Clicked="OnStopClicked" HeightRequest="50" WidthRequest="50" />
<ImageButton Grid.Column="3" Source="forward.png" Clicked="OnNextClicked" HeightRequest="50" WidthRequest="50" />
</Grid>
<VerticalStackLayout Grid.Row="3" Grid.Column="1" Padding="0,10,0,10">
<Slider x:Name="positionSlider"
MinimumTrackColor="Gray"
DragStarted="OnSliderDragStarted"
DragCompleted="OnSliderDragCompleted"/>
<HorizontalStackLayout Padding="0,10,0,10">
<Label HorizontalOptions="Center">
<Label.Text>
<MultiBinding StringFormat="{}Position: {0}/{1}">
<Binding Path="Position" Source="{x:Reference mediaElement}" Converter="{StaticResource SecondsToStringConverter}" />
<Binding Path="Duration" Source="{x:Reference mediaElement}" Converter="{StaticResource SecondsToStringConverter}" />
</MultiBinding>
</Label.Text>
</Label>
</HorizontalStackLayout>
</VerticalStackLayout>
<Grid Grid.Row="4" Grid.Column="1" Padding="0,10,0,10" RowDefinitions="*,*" ColumnDefinitions="*,*" ColumnSpacing="5">
<Label Grid.Column="0" Grid.Row="0">
<Label.FormattedText>
<FormattedString>
<Span Text="Volume:" />
<Span Text="{Binding Source={x:Reference mediaElement}, Path=Volume, StringFormat='{}{0:P0}'}" />
</FormattedString>
</Label.FormattedText>
</Label>
<Slider Grid.Column="0" Grid.Row="1"
Maximum="1.0"
Minimum="0.0"
MinimumTrackColor="Red"
MaximumTrackColor="Gray"
Margin="10,0,10,0">
<Slider.Value>
<Binding Path="Volume" Source="{x:Reference mediaElement}" />
</Slider.Value>
</Slider>
<ImageButton Grid.Column="1" Grid.Row="0" Grid.RowSpan="2"
HeightRequest="50" WidthRequest="50"
Source="mute.png"
Clicked="OnMuteClicked">
<ImageButton.Triggers>
<DataTrigger TargetType="ImageButton"
Binding="{Binding ShouldMute, Source={x:Reference mediaElement}}"
Value="True">
<Setter Property="Source" Value="sound.png" />
</DataTrigger>
<DataTrigger TargetType="ImageButton"
Binding="{Binding ShouldMute, Source={x:Reference mediaElement}}"
Value="False">
<Setter Property="Source" Value="mute.png" />
</DataTrigger>
</ImageButton.Triggers>
</ImageButton>
</Grid>
</Grid>
</ContentPage>
namespace Solution.MobileApp.Pages.Tabs;
public partial class PlayerPage : ContentPage
{
public PlayerPageViewModel ViewModel => BindingContext as PlayerPageViewModel;
private bool isSideBarOpen = false;
public PlayerPage(PlayerPageViewModel viewModel)
{
BindingContext = viewModel;
InitializeComponent();
mediaElement.PropertyChanged += OnPropertyChanged;
}
private void OnPreviousClicked(object sender, EventArgs e) => mediaElement.Source = ViewModel.PreviousTrack();
private void OnNextClicked(object sender, EventArgs e) => mediaElement.Source = ViewModel.NextTrack();
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == MediaElement.DurationProperty.PropertyName)
{
positionSlider.Maximum = mediaElement.Duration.TotalSeconds;
}
}
private void OnMediaOpened(object sender, EventArgs e)
{ }
private void OnStateChanged(object sender, MediaStateChangedEventArgs e)
{ }
private void OnMediaFailed(object sender, MediaFailedEventArgs e) => mediaElement.Source = ViewModel.PreviousTrack();
private void OnMediaEnded(object sender, EventArgs e) => mediaElement.Source = ViewModel.PreviousTrack();
private void OnPositionChanged(object sender, MediaPositionChangedEventArgs e)
{
positionSlider.Value = e.Position.TotalSeconds;
}
private void OnSeekCompleted(object sender, EventArgs e)
{ }
private void OnPlayOrPauseClicked(object sender, EventArgs e)
{
switch(mediaElement.CurrentState)
{
case MediaElementState.None:
mediaElement.Source = ViewModel.PlayTrack();
break;
case MediaElementState.Playing:
mediaElement.Pause();
break;
case MediaElementState.Paused:
mediaElement.Play();
break;
default:
mediaElement.Stop();
break;
}
}
private void OnStopClicked(object sender, EventArgs e)
{
mediaElement.Stop();
}
private void OnMuteClicked(object sender, EventArgs e)
{
mediaElement.ShouldMute = !mediaElement.ShouldMute;
}
private void OnUnloaded(object sender, EventArgs e)
{
// Stop and cleanup MediaElement when we navigate away
mediaElement.Handler?.DisconnectHandler();
}
private void OnSliderDragCompleted(object sender, EventArgs e)
{
ArgumentNullException.ThrowIfNull(sender);
var newValue = ((Slider)sender).Value;
mediaElement.SeekTo(TimeSpan.FromSeconds(newValue));
mediaElement.Play();
}
private void OnSliderDragStarted(object sender, EventArgs e)
{
mediaElement.Pause();
}
private async void OnTrackListImageClicked(object sender, EventArgs e)
{
var from = isSideBarOpen ? DeviceDisplay.MainDisplayInfo.Width * 0.65 : 50;
var to = !isSideBarOpen ? DeviceDisplay.MainDisplayInfo.Width * 0.65 : 50;
await MainThread.InvokeOnMainThreadAsync(() =>
{
var animation = new Animation(x => trackListSideBar.WidthRequest = x, from, to);
animation.Commit(this, "TrackListSideBarAnimation", 16, 500, Easing.Linear);
});
isSideBarOpen = !isSideBarOpen;
}
}
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
xmlns:converters="clr-namespace:Solution.MobileApp.Converters"
xmlns:viewModel="clr-namespace:Solution.MobileApp.Models"
x:Class="Solution.MobileApp.Components.TrackListItemComponent"
x:Name="this">
<ContentView.BindingContext>
<viewModel:TrackModel />
</ContentView.BindingContext>
<ContentView.Resources>
<toolkit:TimeSpanToSecondsConverter x:Key="TimeSpanConverter" />
<converters:SecondsToStringConverter x:Key="SecondsToStringConverter" />
</ContentView.Resources>
<SwipeView x:Name="swipe">
<SwipeView.RightItems>
<SwipeItems>
<SwipeItem IconImageSource="delete.png"
BackgroundColor="Red"
Invoked="OnDelete" />
</SwipeItems>
</SwipeView.RightItems>
<Frame BindingContext="{x:Reference this}"
Margin="2"
BackgroundColor="#101010"
BorderColor="#101010">
<Frame.GestureRecognizers>
<TapGestureRecognizer Tapped="OnTapp"/>
</Frame.GestureRecognizers>
<Grid ColumnDefinitions="5*,1*">
<Label Text="{Binding Track.Title}" TextColor="White" Grid.Column="0"/>
<Label Text="{Binding Track.Length, Converter={StaticResource SecondsToStringConverter}}"
TextColor="WhiteSmoke"
Grid.Column="1"
HorizontalTextAlignment="End"/>
</Grid>
</Frame>
</SwipeView>
</ContentView>
public partial class TrackListItemComponent : ContentView
{
public static readonly BindableProperty TrackProperty = BindableProperty.Create(nameof(Track), typeof(TrackModel), typeof(TrackListItemComponent), null);
public TrackModel Track
{
get => (TrackModel)GetValue(TrackProperty);
set => SetValue(TrackProperty, value);
}
public TrackListItemComponent()
{
InitializeComponent();
}
private void OnDelete(object sender, EventArgs e)
{ }
private void OnTapp(object sender, EventArgs e)
{ }
}
您的
CollectionView
位于 Frame
内,其 HeightRequest
等于 50。这可能就是为什么除了集合中的第一个元素之外您看不到其他任何内容的原因。
此外,您可以从Microsoft文档中找到:
将 CollectionView 放置在 VerticalStackLayout 中可以阻止 CollectionView滚动,并且可以限制滚动的项目数量 显示。在这种情况下,请将 VerticalStackLayout 替换为 Grid。
如果您感兴趣,我在此处提供了演练示例,它将帮助您使用网格组件在 .NET MAUI 页面中排列元素。