返回提交历史
Modified
XFEToolBox/ViewModel/Pages/MainPageViewModel.cs
+23
-4
Modified
XFEToolBox/Views/Controls/Carousel.xaml
+115
-70
Modified
XFEToolBox/Views/Controls/Carousel.xaml.cs
+20
-20
Added
XFEToolBox/Views/Controls/CarouselImageItem.cs
+21
-0
Modified
XFEToolBox/Views/Pages/MainPage.xaml
+5
-5
XFEstudio/XFEToolBox
完成主页轮播图
9bb489a
代码差异
5 个文件
+184
-99
@@ -1,5 +1,5 @@
1
1
using CommunityToolkit.Mvvm.ComponentModel;
2
using System.Windows.Media;
2
using System.Diagnostics;
3
3
using System.Windows.Media.Imaging;
4
4
using XFEToolBox.Utilities.Helpers;
5
5
using XFEToolBox.Views.Pages;
@@ -8,6 +8,7 @@ namespace XFEToolBox.ViewModel.Pages;
8
8
9
9
public partial class MainPageViewModel : ObservableObject
10
10
{
11
public bool IsInitialized { get; set; } = false;
11
12
public MainPage MainPage { get; set; }
12
13
public MainPageViewModel(MainPage mainPage)
13
14
{
@@ -17,10 +18,28 @@ public partial class MainPageViewModel : ObservableObject
17
18
18
19
private async void MainPage_Loaded(object sender, System.Windows.RoutedEventArgs e)
19
20
{
20
var videoInfoList = await BilibiliHelper.GetSeasonVideoList();
21
foreach (var videoInfo in videoInfoList)
21
if (IsInitialized)
22
return;
23
IsInitialized = true;
24
int retryCount = 0;
25
while (true)
22
26
{
23
MainPage.mainCarousel.AddItem(new BitmapImage(new Uri(videoInfo["pic"].ToString())), videoInfo["title"].ToString());
27
try
28
{
29
var videoInfoList = await BilibiliHelper.GetSeasonVideoList();
30
foreach (var videoInfo in videoInfoList)
31
{
32
MainPage.mainCarousel.AddItem(new BitmapImage(new Uri(videoInfo["pic"].ToString())), videoInfo["title"].ToString(), () => Process.Start("explorer.exe", $"https://www.bilibili.com/video/{videoInfo["bvid"]}"));
33
}
34
break;
35
}
36
catch (Exception ex)
37
{
38
if (retryCount > 3)
39
break;
40
await Task.Delay(3000);
41
retryCount++;
42
}
24
43
}
25
44
}
26
45
}
@@ -5,100 +5,145 @@
5
5
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
6
6
xmlns:local="clr-namespace:XFEToolBox.Views.Controls"
7
7
mc:Ignorable="d"
8
d:DesignHeight="280" d:DesignWidth="800">
8
d:DesignHeight="320" d:DesignWidth="900">
9
9
<Grid>
10
<Grid.RowDefinitions>
11
<RowDefinition Height="*"/>
12
<RowDefinition Height="Auto"/>
13
</Grid.RowDefinitions>
10
14
<Grid.ColumnDefinitions>
11
<ColumnDefinition Width="56"/>
15
<ColumnDefinition Width="0"/>
12
16
<ColumnDefinition Width="*"/>
13
<ColumnDefinition Width="56"/>
17
<ColumnDefinition Width="0"/>
14
18
</Grid.ColumnDefinitions>
15
19
16
<!-- Left side bar column -->
17
<Border Grid.Column="0" Background="Transparent" Cursor="Hand" MouseLeftButtonUp="PrevButton_Click">
18
<Grid>
19
<Border Width="36" HorizontalAlignment="Center" VerticalAlignment="Stretch" CornerRadius="18" Background="#33FFFFFF" Margin="0,8"/>
20
<!-- Scalable left-pointing triangle, centered -->
21
<Path Width="12" Height="18" Stretch="Uniform" Fill="#333333" HorizontalAlignment="Center" VerticalAlignment="Center" Data="M1,0 L0,0.5 L1,1 Z"/>
22
</Grid>
23
</Border>
20
<!-- Left spacer column (kept for layout) -->
21
<Border Grid.Row="0" Grid.Column="0" Background="Transparent"/>
24
22
25
23
<!-- Center card column with rounded border and image -->
26
<Border Grid.Column="1" CornerRadius="12" Background="#F0EEF6" Padding="8">
24
<Border Grid.Row="0" Grid.Column="1" CornerRadius="12" Background="#F0EEF6" Padding="8">
27
25
<Grid>
28
26
<!-- Two images for cross-fade -->
29
27
<Grid>
30
<Image x:Name="ImageBack" Stretch="UniformToFill" Opacity="0" ClipToBounds="True"/>
31
<Image x:Name="ImageFront" Stretch="UniformToFill" Opacity="1" ClipToBounds="True"/>
28
<Image x:Name="ImageBack" Stretch="UniformToFill" Opacity="0" ClipToBounds="True" MouseLeftButtonUp="Image_MouseLeftButtonUp"/>
29
<Image x:Name="ImageFront" Stretch="UniformToFill" Opacity="1" ClipToBounds="True" MouseLeftButtonUp="Image_MouseLeftButtonUp"/>
32
30
</Grid>
33
31
34
<!-- Bottom acrylic bar inside center card -->
35
<Border VerticalAlignment="Bottom" Margin="8" CornerRadius="8" Background="#CCFFFFFF" Height="64" Padding="12">
32
<!-- Prev/Next buttons inside content area -->
33
<Button x:Name="PrevButton" Width="25" Height="80" Margin="8,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Stretch" Background="#AAFFFFFF" BorderBrush="Transparent" Click="PrevButton_Click" Cursor="Hand">
34
<Button.Style>
35
<Style TargetType="Button">
36
<Setter Property="Opacity" Value="0.5"/>
37
<Setter Property="Template">
38
<Setter.Value>
39
<ControlTemplate TargetType="Button">
40
<Border Background="{TemplateBinding Background}" CornerRadius="12">
41
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
42
</Border>
43
</ControlTemplate>
44
</Setter.Value>
45
</Setter>
46
<Style.Triggers>
47
<Trigger Property="IsMouseOver" Value="True">
48
<Setter Property="Opacity" Value="1"/>
49
</Trigger>
50
</Style.Triggers>
51
</Style>
52
</Button.Style>
53
<Path Width="12" Height="18" Stretch="Uniform" Fill="#333333" Data="M1,0 L0,0.5 L1,1 Z"/>
54
</Button>
55
56
<Button x:Name="NextButton" Width="25" Height="80" Margin="0,0,8,0" HorizontalAlignment="Right" VerticalAlignment="Center" Background="#AAFFFFFF" BorderBrush="Transparent" Click="NextButton_Click" Cursor="Hand">
57
<Button.Style>
58
<Style TargetType="Button">
59
<Setter Property="Opacity" Value="0.5"/>
60
<Setter Property="Template">
61
<Setter.Value>
62
<ControlTemplate TargetType="Button">
63
<Border Background="{TemplateBinding Background}" CornerRadius="12">
64
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
65
</Border>
66
</ControlTemplate>
67
</Setter.Value>
68
</Setter>
69
<Style.Triggers>
70
<Trigger Property="IsMouseOver" Value="True">
71
<Setter Property="Opacity" Value="1"/>
72
</Trigger>
73
</Style.Triggers>
74
</Style>
75
</Button.Style>
76
<Path Width="12" Height="18" Stretch="Uniform" Fill="#333333" Data="M0,0 L1,0.5 L0,1 Z"/>
77
</Button>
78
79
<!-- Bottom acrylic bar inside center card (title only) -->
80
<Border VerticalAlignment="Bottom" CornerRadius="0" Background="#CCFFFFFF" Height="64" Padding="12">
36
81
<Grid>
37
82
<Grid.ColumnDefinitions>
38
83
<ColumnDefinition Width="*"/>
39
<ColumnDefinition Width="Auto"/>
40
84
<ColumnDefinition Width="120"/>
41
85
</Grid.ColumnDefinitions>
42
86
43
87
<TextBlock x:Name="PART_Title" Grid.Column="0" Text="{Binding CurrentTitle, RelativeSource={RelativeSource AncestorType=UserControl}}" VerticalAlignment="Center" Foreground="#222" FontSize="16" FontWeight="SemiBold" TextTrimming="CharacterEllipsis"/>
44
88
45
<ItemsControl Grid.Column="1" ItemsSource="{Binding ImageList, RelativeSource={RelativeSource AncestorType=UserControl}}" HorizontalAlignment="Center" VerticalAlignment="Center">
46
<ItemsControl.ItemsPanel>
47
<ItemsPanelTemplate>
48
<StackPanel Orientation="Horizontal"/>
49
</ItemsPanelTemplate>
50
</ItemsControl.ItemsPanel>
51
<ItemsControl.ItemTemplate>
52
<DataTemplate>
53
<Border Background="Transparent" Padding="6" Cursor="Hand" MouseLeftButtonUp="Indicator_MouseLeftButtonUp">
54
<Ellipse x:Name="Dot" Width="10" Height="10" Opacity="0.5" RenderTransformOrigin="0.5,0.5">
55
<Ellipse.Fill>
56
<SolidColorBrush Color="#888888"/>
57
</Ellipse.Fill>
58
<Ellipse.RenderTransform>
59
<ScaleTransform ScaleX="1" ScaleY="1"/>
60
</Ellipse.RenderTransform>
61
</Ellipse>
62
</Border>
63
<DataTemplate.Triggers>
64
<DataTrigger Binding="{Binding IsSelected}" Value="True">
65
<DataTrigger.EnterActions>
66
<BeginStoryboard Name="EnterDot">
67
<Storyboard>
68
<ColorAnimation Storyboard.TargetName="Dot" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" To="#FF6A5ACD" Duration="0:0:0.12"/>
69
<DoubleAnimation Storyboard.TargetName="Dot" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.12"/>
70
<DoubleAnimation Storyboard.TargetName="Dot" Storyboard.TargetProperty="RenderTransform.(ScaleTransform.ScaleX)" To="1.4" Duration="0:0:0.18"/>
71
<DoubleAnimation Storyboard.TargetName="Dot" Storyboard.TargetProperty="RenderTransform.(ScaleTransform.ScaleY)" To="1.4" Duration="0:0:0.18"/>
72
</Storyboard>
73
</BeginStoryboard>
74
</DataTrigger.EnterActions>
75
<DataTrigger.ExitActions>
76
<BeginStoryboard Name="ExitDot">
77
<Storyboard>
78
<ColorAnimation Storyboard.TargetName="Dot" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" To="#888888" Duration="0:0:0.12"/>
79
<DoubleAnimation Storyboard.TargetName="Dot" Storyboard.TargetProperty="Opacity" To="0.5" Duration="0:0:0.12"/>
80
<DoubleAnimation Storyboard.TargetName="Dot" Storyboard.TargetProperty="RenderTransform.(ScaleTransform.ScaleX)" To="1" Duration="0:0:0.18"/>
81
<DoubleAnimation Storyboard.TargetName="Dot" Storyboard.TargetProperty="RenderTransform.(ScaleTransform.ScaleY)" To="1" Duration="0:0:0.18"/>
82
</Storyboard>
83
</BeginStoryboard>
84
</DataTrigger.ExitActions>
85
</DataTrigger>
86
</DataTemplate.Triggers>
87
</DataTemplate>
88
</ItemsControl.ItemTemplate>
89
</ItemsControl>
90
89
</Grid>
91
90
</Border>
92
</Grid>
93
</Border>
94
91
95
<!-- Right side bar column -->
96
<Border Grid.Column="2" Background="Transparent" Cursor="Hand" MouseLeftButtonUp="NextButton_Click">
97
<Grid>
98
<Border Width="36" HorizontalAlignment="Center" VerticalAlignment="Stretch" CornerRadius="18" Background="#33FFFFFF" Margin="0,8"/>
99
<!-- Scalable right-pointing triangle, centered -->
100
<Path Width="12" Height="18" Stretch="Uniform" Fill="#333333" HorizontalAlignment="Center" VerticalAlignment="Center" Data="M0,0 L1,0.5 L0,1 Z"/>
101
92
</Grid>
102
93
</Border>
94
95
<!-- Right spacer column -->
96
<Border Grid.Row="0" Grid.Column="2" Background="Transparent"/>
97
98
<!-- Indicators row, placed below content and centered -->
99
<Grid Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Background="Transparent" Margin="0,8,0,0">
100
<ItemsControl ItemsSource="{Binding ImageList, RelativeSource={RelativeSource AncestorType=UserControl}}" HorizontalAlignment="Center" VerticalAlignment="Center">
101
<ItemsControl.ItemsPanel>
102
<ItemsPanelTemplate>
103
<StackPanel Orientation="Horizontal"/>
104
</ItemsPanelTemplate>
105
</ItemsControl.ItemsPanel>
106
<ItemsControl.ItemTemplate>
107
<DataTemplate>
108
<Border Background="Transparent" Padding="6" Cursor="Hand" MouseLeftButtonUp="Indicator_MouseLeftButtonUp">
109
<Grid>
110
<Ellipse x:Name="Dot" Width="5" Height="5" Opacity="0.5" RenderTransformOrigin="0.5,0.5">
111
<Ellipse.Fill>
112
<SolidColorBrush Color="#FFFFFF"/>
113
</Ellipse.Fill>
114
<Ellipse.RenderTransform>
115
<ScaleTransform ScaleX="1" ScaleY="1"/>
116
</Ellipse.RenderTransform>
117
</Ellipse>
118
</Grid>
119
</Border>
120
<DataTemplate.Triggers>
121
<DataTrigger Binding="{Binding IsSelected}" Value="True">
122
<DataTrigger.EnterActions>
123
<BeginStoryboard>
124
<Storyboard>
125
<ColorAnimation Storyboard.TargetName="Dot" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" To="#FF6A5ACD" Duration="0:0:0.12"/>
126
<DoubleAnimation Storyboard.TargetName="Dot" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.12"/>
127
<DoubleAnimation Storyboard.TargetName="Dot" Storyboard.TargetProperty="RenderTransform.(ScaleTransform.ScaleX)" To="1.4" Duration="0:0:0.18"/>
128
<DoubleAnimation Storyboard.TargetName="Dot" Storyboard.TargetProperty="RenderTransform.(ScaleTransform.ScaleY)" To="1.4" Duration="0:0:0.18"/>
129
</Storyboard>
130
</BeginStoryboard>
131
</DataTrigger.EnterActions>
132
<DataTrigger.ExitActions>
133
<BeginStoryboard>
134
<Storyboard>
135
<ColorAnimation Storyboard.TargetName="Dot" Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)" To="#FFFFFF" Duration="0:0:0.12"/>
136
<DoubleAnimation Storyboard.TargetName="Dot" Storyboard.TargetProperty="Opacity" To="0.5" Duration="0:0:0.12"/>
137
<DoubleAnimation Storyboard.TargetName="Dot" Storyboard.TargetProperty="RenderTransform.(ScaleTransform.ScaleX)" To="1" Duration="0:0:0.18"/>
138
<DoubleAnimation Storyboard.TargetName="Dot" Storyboard.TargetProperty="RenderTransform.(ScaleTransform.ScaleY)" To="1" Duration="0:0:0.18"/>
139
</Storyboard>
140
</BeginStoryboard>
141
</DataTrigger.ExitActions>
142
</DataTrigger>
143
</DataTemplate.Triggers>
144
</DataTemplate>
145
</ItemsControl.ItemTemplate>
146
</ItemsControl>
147
</Grid>
103
148
</Grid>
104
149
</UserControl>
@@ -9,21 +9,6 @@ using System.Windows.Media.Imaging;
9
9
10
10
namespace XFEToolBox.Views.Controls;
11
11
12
public class CarouselImageItem : INotifyPropertyChanged
13
{
14
private ImageSource? image;
15
public ImageSource? Image { get => image; set { image = value; OnPropertyChanged(nameof(Image)); } }
16
17
private string title = string.Empty;
18
public string Title { get => title; set { title = value; OnPropertyChanged(nameof(Title)); } }
19
20
private bool isSelected = false;
21
public bool IsSelected { get => isSelected; set { isSelected = value; OnPropertyChanged(nameof(IsSelected)); } }
22
23
public event PropertyChangedEventHandler? PropertyChanged;
24
protected void OnPropertyChanged(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
25
}
26
27
12
/// <summary>
28
13
/// Carousel.xaml 的交互逻辑
29
14
/// </summary>
@@ -85,7 +70,7 @@ public partial class Carousel : UserControl, INotifyPropertyChanged
85
70
86
71
// Ensure ImageList is not null
87
72
if (GetValue(ImageListProperty) == null)
88
ImageList = new ObservableCollection<CarouselImageItem>();
73
ImageList = [];
89
74
90
75
Loaded += Carousel_Loaded;
91
76
Unloaded += Carousel_Unloaded;
@@ -223,15 +208,30 @@ public partial class Carousel : UserControl, INotifyPropertyChanged
223
208
}
224
209
}
225
210
211
private void Image_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
212
{
213
if (currentIndex >= 0 && currentIndex < ImageList.Count)
214
{
215
var item = ImageList[currentIndex];
216
try
217
{
218
item?.Action?.Invoke();
219
}
220
catch
221
{
222
// ignore action exceptions
223
}
224
}
225
}
226
226
227
// Public API helpers
227
public void AddItem(ImageSource image, string title)
228
public void AddItem(ImageSource image, string title, Action? action = null)
228
229
{
229
var frozen = image as BitmapSource;
230
if (frozen != null && frozen.CanFreeze)
230
if (image is BitmapSource frozen && frozen.CanFreeze)
231
231
{
232
232
try { frozen.Freeze(); } catch { }
233
233
}
234
ImageList.Add(new CarouselImageItem { Image = image, Title = title });
234
ImageList.Add(new CarouselImageItem { Image = image, Title = title, Action = action });
235
235
if (ImageList.Count == 1)
236
236
{
237
237
currentIndex = 0;
@@ -0,0 +1,21 @@
1
using System.ComponentModel;
2
using System.Windows.Media;
3
4
namespace XFEToolBox.Views.Controls;
5
6
public class CarouselImageItem : INotifyPropertyChanged
7
{
8
private ImageSource? image;
9
public ImageSource? Image { get => image; set { image = value; OnPropertyChanged(nameof(Image)); } }
10
11
private string title = string.Empty;
12
public string Title { get => title; set { title = value; OnPropertyChanged(nameof(Title)); } }
13
14
private bool isSelected = false;
15
public bool IsSelected { get => isSelected; set { isSelected = value; OnPropertyChanged(nameof(IsSelected)); } }
16
17
public Action? Action { get; set; }
18
19
public event PropertyChangedEventHandler? PropertyChanged;
20
protected void OnPropertyChanged(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
21
}
@@ -9,15 +9,15 @@
9
9
d:DesignHeight="450" d:DesignWidth="800"
10
10
Title="MainPage">
11
11
<Grid>
12
<ctr:SmoothScrollViewer>
12
<ctr:SmoothScrollViewer Padding="10,0,0,0">
13
13
<ScrollViewer.Resources>
14
14
<Style TargetType="ScrollBar" BasedOn="{StaticResource ConsoleScrollBar}"/>
15
15
</ScrollViewer.Resources>
16
16
<StackPanel Orientation="Vertical">
17
<ctr:Carousel x:Name="mainCarousel" Grid.Column="0" HorizontalAlignment="Stretch" VerticalAlignment="Center" Height="220"/>
18
<TextBlock Margin="10,0" Text="最近使用" Foreground="{DynamicResource BackgroundColor}" FontSize="15" FontWeight="Bold"/>
19
<Border CornerRadius="15" Background="White" Margin="10" Padding="10">
20
<WrapPanel MinHeight="500">
17
<ctr:Carousel x:Name="mainCarousel" HorizontalAlignment="Stretch" VerticalAlignment="Center" Height="350"/>
18
<TextBlock Text="最近使用" Foreground="{DynamicResource BackgroundColor}" FontSize="15" FontWeight="Bold"/>
19
<Border CornerRadius="15" Background="White" Margin="0,10" Padding="10">
20
<WrapPanel MinHeight="300">
21
21
</WrapPanel>
22
22
</Border>
23
23
</StackPanel>