返回提交历史

XFEstudio/MyFirstRespo

完善控制台

46167c6
X
XFEstudio <mail@xfegzs.com>
提交于

代码差异

29 个文件 +1517 -42
Modified XFEToolBox.sln +1 -1
@@ -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
Modified XFEToolBox/App.xaml.cs +1 -7
@@ -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 }
Added XFEToolBox/Console/XFEConsole.cs +148 -0
@@ -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 }
Added XFEToolBox/Console/XFEConsoleClientInfo.cs +30 -0
@@ -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 }
Added XFEToolBox/Console/XFEConsoleProgramClient.cs +66 -0
@@ -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 }
Added XFEToolBox/Console/XFEConsoleProgramServer.cs +8 -0
@@ -0,0 +1,8 @@
1 namespace XFEExtension.NetCore.XFEConsole;
2
3 /// <summary>
4 /// XFE控制台进程服务器
5 /// </summary>
6 public class XFEConsoleProgramServer
7 {
8 }
Added XFEToolBox/Console/XFEConsoleTerminalClient.cs +8 -0
@@ -0,0 +1,8 @@
1 namespace XFEExtension.NetCore.XFEConsole;
2
3 /// <summary>
4 /// XFE控制台终端客户端
5 /// </summary>
6 public class XFEConsoleTerminalClient
7 {
8 }
Added XFEToolBox/Console/XFEConsoleTerminalServer.cs +150 -0
@@ -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 }
Added XFEToolBox/Console/XFEConsoleTextWriter.cs +305 -0
@@ -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 }
Added XFEToolBox/Profiles/CrossVersionProfiles/ConsoleProfile.cs +15 -0
@@ -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 }
Modified XFEToolBox/Profiles/CrossVersionProfiles/SystemProfile.cs +9 -2
@@ -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 }
Added XFEToolBox/Resources/Image/clean.png +0 -0
二进制文件已变更,无法进行逐行预览。
Added XFEToolBox/Resources/Image/console.png +0 -0
二进制文件已变更,无法进行逐行预览。
Added XFEToolBox/Resources/Image/corner.png +0 -0
二进制文件已变更,无法进行逐行预览。
Added XFEToolBox/Resources/Image/restart.png +0 -0
二进制文件已变更,无法进行逐行预览。
Added XFEToolBox/Resources/Image/start.png +0 -0
二进制文件已变更,无法进行逐行预览。
Added XFEToolBox/Resources/Image/stop.png +0 -0
二进制文件已变更,无法进行逐行预览。
Modified XFEToolBox/Resources/Style/MainStyle.xaml +150 -25
@@ -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>
Added XFEToolBox/Utilities/DecTextSpan.cs +29 -0
@@ -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 }
Added XFEToolBox/Utilities/DecoratedTextConverter.cs +109 -0
@@ -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 }
Added XFEToolBox/ViewModel/ConsolePageViewModel.cs +209 -0
@@ -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 }
Modified XFEToolBox/ViewModel/MainWindowViewModel.cs +88 -2
@@ -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 }
Added XFEToolBox/Views/Pages/ConsolePage.xaml +75 -0
@@ -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>
Added XFEToolBox/Views/Pages/ConsolePage.xaml.cs +20 -0
@@ -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 }
Modified XFEToolBox/Views/Windows/MainWindow.xaml +17 -4
@@ -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>
Modified XFEToolBox/Views/Windows/MainWindow.xaml.cs +24 -1
@@ -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 }
Added XFEToolBox/Views/Windows/PopupWindow.xaml +15 -0
@@ -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>
Added XFEToolBox/Views/Windows/PopupWindow.xaml.cs +27 -0
@@ -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 }
Modified XFEToolBox/XFEToolBox.csproj +13 -0
@@ -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