XFEstudio/MyFirstRespo
优化滚动,框架正式升级为.NET 9.0
9f47318
代码差异
10 个文件
+195
-34
@@ -7,5 +7,6 @@ public partial class AppCacheProfile
7
7
{
8
8
[ProfileProperty]
9
9
private string noticeText = "";
10
public AppCacheProfile() => ProfilePath = @$"{AppPath.CacheProfile}\{typeof(AppCacheProfile)}.xfe";
10
public AppCacheProfile() => ProfilePath = @$"{AppPath.CacheProfile}\{typeof(AppCacheProfile)}.xprofile";
11
11
12
}
@@ -5,11 +5,25 @@ namespace XFEToolBox.Profiles.CrossVersionProfiles;
5
5
6
6
public partial class ConsoleProfile
7
7
{
8
/// <summary>
9
/// 控制台端口
10
/// </summary>
8
11
[ProfileProperty]
9
12
private int consolePort = 3280;
13
/// <summary>
14
/// 控制台连接密码
15
/// </summary>
10
16
[ProfileProperty]
11
17
private string consolePassword = "";
18
/// <summary>
19
/// 是否只在本机回路中开放
20
/// </summary>
12
21
[ProfileProperty]
13
22
private bool localOnly = true;
14
public ConsoleProfile() => ProfilePath = @$"{AppPath.LocalProfile}\{typeof(ConsoleProfile)}.xfe";
23
/// <summary>
24
/// 最大行数
25
/// </summary>
26
[ProfileProperty]
27
private int maxLine = 9999;
28
public ConsoleProfile() => ProfilePath = @$"{AppPath.LocalProfile}\{typeof(ConsoleProfile)}.xprofile";
15
29
}
@@ -5,13 +5,22 @@ namespace XFEToolBox.Profiles.CrossVersionProfiles;
5
5
6
6
public partial class SystemProfile
7
7
{
8
/// <summary>
9
/// 当前窗口DPI缩放
10
/// </summary>
8
11
[ProfileProperty]
9
12
private double currentWindowDPIScale = 1.0;
13
/// <summary>
14
/// 主窗体宽度
15
/// </summary>
10
16
[ProfileProperty]
11
17
private double mainWindowWidth = 720;
18
/// <summary>
19
/// 主窗体高度
20
/// </summary>
12
21
[ProfileProperty]
13
22
private double mainWindowHeight = 450;
14
public SystemProfile() => ProfilePath = @$"{AppPath.LocalProfile}\{typeof(SystemProfile)}.xfe";
23
public SystemProfile() => ProfilePath = @$"{AppPath.LocalProfile}\{typeof(SystemProfile)}.xprofile";
15
24
/// <summary>
16
25
/// 工具箱现在是否可以被关闭
17
26
/// </summary>
@@ -146,6 +146,7 @@
146
146
<Setter Property="VerticalAlignment" Value="Center"/>
147
147
</Style>
148
148
<Style x:Key="NavigationButton" TargetType="RadioButton">
149
<Setter Property="IsTabStop" Value="False"/>
149
150
<Setter Property="RenderTransformOrigin" Value="0.5,0.5"/>
150
151
<Setter Property="Margin" Value="10,5"/>
151
152
<Setter Property="Template">
@@ -252,10 +253,12 @@
252
253
</Style>
253
254
<Style x:Key="NavigationFrame" TargetType="Frame">
254
255
<Setter Property="NavigationUIVisibility" Value="Hidden"/>
256
<Setter Property="IsTabStop" Value="False"/>
255
257
<Setter Property="Grid.Column" Value="1"/>
256
258
<Setter Property="Grid.Row" Value="1"/>
257
259
</Style>
258
260
<Style x:Key="ConsoleScrollBar" TargetType="ScrollBar">
261
<Setter Property="IsTabStop" Value="False"/>
259
262
<Setter Property="Template">
260
263
<Setter.Value>
261
264
<ControlTemplate TargetType="ScrollBar">
@@ -336,6 +339,7 @@
336
339
<Style x:Key="FoldButton" TargetType="Button">
337
340
<Style.Setters>
338
341
<Setter Property="Opacity" Value="1"/>
342
<Setter Property="IsTabStop" Value="False"/>
339
343
<Setter Property="Template">
340
344
<Setter.Value>
341
345
<ControlTemplate TargetType="Button">
@@ -4,14 +4,24 @@ using System.Windows;
4
4
using System.Windows.Controls;
5
5
using System.Windows.Documents;
6
6
using System.Windows.Media;
7
using System.Windows.Threading;
7
8
8
9
namespace XFEToolBox.Utilities;
9
10
10
11
public partial class DecoratedTextConverter
11
12
{
13
/// <summary>
14
/// 装饰文本
15
/// </summary>
16
/// <returns></returns>
12
17
[GeneratedRegex(@"\[(?:(?:(?<type>color)\s+(?<color>\#[0-9a-fA-F]{6}|\w+)(?:\s+(?<background>\#[0-9a-fA-F]{6}|\w+))?)|(?:(?<type>hyperlink)\s+(?:color:\s*(?<color>\#[0-9a-fA-F]{6}|\w+)(?<background>\s+\#[0-9a-fA-F]{6}|\w+)?(?:\s+))?link:\s*(?<link>.+)\s+text:\s*(?<text>.+))|(?:(?<type>foldblock)\s+(?:color:\s*(?<color>\#[0-9a-fA-F]{6}|\w+)\s+(?<background>\#[0-9a-fA-F]{6}|\w+))\s+title:\s*(?<title>.+)\s+text:\s*(?<text>(?s).+)))\]")]
13
18
public static partial Regex DecorationRegex();
14
19
/// <summary>
20
/// 转为文本块
21
/// </summary>
22
/// <param name="decoratedText"></param>
23
/// <param name="defaultColor"></param>
24
/// <returns></returns>
15
25
public static TextBlock ConvertToTextBlock(string decoratedText, Color defaultColor)
16
26
{
17
27
var results = ConvertToInlineList(decoratedText, defaultColor);
@@ -25,13 +35,49 @@ public partial class DecoratedTextConverter
25
35
}
26
36
return textBlock;
27
37
}
38
/// <summary>
39
/// 转为文本块
40
/// </summary>
41
/// <param name="decoratedText"></param>
42
/// <param name="defaultColor"></param>
43
/// <param name="dispatcher"></param>
44
/// <returns></returns>
45
public static async Task<TextBlock> ConvertToTextBlockAsync(string decoratedText, Color defaultColor, Dispatcher dispatcher)
46
{
47
var results = await ConvertToInlineListAsync(decoratedText, defaultColor, dispatcher);
48
var textBlock = new TextBlock();
49
foreach (var result in results)
50
{
51
if (result is Inline inline)
52
textBlock.Inlines.Add(inline);
53
else
54
textBlock.Inlines.Add((UIElement)result);
55
}
56
return textBlock;
57
}
58
/// <summary>
59
/// 转为行内组件列表
60
/// </summary>
61
/// <param name="decoratedText"></param>
62
/// <param name="defaultColor"></param>
63
/// <returns></returns>
28
64
public static List<object> ConvertToInlineList(string decoratedText, Color defaultColor)
29
65
{
30
66
var results = ConvertText(decoratedText, defaultColor);
67
return ConvertToInlineList(results, decoratedText);
68
}
69
/// <summary>
70
/// 转为行内组件列表
71
/// </summary>
72
/// <param name="decTextSpans"></param>
73
/// <param name="decoratedText"></param>
74
/// <returns></returns>
75
public static List<object> ConvertToInlineList(List<DecTextSpan> decTextSpans, string decoratedText = "")
76
{
31
77
var inLineList = new List<object>();
32
if (results.Count > 0)
78
if (decTextSpans.Count > 0)
33
79
{
34
foreach (var result in results)
80
foreach (var result in decTextSpans)
35
81
{
36
82
if (result is HyperLinkDecSpan hyperLinkDecSpan)
37
83
{
@@ -151,7 +197,7 @@ public partial class DecoratedTextConverter
151
197
}
152
198
}
153
199
}
154
else
200
else if (decoratedText != string.Empty)
155
201
{
156
202
inLineList.Add(new Span(new Run(decoratedText))
157
203
{
@@ -160,6 +206,24 @@ public partial class DecoratedTextConverter
160
206
}
161
207
return inLineList;
162
208
}
209
/// <summary>
210
/// 转为行内组件列表
211
/// </summary>
212
/// <param name="decoratedText"></param>
213
/// <param name="defaultColor"></param>
214
/// <param name="dispatcher"></param>
215
/// <returns></returns>
216
public static async Task<List<object>> ConvertToInlineListAsync(string decoratedText, Color defaultColor, Dispatcher dispatcher)
217
{
218
var result = await ConvertTextAsync(decoratedText, defaultColor);
219
return dispatcher.Invoke(() => ConvertToInlineList(result, decoratedText));
220
}
221
/// <summary>
222
/// 转为装饰文本
223
/// </summary>
224
/// <param name="decoratedText"></param>
225
/// <param name="defaultColor"></param>
226
/// <returns></returns>
163
227
public static List<DecTextSpan> ConvertText(string decoratedText, Color defaultColor)
164
228
{
165
229
var textSpanList = new List<DecTextSpan>();
@@ -212,4 +276,11 @@ public partial class DecoratedTextConverter
212
276
}
213
277
return textSpanList;
214
278
}
279
/// <summary>
280
/// 转为装饰文本
281
/// </summary>
282
/// <param name="decoratedText"></param>
283
/// <param name="defaultColor"></param>
284
/// <returns></returns>
285
public static async Task<List<DecTextSpan>> ConvertTextAsync(string decoratedText, Color defaultColor) => await Task.Run(() => ConvertText(decoratedText, defaultColor));
215
286
}
@@ -1,5 +1,6 @@
1
1
using CommunityToolkit.Mvvm.ComponentModel;
2
2
using CommunityToolkit.Mvvm.Input;
3
using System.Diagnostics;
3
4
using System.Net;
4
5
using System.Windows.Controls;
5
6
using System.Windows.Media;
@@ -23,6 +24,7 @@ public partial class ConsolePageViewModel(ConsolePage viewPage) : ObservableObje
23
24
private bool canCleanUp = false;
24
25
25
26
private bool lastLineIsEnd = true;
27
private bool autoScroll = true;
26
28
public ConsolePage ViewPage { get; set; } = viewPage;
27
29
public XFEConsoleTerminalServer? TerminalServer { get; set; }
28
30
@@ -48,38 +50,49 @@ public partial class ConsolePageViewModel(ConsolePage viewPage) : ObservableObje
48
50
TerminalServer = null;
49
51
}
50
52
51
private void TerminalServer_ErrorOccurred(XFEConsoleClientInfo sender, Exception e)
53
private async void TerminalServer_ErrorOccurred(XFEConsoleClientInfo sender, Exception e)
52
54
{
53
55
var code = e.InnerException?.InnerException is HttpListenerException ? (e.InnerException.InnerException as HttpListenerException)!.ErrorCode : 0;
54
56
if (code != 995)
55
ShowMessageWithTimeLine($"[foldblock color: white #ff0000 title: 错误:{e.Message} text: {e}]");
57
await ShowMessageWithTimeLineAsync($"[foldblock color: white #ff0000 title: 错误:{e.Message} text: {e}]");
56
58
}
57
59
58
private void TerminalServer_MessageReceived(XFEConsoleClientInfo sender, string e)
60
private async void TerminalServer_MessageReceived(XFEConsoleClientInfo sender, string e)
59
61
{
60
62
var dictionary = new XFEDictionary(e);
61
63
var isLine = dictionary["IsLine"] == "true";
62
64
var textMessage = dictionary["Text"];
63
65
if (lastLineIsEnd)
64
ShowMessageWithTimeLine($"{sender.ClientName}> {textMessage}", isLine);
66
await ShowMessageWithTimeLineAsync($"{sender.ClientName}> {textMessage}", isLine);
65
67
else
66
ShowMessage($"{textMessage}", isLine);
68
await ShowMessageAsync($"{textMessage}", isLine);
67
69
}
68
70
69
private void TerminalServer_Disconnected(XFEConsoleTerminalServer sender, XFEConsoleClientInfo e)
71
private async void TerminalServer_Disconnected(XFEConsoleTerminalServer sender, XFEConsoleClientInfo e)
70
72
{
71
ShowMessageWithTimeLine($"[color #9898e7]客户端断开连接 [color white]{e.ClientName}");
73
await ShowMessageWithTimeLineAsync($"[color #9898e7]客户端断开连接 [color white]{e.ClientName}");
72
74
}
73
75
74
private void TerminalServer_Connected(XFEConsoleTerminalServer sender, XFEConsoleClientInfo e)
76
private async void TerminalServer_Connected(XFEConsoleTerminalServer sender, XFEConsoleClientInfo e)
75
77
{
76
ShowMessageWithTimeLine($"[color #9898e7]客户端连接 [color white]{e.ClientName}");
78
await ShowMessageWithTimeLineAsync($"[color #9898e7]客户端连接 [color white]{e.ClientName}");
77
79
}
78
80
79
private void TerminalServer_ServerStarted(XFEConsoleTerminalServer sender)
81
private async void TerminalServer_ServerStarted(XFEConsoleTerminalServer sender)
80
82
{
81
ShowMessageWithTimeLine($"控制台服务器已在端口:[hyperlink color: #05ff00 link: http://localhost:{ConsoleProfile.ConsolePort}/ text: {ConsoleProfile.ConsolePort}] 上运行");
83
await ShowMessageWithTimeLineAsync($"控制台服务器已在端口:[hyperlink color: #05ff00 link: http://localhost:{ConsoleProfile.ConsolePort}/ text: {ConsoleProfile.ConsolePort}] 上运行");
82
84
}
85
86
internal void ScrollChanged(object sender, ScrollChangedEventArgs e)
87
{
88
if (e.VerticalChange < 0)
89
autoScroll = false;
90
else if (e.VerticalChange > 0 && e.VerticalOffset + e.ViewportHeight == e.ExtentHeight)
91
autoScroll = true;
92
if (autoScroll)
93
ViewPage.scrollViewer.ScrollToEnd();
94
}
95
83
96
/// <summary>
84
97
/// 显示消息
85
98
/// </summary>
@@ -113,38 +126,85 @@ public partial class ConsolePageViewModel(ConsolePage viewPage) : ObservableObje
113
126
});
114
127
lastLineIsEnd = isLineEnd;
115
128
}
129
130
/// <summary>
131
/// 显示消息
132
/// </summary>
133
/// <param name="message"></param>
134
/// <param name="defaultColor"></param>
135
public async Task ShowMessageAsync(string message, Color defaultColor, bool isLineEnd, bool forceLine = false)
136
{
137
if (!CanCleanUp)
138
CanCleanUp = true;
139
if (!lastLineIsEnd && !forceLine)
140
{
141
var lastControl = ViewPage.consoleStackPanel.Children.Count > 0 ? ViewPage.consoleStackPanel.Children[^1] : null;
142
if (lastControl is TextBlock lastTextBlock)
143
{
144
var inLineList = await DecoratedTextConverter.ConvertToInlineListAsync(message, defaultColor, ViewPage.Dispatcher);
145
ViewPage.Dispatcher.Invoke(() =>
146
{
147
lastTextBlock.Inlines.AddRange(inLineList);
148
lastLineIsEnd = isLineEnd;
149
});
150
}
151
return;
152
}
153
var textBlock = await DecoratedTextConverter.ConvertToTextBlockAsync(message, defaultColor, ViewPage.Dispatcher);
154
textBlock.TextWrapping = System.Windows.TextWrapping.Wrap;
155
textBlock.FontSize = 13;
156
textBlock.FontFamily = new FontFamily("Consolas");
157
ViewPage.Dispatcher.Invoke(() => ViewPage.consoleStackPanel.Children.Add(textBlock));
158
lastLineIsEnd = isLineEnd;
159
}
160
116
161
/// <summary>
117
162
/// 显示消息
118
163
/// </summary>
119
164
/// <param name="message"></param>
120
165
public void ShowMessage(string message, bool isLineEnd = true, bool forceLine = false) => ShowMessage(message, Colors.White, isLineEnd, forceLine);
166
167
/// <summary>
168
/// 显示消息
169
/// </summary>
170
/// <param name="message"></param>
171
public async Task ShowMessageAsync(string message, bool isLineEnd = true, bool forceLine = false) => await ShowMessageAsync(message, Colors.White, isLineEnd, forceLine);
172
121
173
/// <summary>
122
174
/// 显示附带时间轴的消息
123
175
/// </summary>
124
176
/// <param name="message"></param>
125
177
public void ShowMessageWithTimeLine(string message, bool isLineEnd = true) => ShowMessage($"[{DateTime.Now:HH:mm:ss}] {message}", isLineEnd, true);
178
179
/// <summary>
180
/// 显示附带时间轴的消息
181
/// </summary>
182
/// <param name="message"></param>
183
public async Task ShowMessageWithTimeLineAsync(string message, bool isLineEnd = true) => await ShowMessageAsync($"[{DateTime.Now:HH:mm:ss}] {message}", isLineEnd, true);
184
185
#region Command
126
186
[RelayCommand]
127
private void StartServer()
187
private async Task StartServer()
128
188
{
129
189
CanRestartServer = false;
130
190
CanStartServer = false;
131
191
try
132
192
{
133
ShowMessageWithTimeLine("[color yellow]正在启动服务器...");
193
await ShowMessageWithTimeLineAsync("[color yellow]正在启动服务器...");
134
194
_ = StartConsoleServer();
135
195
CanStopServer = true;
136
196
CanRestartServer = true;
137
197
}
138
198
catch (Exception ex)
139
199
{
140
ShowMessageWithTimeLine($"[color red]无法启动服务器:{ex}");
200
await ShowMessageWithTimeLineAsync($"[color red]无法启动服务器:{ex}");
141
201
CanRestartServer = false;
142
202
CanStopServer = false;
143
203
CanStartServer = true;
144
204
}
145
205
}
146
206
[RelayCommand]
147
private void StopServer()
207
private async Task StopServer()
148
208
{
149
209
CanRestartServer = false;
150
210
CanStopServer = false;
@@ -152,11 +212,11 @@ public partial class ConsolePageViewModel(ConsolePage viewPage) : ObservableObje
152
212
{
153
213
ShutDownServer();
154
214
CanStartServer = true;
155
ShowMessageWithTimeLine("[color yellow]服务器已关闭");
215
await ShowMessageWithTimeLineAsync("[color yellow]服务器已关闭");
156
216
}
157
217
catch (Exception ex)
158
218
{
159
ShowMessageWithTimeLine($"[color red]关闭服务器时出现错误:{ex}");
219
await ShowMessageWithTimeLineAsync($"[color red]关闭服务器时出现错误:{ex}");
160
220
if (TerminalServer is not null && TerminalServer.Server.Server.IsListening)
161
221
{
162
222
CanRestartServer = true;
@@ -169,7 +229,7 @@ public partial class ConsolePageViewModel(ConsolePage viewPage) : ObservableObje
169
229
}
170
230
}
171
231
[RelayCommand]
172
private void RestartServer()
232
private async Task RestartServer()
173
233
{
174
234
CanRestartServer = false;
175
235
CanStartServer = false;
@@ -177,16 +237,16 @@ public partial class ConsolePageViewModel(ConsolePage viewPage) : ObservableObje
177
237
try
178
238
{
179
239
ShutDownServer();
180
ShowMessageWithTimeLine("[color yellow]服务器已关闭");
240
await ShowMessageWithTimeLineAsync("[color yellow]服务器已关闭");
181
241
_ = StartConsoleServer();
182
242
CanStartServer = false;
183
243
CanStopServer = true;
184
244
CanRestartServer = true;
185
ShowMessageWithTimeLine("[color yellow]服务器重启完成");
245
await ShowMessageWithTimeLineAsync("[color yellow]服务器重启完成");
186
246
}
187
247
catch (Exception ex)
188
248
{
189
ShowMessageWithTimeLine($"[color red]重启服务器时出现错误:{ex}");
249
await ShowMessageWithTimeLineAsync($"[color red]重启服务器时出现错误:{ex}");
190
250
if (TerminalServer is not null && TerminalServer.Server.Server.IsListening)
191
251
{
192
252
CanRestartServer = true;
@@ -207,4 +267,5 @@ public partial class ConsolePageViewModel(ConsolePage viewPage) : ObservableObje
207
267
CanCleanUp = false;
208
268
ViewPage.consoleStackPanel.Children.Clear();
209
269
}
210
}
270
#endregion
271
}
@@ -62,7 +62,7 @@
62
62
</StackPanel>
63
63
</Grid>
64
64
<Border Grid.Column="0" Grid.Row="1" CornerRadius="0,0,15,0" Background="Black" Padding="0,5,3,5">
65
<ScrollViewer VerticalScrollBarVisibility="Auto">
65
<ScrollViewer x:Name="scrollViewer" x:FieldModifier="internal" VerticalScrollBarVisibility="Auto" IsTabStop="False" ScrollChanged="ScrollViewer_ScrollChanged">
66
66
<ScrollViewer.Resources>
67
67
<Style TargetType="ScrollBar" BasedOn="{StaticResource ConsoleScrollBar}"/>
68
68
</ScrollViewer.Resources>
@@ -17,4 +17,6 @@ public partial class ConsolePage : Page
17
17
DataContext = ViewModel;
18
18
Current = this;
19
19
}
20
21
private void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e) => ViewModel.ScrollChanged(sender, e);
20
22
}
@@ -1,5 +1,4 @@
1
using System.Diagnostics;
2
using System.Windows;
1
using System.Windows;
3
2
using System.Windows.Media.Animation;
4
3
using XFEExtension.NetCore.InputSimulator;
5
4
using XFEToolBox.Profiles.CrossVersionProfiles;
@@ -2,12 +2,12 @@
2
2
3
3
<PropertyGroup>
4
4
<OutputType>WinExe</OutputType>
5
<TargetFramework>net8.0-windows</TargetFramework>
5
<TargetFramework>net9.0-windows</TargetFramework>
6
6
<Nullable>enable</Nullable>
7
7
<ImplicitUsings>enable</ImplicitUsings>
8
8
<UseWPF>true</UseWPF>
9
9
<ApplicationIcon>Resources\Icon\XFEToolBoxIcon.ico</ApplicationIcon>
10
<Version>0.0.1</Version>
10
<Version>0.0.2</Version>
11
11
</PropertyGroup>
12
12
13
13
<ItemGroup>