XFEstudio/MyFirstRespo
完善控制台
46167c6
代码差异
29 个文件
+1517
-42
@@ -10,7 +10,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XFEToolBox.Test", "XFEToolB
10
10
{AA68EC1E-93C8-4D18-8372-64F3D91A66F1} = {AA68EC1E-93C8-4D18-8372-64F3D91A66F1}
11
11
EndProjectSection
12
12
EndProject
13
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XFEToolBox.Core", "XFEToolBox.Core\XFEToolBox.Core.csproj", "{AA68EC1E-93C8-4D18-8372-64F3D91A66F1}"
13
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XFEToolBox.Core", "XFEToolBox.Core\XFEToolBox.Core.csproj", "{AA68EC1E-93C8-4D18-8372-64F3D91A66F1}"
14
14
EndProject
15
15
Global
16
16
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -1,7 +1,4 @@
1
1
using System.Windows;
2
using XFEExtension.NetCore.AutoConfig;
3
using XFEToolBox.Core.Model;
4
using XFEToolBox.Profiles;
5
2
6
3
namespace XFEToolBox;
7
4
@@ -12,9 +9,6 @@ public partial class App : Application
12
9
{
13
10
public App()
14
11
{
15
foreach (var profile in XFEProfile.Profiles)
16
{
17
//TODO:1 XFE各类拓展的AutoConfig需要改进
18
}
12
this.InitializeComponent();
19
13
}
20
14
}
@@ -0,0 +1,148 @@
1
using System.Diagnostics;
2
using XFEExtension.NetCore.XFETransform.ObjectInfoAnalyzer;
3
using XFEExtension.NetCore.XFETransform;
4
using XFEExtension.NetCore.XFETransform.StringConverter;
5
6
namespace XFEExtension.NetCore.XFEConsole;
7
8
/// <summary>
9
/// XFE控制台
10
/// </summary>
11
public class XFEConsole
12
{
13
/// <summary>
14
/// 是否在本地调试的Debug中展示
15
/// </summary>
16
/// <remarks>
17
/// 默认为 true
18
/// </remarks>
19
public static bool ShowInDebug { get; set; } = true;
20
/// <summary>
21
/// 是否在本地控制台中显示
22
/// </summary>
23
/// <remarks>
24
/// 默认为 false
25
/// </remarks>
26
public static bool ShowInLocalConsole { get; set; } = false;
27
/// <summary>
28
/// 是否自动解析对象,而非直接输出对象的.ToString()方法
29
/// </summary>
30
/// <remarks>
31
/// 默认为 true
32
/// </remarks>
33
public static bool AutoAnalyzeObject { get; set; } = true;
34
/// <summary>
35
/// 客户端列表
36
/// </summary>
37
public static List<XFEConsoleProgramClient> ClientList { get; set; } = [];
38
/// <summary>
39
/// 使用XFE控制台
40
/// </summary>
41
/// <param name="ipAddress">IP地址</param>
42
/// <param name="name">客户端名称</param>
43
/// <param name="id">客户端ID</param>
44
/// <param name="password">密码</param>
45
/// <returns>是否连接成功</returns>
46
public static async Task<bool> UseXFEConsole(string ipAddress, string name, string id, string password)
47
{
48
SetConsoleOutput();
49
return await ConnectConsole(ipAddress, name, id, password);
50
}
51
/// <summary>
52
/// 使用XFE控制台
53
/// </summary>
54
/// <param name="port">端口</param>
55
/// <param name="password">密码</param>
56
/// <returns>是否连接成功</returns>
57
public static async Task<bool> UseXFEConsole(string port, string password = "") => await UseXFEConsole($"ws://*:{port}", AppDomain.CurrentDomain.FriendlyName, Guid.NewGuid().ToString(), password);
58
/// <summary>
59
/// 设置XFE控制台
60
/// </summary>
61
/// <returns></returns>
62
public static void SetConsoleOutput()
63
{
64
Console.SetOut(new XFEConsoleTextWriter());
65
}
66
/// <summary>
67
/// 连接XFE控制台
68
/// </summary>
69
/// <param name="ipAddress">IP地址</param>
70
/// <param name="name">客户端名称</param>
71
/// <param name="id">客户端ID</param>
72
/// <param name="password">密码</param>
73
/// <returns>是否连接成功</returns>
74
public static async Task<bool> ConnectConsole(string ipAddress, string name, string id, string password)
75
{
76
var client = new XFEConsoleProgramClient(ipAddress, name, id, password);
77
if (await client.Connect())
78
{
79
ClientList.Add(client);
80
return true;
81
}
82
else
83
{
84
return false;
85
}
86
}
87
/// <summary>
88
/// 输出对象信息
89
/// </summary>
90
/// <param name="obj">对象</param>
91
/// <param name="remarkName">对象注释</param>
92
/// <param name="onlyProperty">仅解析属性</param>
93
/// <param name="onlyPublic">仅解析公共属性或字段</param>
94
/// <returns></returns>
95
public static async Task WriteObject(object? obj, string remarkName = "分析对象", bool onlyProperty = true, bool onlyPublic = true)
96
{
97
var objectInfo = "无法分析对象:";
98
try
99
{
100
objectInfo = XFEConverter.GetObjectInfo(StringConverter.ObjectAnalyzer, remarkName, ObjectPlace.Main, 0, obj?.GetType(), obj, onlyProperty, onlyPublic).OutPutObject();
101
}
102
catch (Exception ex)
103
{
104
Debug.WriteLine(ex);
105
objectInfo += ex.StackTrace;
106
}
107
if (ShowInDebug)
108
Debug.WriteLine(objectInfo);
109
if (ShowInLocalConsole)
110
Console.WriteLine(objectInfo);
111
foreach (var client in ClientList)
112
await client.OutputMessage(objectInfo, true);
113
}
114
/// <summary>
115
/// 向已连接的XFE控制台输出一条消息
116
/// </summary>
117
/// <param name="text">文本</param>
118
/// <returns></returns>
119
public static async Task WriteLine(string? text)
120
{
121
if (text is not null)
122
{
123
if (ShowInDebug)
124
Debug.WriteLine(text);
125
if (ShowInLocalConsole)
126
Console.WriteLine(text);
127
foreach (var client in ClientList)
128
await client.OutputMessage(text, true);
129
}
130
}
131
/// <summary>
132
/// 向已连接的XFE控制台输出一条消息
133
/// </summary>
134
/// <param name="text">文本</param>
135
/// <returns></returns>
136
public static async Task Write(string? text)
137
{
138
if (text is not null)
139
{
140
if (ShowInDebug)
141
Debug.WriteLine(text);
142
if (ShowInLocalConsole)
143
Console.WriteLine(text);
144
foreach (var client in ClientList)
145
await client.OutputMessage(text, false);
146
}
147
}
148
}
@@ -0,0 +1,30 @@
1
using XFEExtension.NetCore.CyberComm;
2
3
namespace XFEExtension.NetCore.XFEConsole;
4
5
/// <summary>
6
/// XFE控制台客户端信息
7
/// </summary>
8
/// <param name="clientName">客户端名称</param>
9
/// <param name="clientUUID">客户端唯一标识符</param>
10
/// <param name="password">客户端连接时密码</param>
11
/// <param name="eventArgs">事件参数</param>
12
public class XFEConsoleClientInfo(string clientName, string clientUUID, string password, CyberCommServerEventArgs eventArgs)
13
{
14
/// <summary>
15
/// 客户端名称
16
/// </summary>
17
public string ClientName { get; set; } = clientName;
18
/// <summary>
19
/// 客户端唯一标识符
20
/// </summary>
21
public string ClientUUID { get; set; } = clientUUID;
22
/// <summary>
23
/// 客户端密码
24
/// </summary>
25
public string Password { get; set; } = password;
26
/// <summary>
27
/// 事件参数
28
/// </summary>
29
public CyberCommServerEventArgs EventArgs { get; set; } = eventArgs;
30
}
@@ -0,0 +1,66 @@
1
using XFEExtension.NetCore.CyberComm;
2
using XFEExtension.NetCore.FormatExtension;
3
using XFEExtension.NetCore.TaskExtension;
4
5
namespace XFEExtension.NetCore.XFEConsole;
6
7
/// <summary>
8
/// XFE控制台程序客户端
9
/// </summary>
10
/// <remarks>
11
/// 默认构造函数
12
/// </remarks>
13
/// <param name="ipAddress">控制台输出端IP地址</param>
14
/// <param name="clientName">客户端名称</param>
15
/// <param name="password">密码</param>
16
/// <param name="clientID">客户端ID</param>
17
public class XFEConsoleProgramClient(string ipAddress, string clientName, string password, string clientID)
18
{
19
private event EndTaskTrigger<bool>? ConnectTrigger;
20
/// <summary>
21
/// 客户端
22
/// </summary>
23
public CyberCommClient Client { get; set; } = new CyberCommClient(ipAddress);
24
/// <summary>
25
/// 客户端名称
26
/// </summary>
27
public string ClientName { get; set; } = clientName;
28
/// <summary>
29
/// 连接时密码
30
/// </summary>
31
public string Password { get; set; } = password;
32
/// <summary>
33
/// 客户端ID
34
/// </summary>
35
public string ClientID { get; set; } = clientID;
36
/// <summary>
37
/// 连接服务器
38
/// </summary>
39
/// <returns></returns>
40
public async Task<bool> Connect()
41
{
42
Client.Connected += Client_Connected;
43
_ = Client.StartCyberCommClient();
44
Client.ClientWebSocket!.Options.SetRequestHeader(nameof(ClientName), ClientName);
45
Client.ClientWebSocket!.Options.SetRequestHeader(nameof(Password), Password);
46
Client.ClientWebSocket!.Options.SetRequestHeader(nameof(ClientID), ClientID);
47
if (Client.IsConnected)
48
return true;
49
else
50
return await new XFEWaitTask<bool>(ref ConnectTrigger!);
51
}
52
53
private void Client_Connected(object? sender, EventArgs e) => ConnectTrigger?.Invoke(true);
54
55
/// <summary>
56
/// 输出控制台消息
57
/// </summary>
58
/// <param name="message">消息文本</param>
59
/// <param name="isLine">是否是一整行</param>
60
/// <returns></returns>
61
public async Task OutputMessage(string message, bool isLine)
62
{
63
if (Client.IsConnected)
64
await Client.SendTextMessage(new XFEDictionary("IsLine", isLine ? "true" : "false", "Text", message));
65
}
66
}
@@ -0,0 +1,8 @@
1
namespace XFEExtension.NetCore.XFEConsole;
2
3
/// <summary>
4
/// XFE控制台进程服务器
5
/// </summary>
6
public class XFEConsoleProgramServer
7
{
8
}
@@ -0,0 +1,8 @@
1
namespace XFEExtension.NetCore.XFEConsole;
2
3
/// <summary>
4
/// XFE控制台终端客户端
5
/// </summary>
6
public class XFEConsoleTerminalClient
7
{
8
}
@@ -0,0 +1,150 @@
1
using System.Net.WebSockets;
2
using XFEExtension.NetCore.CyberComm;
3
using XFEExtension.NetCore.DelegateExtension;
4
using XFEToolBox.Views.Pages;
5
6
namespace XFEExtension.NetCore.XFEConsole;
7
8
/// <summary>
9
/// XFE控制台终端服务器
10
/// </summary>
11
public class XFEConsoleTerminalServer
12
{
13
/// <summary>
14
/// 服务器
15
/// </summary>
16
public CyberCommServer Server { get; set; }
17
/// <summary>
18
/// 密码
19
/// </summary>
20
public string Password { get; set; }
21
/// <summary>
22
/// Socket客户端-客户端信息字典
23
/// </summary>
24
public Dictionary<WebSocket, XFEConsoleClientInfo> ClientInfoDictionary { get; set; } = [];
25
/// <summary>
26
/// 客户端连接事件
27
/// </summary>
28
public event XFEEventHandler<XFEConsoleTerminalServer, XFEConsoleClientInfo>? Connected;
29
/// <summary>
30
/// 客户端断开连接事件
31
/// </summary>
32
public event XFEEventHandler<XFEConsoleTerminalServer, XFEConsoleClientInfo>? Disconnected;
33
/// <summary>
34
/// 接收到客户端消息触发
35
/// </summary>
36
public event XFEEventHandler<XFEConsoleClientInfo, string>? MessageReceived;
37
/// <summary>
38
/// 发生错误
39
/// </summary>
40
public event XFEEventHandler<XFEConsoleClientInfo, Exception>? ErrorOccurred;
41
/// <summary>
42
/// 服务器启动事件
43
/// </summary>
44
public event XFEEventHandler<XFEConsoleTerminalServer>? ServerStarted;
45
/// <summary>
46
/// XFE控制台终端服务器
47
/// </summary>
48
/// <param name="port">端口号</param>
49
/// <param name="localOnly">是否只在本地开启</param>
50
/// <param name="password">密码(默认为空)</param>
51
public XFEConsoleTerminalServer(int port, bool localOnly = true, string password = "")
52
{
53
Password = password;
54
if (localOnly)
55
Server = new CyberCommServer($"http://localhost:{port}/");
56
else
57
Server = new CyberCommServer(port);
58
Server.ServerStarted += Server_ServerStarted;
59
Server.ClientConnected += Server_ClientConnected;
60
Server.ConnectionClosed += Server_ConnectionClosed;
61
Server.MessageReceived += Server_MessageReceived;
62
}
63
/// <summary>
64
/// XFE控制台终端服务器
65
/// </summary>
66
/// <param name="ipAddress">IP地址</param>
67
/// <param name="password">密码(默认为空)</param>
68
public XFEConsoleTerminalServer(string[] ipAddress, string password = "")
69
{
70
Password = password;
71
Server = new CyberCommServer(ipAddress);
72
Server.ServerStarted += Server_ServerStarted;
73
Server.ClientConnected += Server_ClientConnected;
74
Server.ConnectionClosed += Server_ConnectionClosed;
75
Server.MessageReceived += Server_MessageReceived;
76
}
77
78
private void Server_MessageReceived(object? sender, CyberCommServerEventArgs e)
79
{
80
switch (e.MessageType)
81
{
82
case BackMessageType.Text:
83
MessageReceived?.Invoke(ClientInfoDictionary[e.CurrentWebSocket], e.TextMessage!);
84
break;
85
case BackMessageType.Binary:
86
break;
87
case BackMessageType.Error:
88
ErrorOccurred?.Invoke(ClientInfoDictionary[e.CurrentWebSocket], e.Exception!);
89
break;
90
default:
91
break;
92
}
93
}
94
95
private void Server_ConnectionClosed(object? sender, CyberCommServerEventArgs e)
96
{
97
if (ClientInfoDictionary.TryGetValue(e.CurrentWebSocket, out XFEConsoleClientInfo? clientInfo))
98
{
99
ClientInfoDictionary.Remove(e.CurrentWebSocket);
100
Disconnected?.Invoke(this, clientInfo);
101
}
102
}
103
104
private async void Server_ClientConnected(object? sender, CyberCommServerEventArgs e)
105
{
106
try
107
{
108
if (e.WSHeader["ClientName"] is not null && e.WSHeader["ClientID"] is not null && e.WSHeader["Password"] is not null)
109
{
110
var password = e.WSHeader["Password"]!;
111
var clientName = e.WSHeader["ClientName"]!;
112
var clientUUID = e.WSHeader["ClientID"]!;
113
if (password == Password)
114
{
115
var clientInfo = new XFEConsoleClientInfo(clientName, clientUUID, password, e);
116
ClientInfoDictionary.Add(e.CurrentWebSocket, clientInfo);
117
Connected?.Invoke(this, clientInfo);
118
}
119
return;
120
}
121
}
122
catch { }
123
try
124
{
125
await e.Close();
126
}
127
catch
128
{
129
try
130
{
131
e.ForceClose();
132
}
133
catch { }
134
}
135
}
136
137
private void Server_ServerStarted(object? sender, EventArgs e)
138
{
139
ClientInfoDictionary = [];
140
ServerStarted?.Invoke(this);
141
}
142
/// <summary>
143
/// 启动服务器
144
/// </summary>
145
/// <returns></returns>
146
public async Task StartServer()
147
{
148
await Server.StartCyberCommServer();
149
}
150
}
@@ -0,0 +1,305 @@
1
using System.Diagnostics.CodeAnalysis;
2
using System.IO;
3
using System.Text;
4
5
namespace XFEExtension.NetCore.XFEConsole;
6
7
/// <summary>
8
/// XFE控制台文本写入器
9
/// </summary>
10
public class XFEConsoleTextWriter : TextWriter
11
{
12
/// <inheritdoc/>
13
public override Encoding Encoding => Encoding.UTF8;
14
15
/// <inheritdoc/>
16
public override async void Write(bool value)
17
{
18
base.Write(value);
19
await XFEConsole.Write(value.ToString());
20
}
21
22
/// <inheritdoc/>
23
public override async void Write(char value)
24
{
25
base.Write(value);
26
await XFEConsole.Write(value.ToString());
27
}
28
29
/// <inheritdoc/>
30
public override async void Write(char[]? buffer)
31
{
32
base.Write(buffer);
33
if(buffer is not null)
34
{
35
var stringBuilder = new StringBuilder();
36
foreach (var sigChar in buffer)
37
{
38
stringBuilder.Append(sigChar);
39
}
40
await XFEConsole.Write(stringBuilder.ToString());
41
}
42
}
43
44
/// <inheritdoc/>
45
public override async void Write(char[] buffer, int index, int count)
46
{
47
base.Write(buffer, index, count);
48
for (int i = index; i < count; i++)
49
{
50
var stringBuilder = new StringBuilder();
51
stringBuilder.Append(buffer[i]);
52
await XFEConsole.Write(stringBuilder.ToString());
53
}
54
}
55
56
/// <inheritdoc/>
57
public override async void Write(decimal value)
58
{
59
base.Write(value);
60
await XFEConsole.Write(value.ToString());
61
}
62
63
/// <inheritdoc/>
64
public override async void Write(double value)
65
{
66
base.Write(value);
67
await XFEConsole.Write(value.ToString());
68
}
69
70
/// <inheritdoc/>
71
public override async void Write(int value)
72
{
73
base.Write(value);
74
await XFEConsole.Write(value.ToString());
75
}
76
77
/// <inheritdoc/>
78
public override async void Write(long value)
79
{
80
base.Write(value);
81
await XFEConsole.Write(value.ToString());
82
}
83
84
/// <inheritdoc/>
85
public override async void Write(object? value)
86
{
87
base.Write(value);
88
await XFEConsole.Write(value?.ToString());
89
}
90
91
/// <inheritdoc/>
92
public override async void Write(float value)
93
{
94
base.Write(value);
95
await XFEConsole.Write(value.ToString());
96
}
97
98
/// <inheritdoc/>
99
public override async void Write(string? value)
100
{
101
base.Write(value);
102
await XFEConsole.Write(value);
103
}
104
105
/// <inheritdoc/>
106
public override async void Write(StringBuilder? value)
107
{
108
base.Write(value);
109
await XFEConsole.Write(value?.ToString());
110
}
111
112
/// <inheritdoc/>
113
public override async void Write(uint value)
114
{
115
base.Write(value);
116
await XFEConsole.Write(value.ToString());
117
}
118
119
/// <inheritdoc/>
120
public override async void Write(ulong value)
121
{
122
base.Write(value);
123
await XFEConsole.Write(value.ToString());
124
}
125
126
/// <inheritdoc/>
127
public override async Task WriteAsync(char value)
128
{
129
await base.WriteAsync(value);
130
await XFEConsole.Write(value.ToString());
131
}
132
133
/// <inheritdoc/>
134
public override async Task WriteAsync(char[] buffer, int index, int count)
135
{
136
await base.WriteAsync(buffer, index, count);
137
for (int i = index; i < count; i++)
138
{
139
var stringBuilder = new StringBuilder();
140
stringBuilder.Append(buffer[i]);
141
await XFEConsole.Write(stringBuilder.ToString());
142
}
143
}
144
145
/// <inheritdoc/>
146
public override async Task WriteAsync(string? value)
147
{
148
await base.WriteAsync(value);
149
await XFEConsole.Write(value);
150
}
151
152
/// <inheritdoc/>
153
public override async void WriteLine()
154
{
155
base.WriteLine();
156
await XFEConsole.WriteLine("");
157
}
158
159
/// <inheritdoc/>
160
public override async void WriteLine(bool value)
161
{
162
base.WriteLine(value);
163
await XFEConsole.WriteLine(value.ToString());
164
}
165
166
/// <inheritdoc/>
167
public override async void WriteLine(char value)
168
{
169
base.WriteLine(value);
170
await XFEConsole.WriteLine(value.ToString());
171
}
172
173
/// <inheritdoc/>
174
public override async void WriteLine(char[]? buffer)
175
{
176
base.WriteLine(buffer);
177
if (buffer is not null)
178
{
179
var stringBuilder = new StringBuilder();
180
foreach (var sigChar in buffer)
181
{
182
stringBuilder.Append(sigChar);
183
}
184
await XFEConsole.WriteLine(stringBuilder.ToString());
185
}
186
}
187
188
/// <inheritdoc/>
189
public override async void WriteLine(char[] buffer, int index, int count)
190
{
191
base.WriteLine(buffer, index, count);
192
for (int i = index; i < count; i++)
193
{
194
var stringBuilder = new StringBuilder();
195
stringBuilder.Append(buffer[i]);
196
await XFEConsole.WriteLine(stringBuilder.ToString());
197
}
198
}
199
200
/// <inheritdoc/>
201
public override async void WriteLine(decimal value)
202
{
203
base.WriteLine(value);
204
await XFEConsole.WriteLine(value.ToString());
205
}
206
207
/// <inheritdoc/>
208
public override async void WriteLine(double value)
209
{
210
base.WriteLine(value);
211
await XFEConsole.WriteLine(value.ToString());
212
}
213
214
/// <inheritdoc/>
215
public override async void WriteLine(int value)
216
{
217
base.WriteLine(value);
218
await XFEConsole.WriteLine(value.ToString());
219
}
220
221
/// <inheritdoc/>
222
public override async void WriteLine(long value)
223
{
224
base.WriteLine(value);
225
await XFEConsole.WriteLine(value.ToString());
226
}
227
228
/// <inheritdoc/>
229
public override async void WriteLine(object? value)
230
{
231
base.WriteLine(value);
232
if(XFEConsole.AutoAnalyzeObject)
233
await XFEConsole.WriteObject(value);
234
else
235
await XFEConsole.WriteLine(value?.ToString());
236
}
237
238
/// <inheritdoc/>
239
public override async void WriteLine(float value)
240
{
241
base.WriteLine(value);
242
await XFEConsole.WriteLine(value.ToString());
243
}
244
245
/// <inheritdoc/>
246
public override async void WriteLine(string? value)
247
{
248
base.WriteLine(value);
249
await XFEConsole.WriteLine(value);
250
}
251
252
/// <inheritdoc/>
253
public override async void WriteLine(StringBuilder? value)
254
{
255
base.WriteLine(value);
256
await XFEConsole.WriteLine(value?.ToString());
257
}
258
259
/// <inheritdoc/>
260
public override async void WriteLine(uint value)
261
{
262
base.WriteLine(value);
263
await XFEConsole.WriteLine(value.ToString());
264
}
265
266
/// <inheritdoc/>
267
public override async void WriteLine(ulong value)
268
{
269
base.WriteLine(value);
270
await XFEConsole.WriteLine(value.ToString());
271
}
272
273
/// <inheritdoc/>
274
public override async Task WriteLineAsync()
275
{
276
await base.WriteLineAsync();
277
await XFEConsole.WriteLine("");
278
}
279
280
/// <inheritdoc/>
281
public override async Task WriteLineAsync(char value)
282
{
283
await base.WriteLineAsync(value);
284
await XFEConsole.WriteLine(value.ToString());
285
}
286
287
/// <inheritdoc/>
288
public override async Task WriteLineAsync(char[] buffer, int index, int count)
289
{
290
await base.WriteLineAsync(buffer, index, count);
291
for (int i = index; i < count; i++)
292
{
293
var stringBuilder = new StringBuilder();
294
stringBuilder.Append(buffer[i]);
295
await XFEConsole.WriteLine(stringBuilder.ToString());
296
}
297
}
298
299
/// <inheritdoc/>
300
public override async Task WriteLineAsync(string? value)
301
{
302
await base.WriteLineAsync(value);
303
await XFEConsole.WriteLine(value);
304
}
305
}
@@ -0,0 +1,15 @@
1
using XFEExtension.NetCore.AutoConfig;
2
using XFEToolBox.Core.Model;
3
4
namespace XFEToolBox.Profiles.CrossVersionProfiles;
5
6
public partial class ConsoleProfile
7
{
8
[ProfileProperty]
9
private int consolePort = 3280;
10
[ProfileProperty]
11
private string consolePassword = "";
12
[ProfileProperty]
13
private bool localOnly = true;
14
public ConsoleProfile() => ProfilePath = @$"{AppPath.LocalProfile}\{typeof(ConsoleProfile)}.xfe";
15
}
@@ -1,12 +1,19 @@
1
using XFEToolBox.Core.Model;
1
using XFEExtension.NetCore.AutoConfig;
2
using XFEToolBox.Core.Model;
2
3
3
4
namespace XFEToolBox.Profiles.CrossVersionProfiles;
4
5
5
6
public partial class SystemProfile
6
7
{
8
[ProfileProperty]
9
private double currentWindowDPIScale = 1.0;
10
[ProfileProperty]
11
private double mainWindowWidth = 720;
12
[ProfileProperty]
13
private double mainWindowHeight = 450;
14
public SystemProfile() => ProfilePath = @$"{AppPath.LocalProfile}\{typeof(SystemProfile)}.xfe";
7
15
/// <summary>
8
16
/// 工具箱现在是否可以被关闭
9
17
/// </summary>
10
18
public static bool CanClosed { get; set; }
11
//public SystemProfile() => ProfilePath = @$"{AppPath.LocalProfile}\{typeof(SystemProfile)}.xfe";
12
19
}
二进制文件已变更,无法进行逐行预览。
二进制文件已变更,无法进行逐行预览。
二进制文件已变更,无法进行逐行预览。
二进制文件已变更,无法进行逐行预览。
二进制文件已变更,无法进行逐行预览。
二进制文件已变更,无法进行逐行预览。
@@ -35,6 +35,45 @@
35
35
</EventTrigger>
36
36
</Style.Triggers>
37
37
</Style>
38
<Style x:Key="CornerBorderStyle" TargetType="Border">
39
<Setter Property="Background" Value="White"/>
40
<Setter Property="Opacity" Value="0"/>
41
<Setter Property="Width" Value="25"/>
42
<Setter Property="Height" Value="25"/>
43
<Setter Property="CornerRadius" Value="30,0,10,0"/>
44
<Setter Property="HorizontalAlignment" Value="Right"/>
45
<Setter Property="VerticalAlignment" Value="Bottom"/>
46
<Style.Triggers>
47
<EventTrigger RoutedEvent="MouseEnter">
48
<BeginStoryboard>
49
<Storyboard>
50
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0.25" Duration="0:0:0.1"/>
51
</Storyboard>
52
</BeginStoryboard>
53
</EventTrigger>
54
<EventTrigger RoutedEvent="MouseLeave">
55
<BeginStoryboard>
56
<Storyboard>
57
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:0.1"/>
58
</Storyboard>
59
</BeginStoryboard>
60
</EventTrigger>
61
<EventTrigger RoutedEvent="MouseLeftButtonDown">
62
<BeginStoryboard>
63
<Storyboard>
64
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0.5" Duration="0:0:0.1"/>
65
</Storyboard>
66
</BeginStoryboard>
67
</EventTrigger>
68
<EventTrigger RoutedEvent="MouseLeftButtonUp">
69
<BeginStoryboard>
70
<Storyboard>
71
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0.25" Duration="0:0:0.1"/>
72
</Storyboard>
73
</BeginStoryboard>
74
</EventTrigger>
75
</Style.Triggers>
76
</Style>
38
77
<Style x:Key="HalfRotorImage" TargetType="Image">
39
78
<Setter Property="RenderTransformOrigin" Value="0.5,0.5"/>
40
79
<Setter Property="RenderTransform">
@@ -112,22 +151,24 @@
112
151
<Setter Property="Template">
113
152
<Setter.Value>
114
153
<ControlTemplate TargetType="RadioButton">
115
<Border x:Name="mainBorder" CornerRadius="15" HorizontalAlignment="Stretch" Height="40" Background="White" BorderBrush="#9898e7" BorderThickness="1.5" RenderTransformOrigin="0.5,0.5">
116
<Border.RenderTransform>
117
<ScaleTransform x:Name="mainBorderScale" ScaleX="1" ScaleY="1"/>
118
</Border.RenderTransform>
119
<Border.Effect>
120
<DropShadowEffect x:Name="mainBorderShadow" Color="#9898e7" BlurRadius="8" ShadowDepth="3" Direction="-45" Opacity="0"/>
121
</Border.Effect>
122
<Grid>
123
<Grid.ColumnDefinitions>
124
<ColumnDefinition Width="40"/>
125
<ColumnDefinition Width="10"/>
126
<ColumnDefinition Width="*"/>
127
</Grid.ColumnDefinitions>
128
<Border x:Name="borderSplit" Grid.Column="1" Background="#9898e7" CornerRadius="2" Width="3" Opacity="0.5" HorizontalAlignment="Left" VerticalAlignment="Stretch" Margin="0,5"/>
129
<ContentPresenter Grid.ColumnSpan="3"/>
130
</Grid>
154
<Border x:Name="outBorder" Height="40" Background="White">
155
<Border x:Name="mainBorder" CornerRadius="15" Height="40" HorizontalAlignment="Stretch" Background="White" BorderBrush="#9898e7" BorderThickness="1.5" RenderTransformOrigin="0.5,0.5">
156
<Border.RenderTransform>
157
<ScaleTransform x:Name="mainBorderScale" ScaleX="1" ScaleY="1"/>
158
</Border.RenderTransform>
159
<Border.Effect>
160
<DropShadowEffect x:Name="mainBorderShadow" Color="#9898e7" BlurRadius="8" ShadowDepth="3" Direction="-45" Opacity="0"/>
161
</Border.Effect>
162
<Grid>
163
<Grid.ColumnDefinitions>
164
<ColumnDefinition Width="40"/>
165
<ColumnDefinition Width="10"/>
166
<ColumnDefinition Width="*"/>
167
</Grid.ColumnDefinitions>
168
<Border x:Name="borderSplit" Grid.Column="1" Background="#9898e7" CornerRadius="2" Width="3" Opacity="0.5" HorizontalAlignment="Left" VerticalAlignment="Stretch" Margin="0,5"/>
169
<ContentPresenter Grid.ColumnSpan="3"/>
170
</Grid>
171
</Border>
131
172
</Border>
132
173
<ControlTemplate.Triggers>
133
174
<EventTrigger RoutedEvent="MouseEnter">
@@ -143,11 +184,11 @@
143
184
<CubicEase EasingMode="EaseOut"/>
144
185
</DoubleAnimation.EasingFunction>
145
186
</DoubleAnimation>
146
<ThicknessAnimation Storyboard.TargetName="mainBorder" Storyboard.TargetProperty="Margin" From="0,0" To="0,10" Duration="0:0:0.3">
147
<ThicknessAnimation.EasingFunction>
187
<DoubleAnimation Storyboard.TargetName="outBorder" Storyboard.TargetProperty="Height" From="40" To="60" Duration="0:0:0.3">
188
<DoubleAnimation.EasingFunction>
148
189
<CubicEase EasingMode="EaseOut"/>
149
</ThicknessAnimation.EasingFunction>
150
</ThicknessAnimation>
190
</DoubleAnimation.EasingFunction>
191
</DoubleAnimation>
151
192
</Storyboard>
152
193
</BeginStoryboard>
153
194
</EventTrigger>
@@ -164,11 +205,11 @@
164
205
<CubicEase EasingMode="EaseOut"/>
165
206
</DoubleAnimation.EasingFunction>
166
207
</DoubleAnimation>
167
<ThicknessAnimation Storyboard.TargetName="mainBorder" Storyboard.TargetProperty="Margin" From="0,10" To="0,0" Duration="0:0:0.3">
168
<ThicknessAnimation.EasingFunction>
208
<DoubleAnimation Storyboard.TargetName="outBorder" Storyboard.TargetProperty="Height" From="60" To="40" Duration="0:0:0.3">
209
<DoubleAnimation.EasingFunction>
169
210
<CubicEase EasingMode="EaseOut"/>
170
</ThicknessAnimation.EasingFunction>
171
</ThicknessAnimation>
211
</DoubleAnimation.EasingFunction>
212
</DoubleAnimation>
172
213
</Storyboard>
173
214
</BeginStoryboard>
174
215
</EventTrigger>
@@ -210,9 +251,93 @@
210
251
</Setter>
211
252
</Style>
212
253
<Style x:Key="NavigationFrame" TargetType="Frame">
213
<Setter Property="Margin" Value="10,0,0,0"/>
214
254
<Setter Property="NavigationUIVisibility" Value="Hidden"/>
215
255
<Setter Property="Grid.Column" Value="1"/>
216
256
<Setter Property="Grid.Row" Value="1"/>
217
257
</Style>
258
<Style x:Key="ConsoleScrollBar" TargetType="ScrollBar">
259
<Setter Property="Template">
260
<Setter.Value>
261
<ControlTemplate TargetType="ScrollBar">
262
<Track x:Name="PART_Track" IsDirectionReversed="True" Width="8" HorizontalAlignment="Right">
263
<Track.Thumb>
264
<Thumb x:Name="Thumb" Width="8">
265
<Thumb.Template>
266
<ControlTemplate TargetType="Thumb">
267
<Grid>
268
<Grid.RowDefinitions>
269
<RowDefinition Height="Auto"/>
270
<RowDefinition Height="*"/>
271
<RowDefinition Height="Auto"/>
272
</Grid.RowDefinitions>
273
<TextBlock Text="▲" Foreground="#9898e7" VerticalAlignment="Center" HorizontalAlignment="Center"/>
274
<Border Grid.Row="1" Background="#9898e7" CornerRadius="5"/>
275
<TextBlock Grid.Row="2" Text="▼" Foreground="#9898e7" VerticalAlignment="Center" HorizontalAlignment="Center"/>
276
</Grid>
277
</ControlTemplate>
278
</Thumb.Template>
279
</Thumb>
280
</Track.Thumb>
281
<Track.DecreaseRepeatButton>
282
<RepeatButton Width="1" Background="#9898e7" BorderThickness="0"/>
283
</Track.DecreaseRepeatButton>
284
<Track.IncreaseRepeatButton>
285
<RepeatButton Width="1" Background="#9898e7" BorderThickness="0"/>
286
</Track.IncreaseRepeatButton>
287
</Track>
288
</ControlTemplate>
289
</Setter.Value>
290
</Setter>
291
</Style>
292
<Style x:Key="ConsoleButton" TargetType="Button">
293
<Style.Setters>
294
<Setter Property="Opacity" Value="1"/>
295
<Setter Property="Template">
296
<Setter.Value>
297
<ControlTemplate TargetType="Button">
298
<Grid>
299
<Border x:Name="border" CornerRadius="5" Background="White" Opacity="0.1">
300
</Border>
301
<ContentPresenter Margin="5,3"/>
302
</Grid>
303
<ControlTemplate.Triggers>
304
<EventTrigger RoutedEvent="MouseEnter">
305
<BeginStoryboard>
306
<Storyboard>
307
<DoubleAnimation Storyboard.TargetName="border" Storyboard.TargetProperty="Opacity" To="0.5" Duration="0:0:0.1"/>
308
</Storyboard>
309
</BeginStoryboard>
310
</EventTrigger>
311
<EventTrigger RoutedEvent="MouseLeave">
312
<BeginStoryboard>
313
<Storyboard>
314
<DoubleAnimation Storyboard.TargetName="border" Storyboard.TargetProperty="Opacity" To="0.1" Duration="0:0:0.1"/>
315
</Storyboard>
316
</BeginStoryboard>
317
</EventTrigger>
318
<EventTrigger RoutedEvent="MouseLeftButtonDown">
319
<BeginStoryboard>
320
<Storyboard>
321
<DoubleAnimation Storyboard.TargetName="border" Storyboard.TargetProperty="Opacity" To="0.3" Duration="0:0:0.1"/>
322
</Storyboard>
323
</BeginStoryboard>
324
</EventTrigger>
325
<EventTrigger RoutedEvent="MouseLeftButtonUp">
326
<BeginStoryboard>
327
<Storyboard>
328
<DoubleAnimation Storyboard.TargetName="border" Storyboard.TargetProperty="Opacity" To="0.5" Duration="0:0:0.1"/>
329
</Storyboard>
330
</BeginStoryboard>
331
</EventTrigger>
332
</ControlTemplate.Triggers>
333
</ControlTemplate>
334
</Setter.Value>
335
</Setter>
336
</Style.Setters>
337
<Style.Triggers>
338
<Trigger Property="IsEnabled" Value="False">
339
<Setter Property="Opacity" Value="0.5"/>
340
</Trigger>
341
</Style.Triggers>
342
</Style>
218
343
</ResourceDictionary>
@@ -0,0 +1,29 @@
1
using System.Windows.Media;
2
3
namespace XFEToolBox.Utilities;
4
5
public class DecTextSpan
6
{
7
public bool IsHyperLink { get; set; }
8
public string Text { get; set; }
9
public string? Link { get; set; }
10
public Color Color { get; set; }
11
public Color BackgroundColor { get; set; }
12
13
public DecTextSpan(string text, Color color, Color backgroundColor)
14
{
15
IsHyperLink = false;
16
Text = text;
17
Color = color;
18
BackgroundColor = backgroundColor;
19
}
20
21
public DecTextSpan(string text, string link, Color color, Color backgroundColor)
22
{
23
IsHyperLink= true;
24
Text = text;
25
Link = link;
26
Color = color;
27
BackgroundColor = backgroundColor;
28
}
29
}
@@ -0,0 +1,109 @@
1
using System.Diagnostics;
2
using System.Text.RegularExpressions;
3
using System.Windows.Controls;
4
using System.Windows.Documents;
5
using System.Windows.Media;
6
7
namespace XFEToolBox.Utilities;
8
9
public partial class DecoratedTextConverter
10
{
11
[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>.+)))\]")]
12
public static partial Regex DecorationRegex();
13
14
public static TextBlock ConvertToTextBlock(string decoratedText, Color defaultColor)
15
{
16
var results = ConvertToInlineList(decoratedText, defaultColor);
17
var textBlock = new TextBlock();
18
textBlock.Inlines.AddRange(results);
19
return textBlock;
20
}
21
public static List<Inline> ConvertToInlineList(string decoratedText, Color defaultColor)
22
{
23
var results = ConvertText(decoratedText, defaultColor);
24
var inLineList = new List<Inline>();
25
if (results.Count > 0)
26
{
27
foreach (var result in results)
28
{
29
if (result.IsHyperLink)
30
{
31
var hyperLink = new Hyperlink(new Run(result.Text))
32
{
33
Foreground = new SolidColorBrush(result.Color),
34
NavigateUri = new Uri(result.Link!),
35
Background = new SolidColorBrush(result.BackgroundColor)
36
};
37
hyperLink.RequestNavigate += (sender, e) =>
38
{
39
try { Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri) { UseShellExecute = true }); } catch { }
40
};
41
inLineList.Add(hyperLink);
42
}
43
else
44
{
45
inLineList.Add(new Span(new Run(result.Text))
46
{
47
Foreground = new SolidColorBrush(result.Color),
48
Background = new SolidColorBrush(result.BackgroundColor)
49
});
50
}
51
}
52
}
53
else
54
{
55
inLineList.Add(new Span(new Run(decoratedText))
56
{
57
Foreground = Brushes.White
58
});
59
}
60
return inLineList;
61
}
62
public static List<DecTextSpan> ConvertText(string decoratedText, Color defaultColor)
63
{
64
var textSpanList = new List<DecTextSpan>();
65
var matches = DecorationRegex().Matches(decoratedText);
66
for (int i = 0; i < matches.Count; i++)
67
{
68
var match = matches[i];
69
var nextMatch = i + 1 < matches.Count ? matches[i + 1] : null;
70
var type = match.Groups["type"].Value;
71
var colorText = match.Groups["color"].Value;
72
var backgroundColorText = match.Groups["background"].Value ?? "Transparent";
73
if (backgroundColorText == string.Empty)
74
backgroundColorText = "Transparent";
75
Color backgroundColor = (Color)ColorConverter.ConvertFromString(backgroundColorText);
76
var link = match.Groups["link"].Value;
77
var text = match.Groups["text"].Value;
78
var unMatchString = "";
79
if (i == 0 && match.Index != 0)
80
{
81
textSpanList.Add(new(decoratedText[..match.Index], defaultColor, Colors.Transparent));
82
}
83
if (nextMatch is null)
84
{
85
unMatchString = decoratedText.Substring(match.Index + match.Length, decoratedText.Length - match.Index - match.Length);
86
}
87
else
88
{
89
var unMatchStringLength = nextMatch.Index - match.Index - match.Length;
90
if (unMatchStringLength > 0)
91
unMatchString = decoratedText.Substring(match.Index + match.Length, unMatchStringLength);
92
}
93
switch (type)
94
{
95
case "color":
96
textSpanList.Add(new(unMatchString, (Color)ColorConverter.ConvertFromString(colorText)!, backgroundColor));
97
continue;
98
case "hyperlink":
99
var color = ColorConverter.ConvertFromString(colorText);
100
textSpanList.Add(new(text, link, color is null ? defaultColor : (Color)color, backgroundColor));
101
textSpanList.Add(new(unMatchString, defaultColor, Colors.Transparent));
102
continue;
103
default:
104
continue;
105
}
106
}
107
return textSpanList;
108
}
109
}
@@ -0,0 +1,209 @@
1
using CommunityToolkit.Mvvm.ComponentModel;
2
using CommunityToolkit.Mvvm.Input;
3
using System.Diagnostics;
4
using System.Net;
5
using System.Windows.Controls;
6
using System.Windows.Media;
7
using XFEExtension.NetCore.FormatExtension;
8
using XFEExtension.NetCore.XFEConsole;
9
using XFEToolBox.Profiles.CrossVersionProfiles;
10
using XFEToolBox.Utilities;
11
using XFEToolBox.Views.Pages;
12
13
namespace XFEToolBox.ViewModel;
14
15
public partial class ConsolePageViewModel : ObservableObject
16
{
17
[ObservableProperty]
18
private bool canStartServer = true;
19
[ObservableProperty]
20
private bool canStopServer = false;
21
[ObservableProperty]
22
private bool canRestartServer = false;
23
[ObservableProperty]
24
private bool canCleanUp = false;
25
26
private bool lastLineIsEnd = true;
27
public ConsolePage ViewPage { get; set; }
28
public XFEConsoleTerminalServer? TerminalServer { get; set; }
29
30
public ConsolePageViewModel(ConsolePage viewPage)
31
{
32
ViewPage = viewPage;
33
}
34
35
public async Task StartConsoleServer()
36
{
37
TerminalServer = new XFEConsoleTerminalServer(ConsoleProfile.ConsolePort, ConsoleProfile.LocalOnly, ConsoleProfile.ConsolePassword);
38
TerminalServer.ServerStarted += TerminalServer_ServerStarted;
39
TerminalServer.Connected += TerminalServer_Connected;
40
TerminalServer.Disconnected += TerminalServer_Disconnected;
41
TerminalServer.MessageReceived += TerminalServer_MessageReceived;
42
TerminalServer.ErrorOccurred += TerminalServer_ErrorOccurred;
43
await TerminalServer.StartServer();
44
}
45
46
public void ShutDownServer()
47
{
48
try
49
{
50
TerminalServer?.Server.Server.Close();
51
}
52
catch { }
53
TerminalServer = null;
54
}
55
56
private void TerminalServer_ErrorOccurred(XFEConsoleClientInfo sender, Exception e)
57
{
58
var code = e.InnerException?.InnerException is HttpListenerException ? (e.InnerException.InnerException as HttpListenerException)!.ErrorCode : 0;
59
if (code != 995)
60
ShowMessageWithTimeLine($"[color Red]发生错误:{e.Message}");
61
}
62
63
private void TerminalServer_MessageReceived(XFEConsoleClientInfo sender, string e)
64
{
65
var dictionary = new XFEDictionary(e);
66
var isLine = dictionary["IsLine"] == "true";
67
var textMessage = dictionary["Text"];
68
if (lastLineIsEnd)
69
ShowMessageWithTimeLine($"{sender.ClientName}> {textMessage}", isLine);
70
else
71
ShowMessage($"{textMessage}", isLine);
72
}
73
74
private void TerminalServer_Disconnected(XFEConsoleTerminalServer sender, XFEConsoleClientInfo e)
75
{
76
ShowMessageWithTimeLine($"[color #9898e7]客户端断开连接 [color white]{e.ClientName}");
77
}
78
79
private void TerminalServer_Connected(XFEConsoleTerminalServer sender, XFEConsoleClientInfo e)
80
{
81
ShowMessageWithTimeLine($"[color #9898e7]客户端连接 [color white]{e.ClientName}");
82
}
83
84
private void TerminalServer_ServerStarted(XFEConsoleTerminalServer sender)
85
{
86
ShowMessageWithTimeLine($"控制台服务器已在端口:[hyperlink color: #05ff00 link: http://localhost:{ConsoleProfile.ConsolePort}/ text: {ConsoleProfile.ConsolePort}] 上运行");
87
}
88
/// <summary>
89
/// 显示消息
90
/// </summary>
91
/// <param name="message"></param>
92
/// <param name="defaultColor"></param>
93
public void ShowMessage(string message, Color defaultColor, bool isLineEnd, bool forceLine = false)
94
{
95
if (!CanCleanUp)
96
CanCleanUp = true;
97
if (!lastLineIsEnd && !forceLine)
98
{
99
var lastControl = ViewPage.consoleStackPanel.Children.Count > 0 ? ViewPage.consoleStackPanel.Children[^1] : null;
100
if (lastControl is TextBlock lastTextBlock)
101
{
102
var inLineList = DecoratedTextConverter.ConvertToInlineList(message, defaultColor);
103
lastTextBlock.Inlines.AddRange(inLineList);
104
lastLineIsEnd = isLineEnd;
105
return;
106
}
107
}
108
var textBlock = DecoratedTextConverter.ConvertToTextBlock(message, defaultColor);
109
textBlock.TextWrapping = System.Windows.TextWrapping.Wrap;
110
textBlock.FontSize = 13;
111
textBlock.FontFamily = new FontFamily("Consolas");
112
ViewPage.consoleStackPanel.Children.Add(textBlock);
113
lastLineIsEnd = isLineEnd;
114
}
115
/// <summary>
116
/// 显示消息
117
/// </summary>
118
/// <param name="message"></param>
119
public void ShowMessage(string message, bool isLineEnd = true, bool forceLine = false) => ShowMessage(message, Colors.White, isLineEnd, forceLine);
120
/// <summary>
121
/// 显示附带时间轴的消息
122
/// </summary>
123
/// <param name="message"></param>
124
public void ShowMessageWithTimeLine(string message, bool isLineEnd = true) => ShowMessage($"[{DateTime.Now:HH:mm:ss}] {message}", isLineEnd, true);
125
[RelayCommand]
126
private void StartServer()
127
{
128
CanRestartServer = false;
129
CanStartServer = false;
130
try
131
{
132
ShowMessageWithTimeLine("[color yellow]正在启动服务器...");
133
_ = StartConsoleServer();
134
CanStopServer = true;
135
CanRestartServer = true;
136
}
137
catch (Exception ex)
138
{
139
ShowMessageWithTimeLine($"[color red]无法启动服务器:{ex}");
140
CanRestartServer = false;
141
CanStopServer = false;
142
CanStartServer = true;
143
}
144
}
145
[RelayCommand]
146
private void StopServer()
147
{
148
CanRestartServer = false;
149
CanStopServer = false;
150
try
151
{
152
ShutDownServer();
153
CanStartServer = true;
154
ShowMessageWithTimeLine("[color yellow]服务器已关闭");
155
}
156
catch (Exception ex)
157
{
158
ShowMessageWithTimeLine($"[color red]关闭服务器时出现错误:{ex}");
159
if (TerminalServer is not null && TerminalServer.Server.Server.IsListening)
160
{
161
CanRestartServer = true;
162
CanStopServer = true;
163
}
164
else
165
{
166
CanStartServer = true;
167
}
168
}
169
}
170
[RelayCommand]
171
private void RestartServer()
172
{
173
CanRestartServer = false;
174
CanStartServer = false;
175
CanStopServer = false;
176
try
177
{
178
ShutDownServer();
179
ShowMessageWithTimeLine("[color yellow]服务器已关闭");
180
_ = StartConsoleServer();
181
CanStartServer = false;
182
CanStopServer = true;
183
CanRestartServer = true;
184
ShowMessageWithTimeLine("[color yellow]服务器重启完成");
185
}
186
catch (Exception ex)
187
{
188
ShowMessageWithTimeLine($"[color red]重启服务器时出现错误:{ex}");
189
if (TerminalServer is not null && TerminalServer.Server.Server.IsListening)
190
{
191
CanRestartServer = true;
192
CanStopServer = true;
193
CanStartServer = false;
194
}
195
else
196
{
197
CanRestartServer = false;
198
CanStopServer = false;
199
CanStartServer = true;
200
}
201
}
202
}
203
[RelayCommand]
204
private void CleanUp()
205
{
206
CanCleanUp = false;
207
ViewPage.consoleStackPanel.Children.Clear();
208
}
209
}
@@ -2,6 +2,8 @@
2
2
using CommunityToolkit.Mvvm.Input;
3
3
using System.Windows;
4
4
using System.Windows.Controls;
5
using System.Windows.Interop;
6
using XFEExtension.NetCore.InputSimulator;
5
7
using XFEToolBox.Profiles.CrossVersionProfiles;
6
8
using XFEToolBox.Views.Pages;
7
9
using XFEToolBox.Views.Windows;
@@ -10,7 +12,11 @@ namespace XFEToolBox.ViewModel;
10
12
11
13
public partial class MainWindowViewModel : ObservableObject
12
14
{
13
public MainWindow? ViewPage { get; set; }
15
private bool isDragMouseDown = false;
16
private bool isDoubleClickMouseDown = false;
17
private int mouseOriginalX = 0;
18
private int mouseOriginalY = 0;
19
public MainWindow ViewPage { get; set; }
14
20
15
21
[ObservableProperty]
16
22
private Page? currentPage = MainPage.Current;
@@ -48,7 +54,83 @@ public partial class MainWindowViewModel : ObservableObject
48
54
ExitApp(true);
49
55
//TODO:1 待完善的退出应用逻辑,强制退出等
50
56
}
51
57
/// <summary>
58
/// 获取窗体DPI缩放
59
/// </summary>
60
public void GetDPIScale() => SystemProfile.CurrentWindowDPIScale = InputSimulator.GetScalingFactorForWindow(new WindowInteropHelper(ViewPage).Handle);
61
public void InitMousePosition()
62
{
63
var mousePosition = InputSimulator.GetMousePosition();
64
mouseOriginalX = mousePosition.X;
65
mouseOriginalY = mousePosition.Y;
66
}
67
/// <summary>
68
/// 初始化调整窗体参数
69
/// </summary>
70
public void InitializeToResize()
71
{
72
isDragMouseDown = true;
73
InitMousePosition();
74
_ = Task.Run(Resize);
75
}
76
/// <summary>
77
/// 调整窗体大小
78
/// </summary>
79
public void Resize()
80
{
81
while (isDragMouseDown && InputSimulator.GetMouseDown(MouseButton.Left))
82
{
83
var nowMousePoint = InputSimulator.GetMousePosition();
84
ViewPage.Dispatcher.Invoke(() =>
85
{
86
double newWidth = ViewPage.Width + (nowMousePoint.X - mouseOriginalX) / SystemProfile.CurrentWindowDPIScale;
87
double newHeight = ViewPage.Height + (nowMousePoint.Y - mouseOriginalY) / SystemProfile.CurrentWindowDPIScale;
88
if (newWidth > ViewPage.MinWidth)
89
ViewPage.Width = newWidth;
90
else
91
ViewPage.Width = ViewPage.MinWidth;
92
if (newHeight > ViewPage.MinHeight)
93
ViewPage.Height = newHeight;
94
else
95
ViewPage.Height = ViewPage.MinHeight;
96
});
97
mouseOriginalX = nowMousePoint.X;
98
mouseOriginalY = nowMousePoint.Y;
99
}
100
ViewPage.Dispatcher.Invoke(() =>
101
{
102
SystemProfile.MainWindowWidth = ViewPage.Width;
103
SystemProfile.MainWindowHeight = ViewPage.Height;
104
});
105
}
106
/// <summary>
107
/// 判断是否双击
108
/// </summary>
109
/// <param name="delay">间隔</param>
110
/// <returns></returns>
111
public bool CheckDoubleClick(int delay)
112
{
113
if (isDoubleClickMouseDown)
114
{
115
var nowMousePosition = InputSimulator.GetMousePosition();
116
if (mouseOriginalX == nowMousePosition.X && mouseOriginalY == nowMousePosition.Y)
117
return true;
118
else
119
return false;
120
}
121
else
122
{
123
isDoubleClickMouseDown = true;
124
InitMousePosition();
125
_ = Task.Run(async () =>
126
{
127
await Task.Delay(delay);
128
isDoubleClickMouseDown = false;
129
});
130
return false;
131
}
132
}
133
#region Command
52
134
[RelayCommand]
53
135
private void NavigateToPage(string pageTag)
54
136
{
@@ -63,6 +145,9 @@ public partial class MainWindowViewModel : ObservableObject
63
145
case "download":
64
146
CurrentPage = MainPage.Current;
65
147
break;
148
case "console":
149
CurrentPage = ConsolePage.Current;
150
break;
66
151
case "setting":
67
152
CurrentPage = SettingPage.Current;
68
153
break;
@@ -70,4 +155,5 @@ public partial class MainWindowViewModel : ObservableObject
70
155
break;
71
156
}
72
157
}
158
#endregion
73
159
}
@@ -0,0 +1,75 @@
1
<Page x:Class="XFEToolBox.Views.Pages.ConsolePage"
2
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
5
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
6
xmlns:local="clr-namespace:XFEToolBox.Views.Pages"
7
xmlns:viewmodel="clr-namespace:XFEToolBox.ViewModel"
8
mc:Ignorable="d"
9
d:DesignHeight="450" d:DesignWidth="800"
10
Title="ConsolePage" d:DataContext="{d:DesignInstance Type=viewmodel:ConsolePageViewModel}">
11
<Border CornerRadius="0,0,15,0" Background="#1e1e1e">
12
<Grid>
13
<Grid.ColumnDefinitions>
14
<ColumnDefinition Width="*"/>
15
</Grid.ColumnDefinitions>
16
<Grid.RowDefinitions>
17
<RowDefinition Height="30"/>
18
<RowDefinition Height="*"/>
19
</Grid.RowDefinitions>
20
<Grid>
21
<StackPanel Orientation="Horizontal">
22
<Button Style="{StaticResource ConsoleButton}" Margin="3" IsEnabled="{Binding CanStartServer}" Command="{Binding StartServerCommand}">
23
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
24
<Grid.ColumnDefinitions>
25
<ColumnDefinition Width="Auto"/>
26
<ColumnDefinition Width="*"/>
27
</Grid.ColumnDefinitions>
28
<Image Source="/Resources/Image/start.png" HorizontalAlignment="Center" VerticalAlignment="Center"/>
29
<TextBlock Margin="3,0,0,0" Grid.Column="1" Foreground="White" Text="启动" HorizontalAlignment="Center" VerticalAlignment="Center"/>
30
</Grid>
31
</Button>
32
<Button Style="{StaticResource ConsoleButton}" Margin="3" IsEnabled="{Binding CanStopServer}" Command="{Binding StopServerCommand}">
33
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
34
<Grid.ColumnDefinitions>
35
<ColumnDefinition Width="Auto"/>
36
<ColumnDefinition Width="*"/>
37
</Grid.ColumnDefinitions>
38
<Image Source="/Resources/Image/stop.png" HorizontalAlignment="Center" VerticalAlignment="Center"/>
39
<TextBlock Margin="3,0,0,0" Grid.Column="1" Foreground="White" Text="停止" HorizontalAlignment="Center" VerticalAlignment="Center"/>
40
</Grid>
41
</Button>
42
<Button Style="{StaticResource ConsoleButton}" Margin="3" IsEnabled="{Binding CanRestartServer}" Command="{Binding RestartServerCommand}">
43
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
44
<Grid.ColumnDefinitions>
45
<ColumnDefinition Width="Auto"/>
46
<ColumnDefinition Width="*"/>
47
</Grid.ColumnDefinitions>
48
<Image Source="/Resources/Image/restart.png" HorizontalAlignment="Center" VerticalAlignment="Center"/>
49
<TextBlock Margin="3,0,0,0" Grid.Column="1" Foreground="White" Text="重启" HorizontalAlignment="Center" VerticalAlignment="Center"/>
50
</Grid>
51
</Button>
52
<Button Style="{StaticResource ConsoleButton}" Margin="3" IsEnabled="{Binding CanCleanUp}" Command="{Binding CleanUpCommand}">
53
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
54
<Grid.ColumnDefinitions>
55
<ColumnDefinition Width="Auto"/>
56
<ColumnDefinition Width="*"/>
57
</Grid.ColumnDefinitions>
58
<Image Source="/Resources/Image/clean.png" HorizontalAlignment="Center" VerticalAlignment="Center"/>
59
<TextBlock Margin="3,0,0,0" Grid.Column="1" Foreground="White" Text="清空" HorizontalAlignment="Center" VerticalAlignment="Center"/>
60
</Grid>
61
</Button>
62
</StackPanel>
63
</Grid>
64
<Border Grid.Column="0" Grid.Row="1" CornerRadius="0,0,15,0" Background="Black" Padding="0,5,3,5">
65
<ScrollViewer VerticalScrollBarVisibility="Auto">
66
<ScrollViewer.Resources>
67
<Style TargetType="ScrollBar" BasedOn="{StaticResource ConsoleScrollBar}"/>
68
</ScrollViewer.Resources>
69
<StackPanel x:Name="consoleStackPanel" x:FieldModifier="internal">
70
</StackPanel>
71
</ScrollViewer>
72
</Border>
73
</Grid>
74
</Border>
75
</Page>
@@ -0,0 +1,20 @@
1
using System.Windows.Controls;
2
using XFEToolBox.ViewModel;
3
4
namespace XFEToolBox.Views.Pages;
5
6
/// <summary>
7
/// ConsolePage.xaml 的交互逻辑
8
/// </summary>
9
public partial class ConsolePage : Page
10
{
11
public static ConsolePage? Current { get; set; } = new ConsolePage();
12
public ConsolePageViewModel ViewModel { get; set; }
13
public ConsolePage()
14
{
15
InitializeComponent();
16
ViewModel = new ConsolePageViewModel(this);
17
DataContext = ViewModel;
18
Current = this;
19
}
20
}
@@ -8,13 +8,13 @@
8
8
xmlns:local="clr-namespace:XFEToolBox.Views.Windows"
9
9
mc:Ignorable="d"
10
10
d:DataContext="{d:DesignInstance Type=viewmodel:MainWindowViewModel}"
11
Title="MainWindow" Height="450" Width="720" Background="Transparent" WindowStyle="None" AllowsTransparency="True" WindowStartupLocation="CenterScreen" Icon="/Resources/Icon/XFEToolBoxIcon.ico" Loaded="Window_Loaded">
11
Title="XFE工具箱" Height="450" Width="720" MinHeight="450" MinWidth="720" Background="Transparent" WindowStyle="None" AllowsTransparency="True" WindowStartupLocation="CenterScreen" Icon="/Resources/Icon/XFEToolBoxIcon.ico" Loaded="Window_Loaded">
12
12
13
<Border CornerRadius="20" Background="#9898e7">
13
<Border CornerRadius="19" Background="#9898e7">
14
14
<Grid>
15
15
<Grid.ColumnDefinitions>
16
<ColumnDefinition Width="180"/>
16
17
<ColumnDefinition Width="*"/>
17
<ColumnDefinition Width="3*"/>
18
18
</Grid.ColumnDefinitions>
19
19
<Grid.RowDefinitions>
20
20
<RowDefinition Height="25"/>
@@ -28,7 +28,7 @@
28
28
<ColumnDefinition Width="45"/>
29
29
<ColumnDefinition Width="45"/>
30
30
</Grid.ColumnDefinitions>
31
<Border x:Name="dragTabBorder" CornerRadius="0,0,20,20" Style="{StaticResource TabBorderStyle}" MouseMove="DragTabBorder_MouseMove">
31
<Border x:Name="dragTabBorder" CornerRadius="0,0,20,20" Style="{StaticResource TabBorderStyle}" MouseMove="DragTabBorder_MouseMove" MouseDown="DragTabBorder_MouseDown">
32
32
<Image x:Name="dragTabImage" Source="/Resources/Image/tabline.png" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="4"/>
33
33
</Border>
34
34
<Image x:Name="minimizeImage" Style="{StaticResource RotorImage}" Grid.Column="1" Source="/Resources/Image/min.png" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="4" MouseLeftButtonUp="MinimizeImage_MouseLeftButtonUp"/>
@@ -61,6 +61,16 @@
61
61
<TextBlock Grid.Column="1" Style="{StaticResource NavigationButtonText}" Text="工具箱"/>
62
62
</Grid>
63
63
</ctr:NavigationButton>
64
<ctr:NavigationButton Style="{StaticResource NavigationButton}" Command="{Binding NavigateToPageCommand}" CommandParameter="console">
65
<Grid>
66
<Grid.ColumnDefinitions>
67
<ColumnDefinition Width="40"/>
68
<ColumnDefinition Width="*"/>
69
</Grid.ColumnDefinitions>
70
<Image Source="/Resources/Image/console.png" Margin="7"/>
71
<TextBlock Grid.Column="1" Style="{StaticResource NavigationButtonText}" Text="C#控制台"/>
72
</Grid>
73
</ctr:NavigationButton>
64
74
<ctr:NavigationButton Style="{StaticResource NavigationButton}" Command="{Binding NavigateToPageCommand}" CommandParameter="download">
65
75
<Grid>
66
76
<Grid.ColumnDefinitions>
@@ -83,6 +93,9 @@
83
93
</ctr:NavigationButton>
84
94
</StackPanel>
85
95
<Frame x:Name="contentFrame" Style="{StaticResource NavigationFrame}" Content="{Binding CurrentPage}" Navigated="ContentFrame_Navigated"/>
96
<Image Grid.Column="1" Grid.Row="1" Source="/Resources/Image/corner.png" Width="25" Height="25" HorizontalAlignment="Right" VerticalAlignment="Bottom"/>
97
<Border x:Name="cornerBorder" Grid.Column="1" Grid.Row="1" Style="{StaticResource CornerBorderStyle}" MouseDown="CornerBorder_MouseDown">
98
</Border>
86
99
</Grid>
87
100
</Border>
88
101
</Window>
@@ -1,5 +1,8 @@
1
using System.Windows;
1
using System.Diagnostics;
2
using System.Windows;
2
3
using System.Windows.Media.Animation;
4
using XFEExtension.NetCore.InputSimulator;
5
using XFEToolBox.Profiles.CrossVersionProfiles;
3
6
using XFEToolBox.ViewModel;
4
7
5
8
namespace XFEToolBox.Views.Windows
@@ -17,6 +20,8 @@ namespace XFEToolBox.Views.Windows
17
20
ViewModel = new MainWindowViewModel(this);
18
21
DataContext = ViewModel;
19
22
Current = this;
23
Width = SystemProfile.MainWindowWidth;
24
Height = SystemProfile.MainWindowHeight;
20
25
}
21
26
22
27
private void MinimizeImage_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e) => ViewModel.Minimize();
@@ -26,12 +31,28 @@ namespace XFEToolBox.Views.Windows
26
31
private void DragTabBorder_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
27
32
{
28
33
if (e.LeftButton == System.Windows.Input.MouseButtonState.Pressed)
34
{
35
if (WindowState == WindowState.Maximized)
36
{
37
WindowState = WindowState.Normal;
38
var mousePosition = InputSimulator.GetMousePosition();
39
Left = mousePosition.X / SystemProfile.CurrentWindowDPIScale - Width / 2;
40
Top = mousePosition.Y / SystemProfile.CurrentWindowDPIScale - 10;
41
}
29
42
DragMove();
43
}
44
}
45
46
private void DragTabBorder_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
47
{
48
if (ViewModel.CheckDoubleClick(500))
49
WindowState = WindowState.Maximized;
30
50
}
31
51
32
52
private void Window_Loaded(object sender, RoutedEventArgs e)
33
53
{
34
54
mainButton.IsChecked = true;
55
ViewModel.GetDPIScale();
35
56
}
36
57
37
58
private void ContentFrame_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
@@ -49,5 +70,7 @@ namespace XFEToolBox.Views.Windows
49
70
storyboard.Children.Add(fadeIn);
50
71
storyboard.Begin();
51
72
}
73
74
private void CornerBorder_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) => ViewModel.InitializeToResize();
52
75
}
53
76
}
@@ -0,0 +1,15 @@
1
<Window x:Class="XFEToolBox.Views.Windows.PopupWindow"
2
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
5
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
6
xmlns:local="clr-namespace:XFEToolBox.Views.Windows"
7
mc:Ignorable="d"
8
Title="PopupWindow" ShowInTaskbar="False" Height="100" Width="200" Background="Transparent" WindowStyle="None" AllowsTransparency="True" WindowStartupLocation="CenterOwner">
9
<Window.Effect>
10
<DropShadowEffect BlurRadius="3" ShadowDepth="3" Direction="-45"/>
11
</Window.Effect>
12
<Border CornerRadius="10" BorderThickness="2" BorderBrush="#5b5be7" Background="White">
13
14
</Border>
15
</Window>
@@ -0,0 +1,27 @@
1
using System;
2
using System.Collections.Generic;
3
using System.Linq;
4
using System.Text;
5
using System.Threading.Tasks;
6
using System.Windows;
7
using System.Windows.Controls;
8
using System.Windows.Data;
9
using System.Windows.Documents;
10
using System.Windows.Input;
11
using System.Windows.Media;
12
using System.Windows.Media.Imaging;
13
using System.Windows.Shapes;
14
15
namespace XFEToolBox.Views.Windows
16
{
17
/// <summary>
18
/// PopupWindow.xaml 的交互逻辑
19
/// </summary>
20
public partial class PopupWindow : Window
21
{
22
public PopupWindow()
23
{
24
InitializeComponent();
25
}
26
}
27
}
@@ -13,11 +13,17 @@
13
13
<None Remove="Resources\Icon\XFEToolBoxIcon.ico" />
14
14
<None Remove="Resources\Icon\XFEToolBoxLogo.png" />
15
15
<None Remove="Resources\Icon\XFEToolBoxSplashScreen.png" />
16
<None Remove="Resources\Image\clean.png" />
16
17
<None Remove="Resources\Image\close.png" />
18
<None Remove="Resources\Image\console.png" />
19
<None Remove="Resources\Image\corner.png" />
17
20
<None Remove="Resources\Image\download.png" />
18
21
<None Remove="Resources\Image\grid.png" />
19
22
<None Remove="Resources\Image\min.png" />
23
<None Remove="Resources\Image\restart.png" />
20
24
<None Remove="Resources\Image\setting.png" />
25
<None Remove="Resources\Image\start.png" />
26
<None Remove="Resources\Image\stop.png" />
21
27
<None Remove="Resources\Image\tabline.png" />
22
28
<None Remove="Resources\Image\toolbox.png" />
23
29
<None Remove="Resources\Image\wrench.png" />
@@ -28,17 +34,24 @@
28
34
<PackageReference Include="XFEExtension.NetCore" Version="3.0.0" />
29
35
<PackageReference Include="XFEExtension.NetCore.AutoConfig" Version="1.1.1" />
30
36
<PackageReference Include="XFEExtension.NetCore.AutoPath" Version="1.0.1" />
37
<PackageReference Include="XFEExtension.NetCore.InputSimulator" Version="2.2.0" />
31
38
<PackageReference Include="XFEExtension.NetCore.TodoHighLight" Version="1.0.0" />
32
39
</ItemGroup>
33
40
34
41
<ItemGroup>
35
42
<Resource Include="Resources\Icon\XFEToolBoxIcon.ico" />
36
43
<Resource Include="Resources\Icon\XFEToolBoxLogo.png" />
44
<Resource Include="Resources\Image\clean.png" />
37
45
<Resource Include="Resources\Image\close.png" />
46
<Resource Include="Resources\Image\console.png" />
47
<Resource Include="Resources\Image\corner.png" />
38
48
<Resource Include="Resources\Image\download.png" />
39
49
<Resource Include="Resources\Image\grid.png" />
40
50
<Resource Include="Resources\Image\min.png" />
51
<Resource Include="Resources\Image\restart.png" />
41
52
<Resource Include="Resources\Image\setting.png" />
53
<Resource Include="Resources\Image\start.png" />
54
<Resource Include="Resources\Image\stop.png" />
42
55
<Resource Include="Resources\Image\tabline.png" />
43
56
</ItemGroup>
44
57