正在决定是否基于 WPF+MVVM 重新开发高考倒计时

本文最后更新于:3 个月前

初识 WPF

这两天简单学习了一下 WPF、XAML、MVVM,正在决定是否基于 WPF 重新开发高考倒计时,目前已经完成了 0.5% (🤣),只是完成了一点点设计,主要代码还没开始。

先说说我对 WPF 的第一印象吧:

  • 排在第一肯定是内存占用,我不李姐为什么一个空窗口都能占 50MB 内存,那以后代码量增多了不得冲个100MB或者是更多?这确实是一件难以置信的事,不过现在大多数的电脑运行内存都较大,应该也不会太过在意内存占用的问题。这点可以暂时忽略。
  • 通过数据绑定更改控件属性也太麻烦了吧,尽管可以使用纯 C# 代码控制控件属性,可这显然不符合 WPF 的设计理念。这一点上不如 WinForms (如果微软能花更多心思在 WinForms 上就好了,结合一下两者的优点)。

另外,由于高考完后时间确实很紧以及其他原因,以后 (已更新) 可能会放弃此次决定,特别是当前 WinForms 对于一个高考倒计时来说已经完全足够了的情况。

部分代码预览

MainWindow.xaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<Window
x:Class="WangHaonie.CEETimer.Windows.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:reactiveui="http://reactiveui.net"
xmlns:local="clr-namespace:WangHaonie.CEETimer.Windows"
mc:Ignorable="d"
Title="高考倒计时"
Height="37"
Width="162"
ResizeMode="NoResize"
SizeToContent="WidthAndHeight"
Topmost="True"
Background="Transparent"
AllowsTransparency="True"
WindowStyle="None">
<Border x:Name="WindowBorder" Padding="2,1,2,1" Background="Black">
<Grid>
<TextBlock x:Name="TextBlockCountdown" Background="Black" Foreground="White" FontSize="28" FontFamily="Noto Sans SC" VerticalAlignment="Top" HorizontalAlignment="Left">
<TextBlock.ContextMenu>
<ContextMenu FontFamily="Microsoft YaHei" >
<MenuItem x:Name="ContextOptions" Header="选项" />
<Separator Margin="0" />
<MenuItem x:Name="ContextOpenDir" Header="安装目录" />
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</Grid>
</Border>
</Window>

MainWindow.xaml.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
using ReactiveUI;
using System;
using System.ComponentModel;
using System.Reactive.Disposables;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using WangHaonie.CEETimer.Modules;
using WangHaonie.CEETimer.ViewModels;

namespace WangHaonie.CEETimer.Windows
{
public partial class MainWindow : IViewFor<MainWindowViewModel>
{
public static readonly DependencyProperty ViewModelProperty = DependencyProperty
.Register(nameof(ViewModel), typeof(MainWindowViewModel), typeof(MainWindow));

public MainWindowViewModel ViewModel
{
get => (MainWindowViewModel)GetValue(ViewModelProperty);
set => SetValue(ViewModelProperty, value);
}

object IViewFor.ViewModel
{
get => ViewModel;
set => ViewModel = (MainWindowViewModel)value;
}

public MainWindow()
{
InitializeComponent();
ApplyRoundedCorners();
BindData();
BindEvents();
}

private void ApplyRoundedCorners()
{
if (Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version > new Version(10, 0, 21999, 0))
{
var preference = Natives.DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUND;
Natives.DwmSetWindowAttribute(new WindowInteropHelper(GetWindow(this)).EnsureHandle(), Natives.DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE, ref preference, sizeof(uint));
}
else
{
WindowBorder.CornerRadius = new(8);
WindowBorder.BorderBrush = Brushes.Gray;
WindowBorder.BorderThickness = new(1);
}

}

private void BindData()
{
ViewModel = new MainWindowViewModel();

this.WhenActivated(d =>
{
this.Bind(ViewModel, x => x.CountdownText, x => x.TextBlockCountdown.Text).DisposeWith(d);
});
}

private void BindEvents()
{
ContextOptions.Click += ContextOptions_Click;
ContextOpenDir.Click += ContextOpenDir_Click;
TextBlockCountdown.MouseLeftButtonDown += TextBlockCountdown_MouseLeftButtonDown;
TextBlockCountdown.MouseMove += TextBlockCountdown_MouseMove;
TextBlockCountdown.MouseLeftButtonUp += TextBlockCountdown_MouseLeftButtonUp;
Closing += MainWindow_Closing;
}

private void TextBlockCountdown_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
Cursor = Cursors.SizeAll;
DragMove();
}
}

private void TextBlockCountdown_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
Cursor = Cursors.SizeAll;
}
else
{
Cursor = Cursors.Arrow;
}
}

private void TextBlockCountdown_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
Cursor = Cursors.Arrow;
}

private void ContextOptions_Click(object sender, RoutedEventArgs e)
{
SingletonHelper<OptionsWindow>.ShowWindow();
}

private void ContextOpenDir_Click(object sender, RoutedEventArgs e)
{
App.OpenInstallDir();
}

private void MainWindow_Closing(object sender, CancelEventArgs e)
{
e.Cancel = true;
}
}
}

MainWindowViewModel.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using System;
using System.Timers;
using System.Windows;

namespace WangHaonie.CEETimer.ViewModels
{
public class MainWindowViewModel : ReactiveObject
{
[Reactive]
public DateTime ExamStartTime { get; set; } = new(2025, 6, 7, 9, 0, 0);

[Reactive]
public string CountdownText { get; private set; }

public MainWindowViewModel()
{
var CountdownTimer = new Timer(1000);

CountdownTimer.Elapsed += CountdownTimer_Tick;
CountdownTimer.AutoReset = true;
CountdownTimer.Start();
}

private void CountdownTimer_Tick(object sender, ElapsedEventArgs e)
{
Application.Current.Dispatcher.BeginInvoke(() =>
{
CountdownText = $"{DateTime.Now:yyyy-MM-dd dddd HH:mm:ss}";
});
}
}
}

正在决定是否基于 WPF+MVVM 重新开发高考倒计时
https://wanghaonie.github.io/posts/10cd5e4380e2/
作者
WangHaonie
发布于
2024-07-06 11:15:05
更新于
2024-07-10 10:40:43
许可协议