XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEToolBox

【WPF】XFE工具箱

公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/XFEToolBox

完成主页轮播图

9bb489a
XFE工作室室长 <mail@xfegzs.com>
提交于

代码差异

5 个文件 +184 -99
Modified XFEToolBox/ViewModel/Pages/MainPageViewModel.cs +23 -4
@@ -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 }
Modified XFEToolBox/Views/Controls/Carousel.xaml +115 -70
@@ -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>
Modified XFEToolBox/Views/Controls/Carousel.xaml.cs +20 -20
@@ -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;
Added XFEToolBox/Views/Controls/CarouselImageItem.cs +21 -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 }
Modified XFEToolBox/Views/Pages/MainPage.xaml +5 -5
@@ -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>