Skip to content

Commit e78b270

Browse files
committed
feat: extract MainView.
1 parent eb904f1 commit e78b270

File tree

7 files changed

+216
-184
lines changed

7 files changed

+216
-184
lines changed

src/Client.Avalonia/App.axaml.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Linq;
23
using Avalonia;
34
using Avalonia.Controls.ApplicationLifetimes;
@@ -12,6 +13,8 @@ namespace Client.Avalonia;
1213

1314
public partial class App : Application
1415
{
16+
public static IServiceProvider? ServiceProvider { get; private set; }
17+
1518
public override void Initialize()
1619
{
1720
AvaloniaXamlLoader.Load(this);
@@ -21,18 +24,15 @@ public override void OnFrameworkInitializationCompleted()
2124
{
2225
var services = new ServiceCollection();
2326
services.AddSingleton<IDownloadService, MockDownloadService>();
24-
services.AddTransient<MainWindowViewModel>();
25-
var serviceProvider = new DefaultServiceProviderFactory().CreateServiceProvider(services);
27+
services.AddTransient<MainViewViewModel>();
28+
ServiceProvider = new DefaultServiceProviderFactory().CreateServiceProvider(services);
2629

2730
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
2831
{
2932
// Avoid duplicate validations from both Avalonia and the CommunityToolkit.
3033
// More info: https://docs.avaloniaui.net/docs/guides/development-guides/data-validation#manage-validationplugins
3134
DisableAvaloniaDataAnnotationValidation();
32-
desktop.MainWindow = new MainWindow
33-
{
34-
DataContext = serviceProvider.GetRequiredService<MainWindowViewModel>()
35-
};
35+
desktop.MainWindow = new MainWindow();
3636
}
3737

3838
base.OnFrameworkInitializationCompleted();
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using Client.Avalonia.Models;
2+
using Client.Avalonia.Services;
3+
using CommunityToolkit.Mvvm.ComponentModel;
4+
using CommunityToolkit.Mvvm.Input;
5+
6+
namespace Client.Avalonia.ViewModels;
7+
8+
public partial class MainViewViewModel : ViewModelBase
9+
{
10+
private readonly IDownloadService _downloadService;
11+
12+
[ObservableProperty] private DownloadStatistics _statistics;
13+
14+
[ObservableProperty] [NotifyCanExecuteChangedFor(nameof(StartCommand), nameof(StopCommand))]
15+
private DownloadStatus _status;
16+
17+
public MainViewViewModel(IDownloadService downloadService)
18+
{
19+
_downloadService = downloadService;
20+
_downloadService.ProgressChanged += stats => Statistics = stats;
21+
_downloadService.StatusChanged += status => Status = status;
22+
23+
Statistics = _downloadService.CurrentStatistics;
24+
Status = _downloadService.Status;
25+
}
26+
27+
#region Buttons
28+
29+
private bool CanStart => Status is DownloadStatus.NotStarted or DownloadStatus.Downloading or DownloadStatus.Paused;
30+
31+
[RelayCommand(CanExecute = nameof(CanStart))]
32+
private void Start() => _downloadService.Start();
33+
34+
private bool CanStop => Status is DownloadStatus.Downloading or DownloadStatus.Paused;
35+
36+
[RelayCommand(CanExecute = nameof(CanStop))]
37+
private void Stop() => _downloadService.Stop();
38+
39+
[RelayCommand]
40+
private void Restart() => _downloadService.Restart();
41+
42+
#endregion
43+
}
Lines changed: 2 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,3 @@
1-
using Client.Avalonia.Models;
2-
using Client.Avalonia.Services;
3-
using CommunityToolkit.Mvvm.ComponentModel;
4-
using CommunityToolkit.Mvvm.Input;
1+
namespace Client.Avalonia.ViewModels;
52

6-
namespace Client.Avalonia.ViewModels;
7-
8-
public partial class MainWindowViewModel : ViewModelBase
9-
{
10-
private readonly IDownloadService _downloadService;
11-
12-
[ObservableProperty] private DownloadStatistics _statistics;
13-
14-
[ObservableProperty] [NotifyCanExecuteChangedFor(nameof(StartCommand), nameof(StopCommand))]
15-
private DownloadStatus _status;
16-
17-
public MainWindowViewModel(IDownloadService downloadService)
18-
{
19-
_downloadService = downloadService;
20-
_downloadService.ProgressChanged += stats => Statistics = stats;
21-
_downloadService.StatusChanged += status => Status = status;
22-
23-
Statistics = _downloadService.CurrentStatistics;
24-
Status = _downloadService.Status;
25-
}
26-
27-
#region Buttons
28-
29-
private bool CanStart => Status is DownloadStatus.NotStarted or DownloadStatus.Downloading or DownloadStatus.Paused;
30-
31-
[RelayCommand(CanExecute = nameof(CanStart))]
32-
private void Start() => _downloadService.Start();
33-
34-
private bool CanStop => Status is DownloadStatus.Downloading or DownloadStatus.Paused;
35-
36-
[RelayCommand(CanExecute = nameof(CanStop))]
37-
private void Stop() => _downloadService.Stop();
38-
39-
[RelayCommand]
40-
private void Restart() => _downloadService.Restart();
41-
42-
#endregion
43-
}
3+
public partial class MainWindowViewModel : ViewModelBase;
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
<UserControl xmlns="https://github.com/avaloniaui"
2+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
3+
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
4+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
5+
xmlns:converters="clr-namespace:Avalonia.Controls.Converters;assembly=Avalonia.Controls"
6+
xmlns:vm="clr-namespace:Client.Avalonia.ViewModels"
7+
xmlns:services="clr-namespace:Client.Avalonia.Services"
8+
xmlns:cvt="clr-namespace:Client.Avalonia.Converters"
9+
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
10+
x:Class="Client.Avalonia.Views.MainView"
11+
x:DataType="vm:MainViewViewModel">
12+
<UserControl.Resources>
13+
<ResourceDictionary>
14+
<converters:EnumToBoolConverter x:Key="EnumToBooleanConverter" />
15+
</ResourceDictionary>
16+
</UserControl.Resources>
17+
18+
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Spacing="10" MinWidth="550">
19+
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
20+
<Image
21+
Margin="30"
22+
Width="150"
23+
Height="150"
24+
Source="/Assets/GeneralUpdate.png" />
25+
<Image
26+
Margin="30"
27+
Width="150"
28+
Height="150"
29+
Source="{SvgImage /Assets/Semi.svg}" />
30+
</StackPanel>
31+
32+
<SelectableTextBlock
33+
HorizontalAlignment="Center"
34+
FontSize="20"
35+
FontWeight="Bold">
36+
<Run Text="General Update" />
37+
<Run Text="{Binding Statistics.Version}" />
38+
<Run Text="版本更新" />
39+
</SelectableTextBlock>
40+
41+
<Panel>
42+
<SelectableTextBlock
43+
HorizontalAlignment="Left"
44+
Text="{Binding Statistics.Speed, StringFormat='下载速度:{0:F2} MB/s'}" />
45+
46+
<SelectableTextBlock
47+
HorizontalAlignment="Center">
48+
<Run Text="已下载:" />
49+
<Run Text="{Binding Statistics.BytesReceivedInMB, StringFormat='\{0:F2}'}" />
50+
<Run Text="/" />
51+
<Run Text="{Binding Statistics.TotalBytesToReceiveInMB, StringFormat='\{0:F2}'}" />
52+
<Run Text="MB" />
53+
</SelectableTextBlock>
54+
55+
<SelectableTextBlock
56+
Name="RemainingTimeTextBlock"
57+
HorizontalAlignment="Right"
58+
IsVisible="{Binding Status,
59+
Converter={StaticResource EnumToBooleanConverter},
60+
ConverterParameter={x:Static services:DownloadStatus.Downloading},
61+
Mode=OneWay}"
62+
Text="{Binding Statistics.Remaining, StringFormat='剩余时间:{0:mm\\:ss}'}" />
63+
<SelectableTextBlock
64+
HorizontalAlignment="Right"
65+
IsVisible="{Binding !#RemainingTimeTextBlock.IsVisible}"
66+
Text="{Binding Status,Converter={x:Static cvt:EnumConverter.EnumToDescriptionConverter}}" />
67+
</Panel>
68+
69+
<ProgressBar
70+
Name="Bar"
71+
ShowProgressText="True"
72+
Classes.Success="{Binding Status,
73+
Converter={StaticResource EnumToBooleanConverter},
74+
ConverterParameter={x:Static services:DownloadStatus.Completed},
75+
Mode=OneWay}"
76+
Value="{Binding Statistics.ProgressPercentage}">
77+
<ProgressBar.Styles>
78+
<Style Selector="ProgressBar">
79+
<Setter Property="Transitions">
80+
<Transitions>
81+
<DoubleTransition Easing="CubicEaseInOut" Property="Value" Duration="0:0:0.3" />
82+
<BrushTransition Property="Foreground" Duration="0:0:0.3" />
83+
</Transitions>
84+
</Setter>
85+
</Style>
86+
</ProgressBar.Styles>
87+
</ProgressBar>
88+
<Border
89+
Padding="10"
90+
Background="{DynamicResource SemiBackground0Color}"
91+
Theme="{StaticResource CardBorder}">
92+
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Spacing="10">
93+
<Button
94+
Name="StopButton"
95+
Padding="8"
96+
Theme="{StaticResource BorderlessButton}"
97+
Classes="Danger"
98+
ToolTip.Tip="停止"
99+
Command="{Binding StopCommand}">
100+
<PathIcon
101+
Theme="{StaticResource InnerPathIcon}"
102+
Data="{StaticResource IconStop}" />
103+
</Button>
104+
<ToggleSwitch
105+
Name="StartButton"
106+
Padding="8"
107+
IsChecked="{Binding Status,
108+
Converter={StaticResource EnumToBooleanConverter},
109+
ConverterParameter={x:Static services:DownloadStatus.Downloading},
110+
Mode=OneWay}"
111+
Theme="{StaticResource ButtonToggleSwitch}"
112+
Command="{Binding StartCommand}">
113+
<ToolTip.Tip>
114+
<Panel>
115+
<TextBlock Text="下载" IsVisible="{Binding !$parent[ToggleSwitch].IsChecked}" />
116+
<TextBlock Text="暂停" IsVisible="{Binding $parent[ToggleSwitch].IsChecked}" />
117+
</Panel>
118+
</ToolTip.Tip>
119+
<ToggleSwitch.OffContent>
120+
<PathIcon
121+
Theme="{StaticResource InnerPathIcon}"
122+
Data="{StaticResource IconPlay}" />
123+
</ToggleSwitch.OffContent>
124+
<ToggleSwitch.OnContent>
125+
<PathIcon
126+
Theme="{StaticResource InnerPathIcon}"
127+
Data="{StaticResource IconPause}" />
128+
</ToggleSwitch.OnContent>
129+
</ToggleSwitch>
130+
<Button
131+
Name="RestartButton"
132+
Padding="8"
133+
Theme="{StaticResource BorderlessButton}"
134+
Classes="Tertiary"
135+
ToolTip.Tip="重新下载"
136+
Command="{Binding RestartCommand}">
137+
<PathIcon
138+
Theme="{StaticResource InnerPathIcon}"
139+
Data="{StaticResource IconRestart}" />
140+
</Button>
141+
</StackPanel>
142+
</Border>
143+
</StackPanel>
144+
</UserControl>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using Avalonia.Controls;
2+
3+
namespace Client.Avalonia.Views;
4+
5+
public partial class MainView : UserControl
6+
{
7+
public MainView()
8+
{
9+
InitializeComponent();
10+
}
11+
}

0 commit comments

Comments
 (0)