ScriptEditControl.xaml.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. using ICSharpCode.AvalonEdit.CodeCompletion;
  2. using ICSharpCode.AvalonEdit.Document;
  3. using ICSharpCode.AvalonEdit.Editing;
  4. using ICSharpCode.AvalonEdit.Folding;
  5. using Microsoft.Win32;
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Collections.ObjectModel;
  9. using System.ComponentModel;
  10. using System.Linq;
  11. using System.Reflection;
  12. using System.Runtime.CompilerServices;
  13. using System.Text;
  14. using System.Text.RegularExpressions;
  15. using System.Threading.Tasks;
  16. using System.Windows;
  17. using System.Windows.Controls;
  18. using System.Windows.Data;
  19. using System.Windows.Documents;
  20. using System.Windows.Input;
  21. using System.Windows.Media;
  22. using System.Windows.Media.Imaging;
  23. using System.Windows.Navigation;
  24. using System.Windows.Shapes;
  25. using System.Windows.Threading;
  26. using TeamAAS_VP.Core;
  27. using TeamAAS_VP.Models;
  28. using TeamAAS_VP.ViewModels.DebugMod;
  29. using Prism.Commands;
  30. using TeamAAS_VP;
  31. using TeamAAS_VP.Resources.Languages;
  32. namespace TeamAAS_VP.Controls
  33. {
  34. /// <summary>
  35. /// ScriptEditControl.xaml 的交互逻辑
  36. /// </summary>
  37. public partial class ScriptEditControl : UserControl
  38. {
  39. /// <summary>
  40. /// 当前app的所有程序集中的类
  41. /// </summary>
  42. Dictionary<string, Type> ClassMemberlist = new Dictionary<string, Type>();
  43. #region 属性
  44. private double _fontSize = 14;
  45. public double FontSize
  46. {
  47. get { return _fontSize; }
  48. set { _fontSize = value;
  49. this.textEditor.FontSize = value;
  50. }
  51. }
  52. private TextDocument _document = new TextDocument();
  53. public TextDocument Document
  54. {
  55. get { return _document; }
  56. set { _document = value;
  57. }
  58. }
  59. private TextArea _mTextArea;
  60. public TextArea mTextArea
  61. {
  62. get { return _mTextArea; }
  63. set { _mTextArea = value; }
  64. }
  65. private ObservableCollection<ScriptCompileMessage> _CompileMessageList = new ObservableCollection<ScriptCompileMessage>();
  66. public ObservableCollection<ScriptCompileMessage> CompileMessageList
  67. {
  68. get { return _CompileMessageList; }
  69. set {
  70. _CompileMessageList = value;
  71. this.messagelist.ItemsSource = value;
  72. }
  73. }
  74. private bool _IsCompile = true;
  75. public bool IsCompile
  76. {
  77. get { return _IsCompile; }
  78. set { _IsCompile = value;
  79. this.Dispatcher.Invoke(new Action(() => {
  80. this.btn1.IsEnabled = value;
  81. this.btn2.IsEnabled = value;
  82. this.btn3.IsEnabled = value;
  83. this.btn4.IsEnabled = value;
  84. this.btn5.IsEnabled = value;
  85. this.btn6.IsEnabled = value;
  86. this.btn7.IsEnabled = value;
  87. this.btn8.IsEnabled = value;
  88. this.btn9.IsEnabled = value;
  89. this.btn10.IsEnabled = value;
  90. this.btn11.IsEnabled = value;
  91. this.btn12.IsEnabled = value;
  92. this.btnShowEndOfLine.IsEnabled= value;
  93. this.btnShowLineNumbers.IsEnabled= value;
  94. this.btnWordWrap.IsEnabled= value;
  95. this.textEditor.IsEnabled= value;
  96. }));
  97. }
  98. }
  99. [Bindable(true)]
  100. [Category("Common")]
  101. [DefaultValue("")]
  102. public string Text
  103. {
  104. get { return (string)GetValue(TextProperty); }
  105. set { SetValue(TextProperty, value);
  106. }
  107. }
  108. // Using a DependencyProperty as the backing store for Text. This enables animation, styling, binding, etc...
  109. public static readonly DependencyProperty TextProperty =
  110. DependencyProperty.Register("Text", typeof(string), typeof(ScriptEditControl), new PropertyMetadata(default(string),new PropertyChangedCallback(OnValueChanged)));
  111. private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  112. {
  113. if (d is ScriptEditControl control)
  114. {
  115. if (e.NewValue!=e.OldValue)
  116. {
  117. if (!control.isWrite)
  118. {
  119. control.textEditor.Document.Text = (string)e.NewValue;
  120. }
  121. }
  122. }
  123. }
  124. [Bindable(true)]
  125. [Category("Common")]
  126. [DefaultValue("")]
  127. public string ScriptPath
  128. {
  129. get { return (string)GetValue(ScriptPathProperty); }
  130. set { SetValue(ScriptPathProperty, value); }
  131. }
  132. // Using a DependencyProperty as the backing store for ScriptPath. This enables animation, styling, binding, etc...
  133. public static readonly DependencyProperty ScriptPathProperty =
  134. DependencyProperty.Register("ScriptPath", typeof(string), typeof(ScriptEditControl), new PropertyMetadata(default(string), new PropertyChangedCallback(OnPathChanged)));
  135. private static void OnPathChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  136. {
  137. if (d is ScriptEditControl control)
  138. {
  139. if (e.NewValue != e.OldValue)
  140. {
  141. }
  142. }
  143. }
  144. [Bindable(true)]
  145. [Category("Common")]
  146. [DefaultValue("")]
  147. public string CompileType
  148. {
  149. get { return (string)GetValue(CompileTypeProperty); }
  150. set { SetValue(CompileTypeProperty, value); }
  151. }
  152. // Using a DependencyProperty as the backing store for CompileType. This enables animation, styling, binding, etc...
  153. public static readonly DependencyProperty CompileTypeProperty =
  154. DependencyProperty.Register("CompileType", typeof(string), typeof(ScriptEditControl), new PropertyMetadata(default(string)));
  155. #endregion
  156. #region 命令
  157. #endregion
  158. #region 事件
  159. public static readonly RoutedEvent SaveScriptEvent=EventManager.RegisterRoutedEvent("OnSaveScript", RoutingStrategy.Bubble,typeof(RoutedEventHandler),typeof(ScriptEditControl));
  160. public event RoutedEventHandler OnSaveScript
  161. {
  162. add { AddHandler(SaveScriptEvent, value);}
  163. remove { RemoveHandler(SaveScriptEvent, value);}
  164. }
  165. #endregion
  166. public ScriptEditControl()
  167. {
  168. InitializeComponent();
  169. this.Loaded += ScriptEditControl_Loaded;
  170. this.textEditor.PreviewMouseWheel += TextEditor_PreviewMouseWheel;
  171. this.messagelist.SelectionChanged += Messagelist_SelectionChanged;
  172. this.textEditor.Document = Document;
  173. this.textEditor.Document.TextChanged += Document_TextChanged;
  174. this.messagelist.ItemsSource= new ObservableCollection<ScriptCompileMessage>();
  175. }
  176. bool isWrite = false;
  177. private void Document_TextChanged(object sender, EventArgs e)
  178. {
  179. isWrite = true;
  180. Text = this.textEditor.Document.Text;
  181. isWrite = false;
  182. }
  183. #region 方法
  184. bool isFirst=true;
  185. private async void ScriptEditControl_Loaded(object sender, RoutedEventArgs e)
  186. {
  187. if (isFirst)
  188. {
  189. textEditor.Options.HighlightCurrentLine = true;
  190. mTextArea = textEditor.TextArea;
  191. textEditor.TextArea.TextEntering += TextArea_TextEntering;
  192. textEditor.TextArea.TextEntered += TextArea_TextEntered;
  193. //textEditor.PreviewMouseWheel += vm.TextEditor_PreviewMouseWheel;
  194. ICSharpCode.AvalonEdit.Search.SearchPanel.Install(textEditor);
  195. foldingManager = FoldingManager.Install(textEditor.TextArea);
  196. foldingStrategy = new BraceFoldingStrategy();
  197. DispatcherTimer foldingUpdateTimer = new DispatcherTimer();
  198. foldingUpdateTimer.Interval = TimeSpan.FromSeconds(2);
  199. foldingUpdateTimer.Tick += delegate { UpdateFoldings(); };
  200. foldingUpdateTimer.Start();
  201. await Task.Run(() =>
  202. {
  203. try
  204. {
  205. var cc = GetTypesInNamespace(Assembly.GetExecutingAssembly(), "TeamAAS_VP");
  206. foreach (var item in GetTypesInNamespace(Assembly.GetExecutingAssembly(), "TeamAAS_VP"))
  207. {
  208. if (!item.IsPublic)
  209. {
  210. continue;
  211. }
  212. ClassMemberlist.Add(item.Name, item);
  213. }
  214. //获取当前应用域中加载的所有程序集数组
  215. Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
  216. foreach (var item in assemblies)
  217. {
  218. try
  219. {
  220. foreach (var item1 in GetTypesInNamespace(Assembly.Load(item.FullName)))
  221. {
  222. if (!item1.IsPublic)
  223. {
  224. continue;
  225. }
  226. ClassMemberlist.Add(item1.Name, item1);
  227. }
  228. }
  229. catch (Exception)
  230. {
  231. }
  232. }
  233. }
  234. catch (Exception)
  235. {
  236. }
  237. });
  238. isFirst = false;
  239. }
  240. }
  241. /// <summary>
  242. /// 打开脚本文件
  243. /// </summary>
  244. /// <param name="sender"></param>
  245. /// <param name="e"></param>
  246. private void Button_Click(object sender, RoutedEventArgs e)
  247. {
  248. try
  249. {
  250. OpenFileDialog dlg = new OpenFileDialog();
  251. dlg.Title = Lang.打开脚本;
  252. dlg.Filter = $"{Lang.脚本文件}|*.cs|{Lang.所有文件}.*|*.*";
  253. dlg.CheckFileExists = true;
  254. if (dlg.ShowDialog() ?? false)
  255. {
  256. this.textEditor.Document.Text = FileHelper.ReadFile(dlg.FileName);
  257. }
  258. }
  259. catch (Exception)
  260. {
  261. }
  262. }
  263. /// <summary>
  264. /// 保存脚本
  265. /// </summary>
  266. /// <param name="sender"></param>
  267. /// <param name="e"></param>
  268. private void Button_Click_1(object sender, RoutedEventArgs e)
  269. {
  270. try
  271. {
  272. if (string.IsNullOrEmpty(ScriptPath))
  273. {
  274. return;
  275. }
  276. FileHelper.WriteFile(this.textEditor.Document.Text, ScriptPath);
  277. RoutedEventArgs args = new RoutedEventArgs(ScriptEditControl.SaveScriptEvent, Text);
  278. RaiseEvent(args);
  279. MessageBox.Show("Succed!");
  280. }
  281. catch (Exception ex)
  282. {
  283. LogHelper.WriteLogError("保存脚本文件时出错",ex);
  284. MessageBox.Show(ex.Message);
  285. }
  286. }
  287. /// <summary>
  288. /// 另存为
  289. /// </summary>
  290. /// <param name="sender"></param>
  291. /// <param name="e"></param>
  292. private void Button_Click_2(object sender, RoutedEventArgs e)
  293. {
  294. try
  295. {
  296. SaveFileDialog dlg = new SaveFileDialog();
  297. dlg.Title = $"{Lang.保存脚本}";
  298. dlg.DefaultExt = ".cs";
  299. dlg.Filter = $"C# {Lang.文件} (*.cs)|*.cs|{Lang.所有文件} (*.txt)|*.txt|All files (*.*)|*.*";
  300. if (dlg.ShowDialog() ?? false)
  301. {
  302. FileHelper.WriteFile(this.textEditor.Document.Text, dlg.FileName);
  303. }
  304. }
  305. catch (Exception)
  306. {
  307. }
  308. }
  309. /// <summary>
  310. /// 字体放大
  311. /// </summary>
  312. /// <param name="sender"></param>
  313. /// <param name="e"></param>
  314. private void Button_Click_3(object sender, RoutedEventArgs e)
  315. {
  316. if (FontSize < 50)
  317. {
  318. FontSize += 1;
  319. }
  320. }
  321. /// <summary>
  322. /// 字体缩小
  323. /// </summary>
  324. /// <param name="sender"></param>
  325. /// <param name="e"></param>
  326. private void Button_Click_4(object sender, RoutedEventArgs e)
  327. {
  328. if (FontSize > 1)
  329. FontSize -= 1;
  330. }
  331. /// <summary>
  332. /// 编译
  333. /// </summary>
  334. /// <param name="sender"></param>
  335. /// <param name="e"></param>
  336. private async void Button_Click_5(object sender, RoutedEventArgs e)
  337. {
  338. IsCompile = false;
  339. CompileMessageList.Clear();
  340. this.messagelist.ItemsSource = CompileMessageList;
  341. CompileMessageList.Add(new ScriptCompileMessage() { DT = DateTime.Now, Kind = MaterialDesignThemes.Wpf.PackIconKind.InformationSlabCircleOutline, FontColol = new SolidColorBrush(Colors.Black), Message = "开始编译...!" });
  342. if (!string.IsNullOrEmpty(CompileType) && CompileType== "VisionScripting")
  343. {
  344. var rst = await ScriptHelper.CompileVisionScriptingAsync(this.textEditor.Document.Text,null,null,null,null,null,null);
  345. if (rst.Item1)
  346. {
  347. CompileMessageList.Add(new ScriptCompileMessage() { DT = DateTime.Now, Kind = MaterialDesignThemes.Wpf.PackIconKind.CheckCircle, FontColol = new SolidColorBrush(Colors.Green), Message = "编译完成!" });
  348. //rst.Item3.Invoke(rst.Item4,null);
  349. }
  350. else
  351. {
  352. foreach (var item in rst.Item2)
  353. {
  354. CompileMessageList.Add(new ScriptCompileMessage() { DT = DateTime.Now, Kind = MaterialDesignThemes.Wpf.PackIconKind.CloseCircle, FontColol = new SolidColorBrush(Colors.Red), Message = item });
  355. }
  356. }
  357. }
  358. else
  359. {
  360. var rst = await ScriptHelper.CompileAsync(this.textEditor.Document.Text, null, null, null);
  361. if (rst.Item1)
  362. {
  363. CompileMessageList.Add(new ScriptCompileMessage() { DT = DateTime.Now, Kind = MaterialDesignThemes.Wpf.PackIconKind.CheckCircle, FontColol = new SolidColorBrush(Colors.Green), Message = "编译完成!" });
  364. //rst.Item3.Invoke(rst.Item4,null);
  365. }
  366. else
  367. {
  368. foreach (var item in rst.Item2)
  369. {
  370. CompileMessageList.Add(new ScriptCompileMessage() { DT = DateTime.Now, Kind = MaterialDesignThemes.Wpf.PackIconKind.CloseCircle, FontColol = new SolidColorBrush(Colors.Red), Message = item });
  371. }
  372. }
  373. }
  374. this.messagelist.ItemsSource = CompileMessageList;
  375. IsCompile = true;
  376. }
  377. CompletionWindow completionWindow;
  378. public void TextArea_TextEntered(object sender, System.Windows.Input.TextCompositionEventArgs e)
  379. {
  380. if (e.Text == ".")
  381. {
  382. //1.获取.前面的字符串
  383. // 获取当前光标位置
  384. int caretOffset = mTextArea.Caret.Offset;
  385. // 获取当前光标位置前面的单词(以空格、标点等作为分隔符)
  386. TextDocument document = Document;
  387. int startOffset = Math.Max(0, caretOffset - 1); // 从当前位置的前一个字符开始
  388. // 找到单词的起始位置
  389. while (startOffset >= 0 && !char.IsWhiteSpace(document.GetCharAt(startOffset)))
  390. {
  391. startOffset--;
  392. }
  393. startOffset++; // 回到单词的起始位置
  394. // 获取单词的文本
  395. int endOffset = caretOffset - 1;
  396. // 使用 AvalonEdit 提供的 TextUtilities 获取当前位置前面的单词
  397. string word = document.GetText(startOffset, endOffset - startOffset);
  398. //2.检索字符串,并得到对象
  399. var type = word.GetType();
  400. foreach (var item in ClassMemberlist)
  401. {
  402. if (item.Key.ToLower().Trim(' ') == word.ToLower().Trim(' '))
  403. {
  404. type = item.Value;
  405. break;
  406. }
  407. }
  408. if (type == null)
  409. return;
  410. //3.检索对象中所有的属性,字段,事件,方法,并做提示
  411. // open code completion after the user has pressed dot:
  412. completionWindow = new CompletionWindow(((ICSharpCode.AvalonEdit.Editing.TextArea)sender));
  413. // provide AvalonEdit with the data:
  414. IList<ICompletionData> data = completionWindow.CompletionList.CompletionData;
  415. System.Reflection.MethodInfo[] methodInfos = type.GetMethods(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Static);
  416. System.Reflection.PropertyInfo[] propertyInfos = type.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Static); // System.Reflection.BindingFlags.Public| System.Reflection.BindingFlags.Static
  417. System.Reflection.EventInfo[] eventInfos = type.GetEvents(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Static);
  418. System.Reflection.FieldInfo[] fieldInfos = type.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Static);
  419. List<String> proposals = new List<string>();
  420. //遍历字段
  421. foreach (var item in fieldInfos)
  422. {
  423. data.Add(new ScriptCompletionData(item.Name, CoreMemberType.Field));
  424. }
  425. //遍历属性
  426. foreach (var item in propertyInfos)
  427. {
  428. data.Add(new ScriptCompletionData(item.Name, CoreMemberType.Property));
  429. }
  430. //遍历方法
  431. foreach (var item in methodInfos)
  432. {
  433. if (item.IsSpecialName)
  434. {
  435. continue;
  436. }
  437. if (item.ToString().Split(' ').Length >= 2)
  438. {
  439. data.Add(new ScriptCompletionData(item.ToString().Split(" ".ToCharArray(), 2)[1].Replace(" ", ""), CoreMemberType.Void));
  440. }
  441. else
  442. {
  443. data.Add(new ScriptCompletionData(item.Name, CoreMemberType.Void));
  444. }
  445. }
  446. //遍历事件
  447. foreach (var item in eventInfos)
  448. {
  449. data.Add(new ScriptCompletionData(item.Name, CoreMemberType.Event));
  450. }
  451. completionWindow.Show();
  452. completionWindow.Closed += delegate {
  453. completionWindow = null;
  454. };
  455. }
  456. else if (e.Text == "(")
  457. {
  458. Document.Insert(mTextArea.Caret.Offset, ")");
  459. mTextArea.Caret.Offset = mTextArea.Caret.Offset - 1;
  460. }
  461. else if (e.Text == "{")
  462. {
  463. Document.Insert(mTextArea.Caret.Offset, "}");
  464. mTextArea.Caret.Offset = mTextArea.Caret.Offset - 1;
  465. }
  466. }
  467. public void TextArea_TextEntering(object sender, System.Windows.Input.TextCompositionEventArgs e)
  468. {
  469. if (e.Text.Length > 0 && completionWindow != null)
  470. {
  471. if (!char.IsLetterOrDigit(e.Text[0]))
  472. {
  473. // Whenever a non-letter is typed while the completion window is open,
  474. // insert the currently selected element.
  475. completionWindow.CompletionList.RequestInsertion(e);
  476. }
  477. }
  478. // do not set e.Handled=true - we still want to insert the character that was typed
  479. }
  480. /// <summary>
  481. /// 获取指定命名空间下的所有类
  482. /// </summary>
  483. /// <param name="assembly"></param>
  484. /// <param name="nameSpace"></param>
  485. /// <returns></returns>
  486. private Type[] GetTypesInNamespace(Assembly assembly, string nameSpace = null)
  487. {
  488. if (nameSpace == null)
  489. {
  490. return assembly.GetTypes();
  491. }
  492. else
  493. {
  494. return assembly.GetTypes();
  495. //return assembly.GetTypes().Where(t => String.Equals(t.Namespace, nameSpace, StringComparison.Ordinal)).ToArray();
  496. }
  497. }
  498. public void TextEditor_PreviewMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)
  499. {
  500. // 检查是否按下了 Ctrl 键
  501. if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
  502. {
  503. // 根据鼠标滚轮方向调整字体大小
  504. if (e.Delta > 0)
  505. {
  506. Button_Click_3(null,null);
  507. }
  508. else
  509. {
  510. Button_Click_4(null,null);
  511. }
  512. // 禁止事件继续传播,防止影响文本编辑器的滚动
  513. e.Handled = true;
  514. }
  515. }
  516. /// <summary>
  517. /// 编辑输出信息选中时
  518. /// </summary>
  519. /// <param name="sender"></param>
  520. /// <param name="e"></param>
  521. private void Messagelist_SelectionChanged(object sender, SelectionChangedEventArgs e)
  522. {
  523. ListView listView = sender as ListView;
  524. ScriptCompileMessage item = listView.SelectedItem as ScriptCompileMessage;
  525. if (item == null)
  526. {
  527. return;
  528. }
  529. if (item.Kind != MaterialDesignThemes.Wpf.PackIconKind.CloseCircle)
  530. {
  531. return;
  532. }
  533. // 正则表达式
  534. string pattern = @"<script>\((\d+),(\d+)\): error (\w+): (.*)";
  535. // 匹配正则表达式
  536. Match match = Regex.Match(item.Message, pattern);
  537. // 如果匹配成功,提取各个部分
  538. if (match.Success)
  539. {
  540. string fullMatch = match.Groups[0].Value; // 完整匹配结果
  541. string lineNumber = match.Groups[1].Value; // 行号
  542. string charNumber = match.Groups[2].Value; // 字符号
  543. string errorCode = match.Groups[3].Value; // 错误编码
  544. string errorDescription = match.Groups[4].Value; // 错误信息
  545. mTextArea.Caret.Line = int.Parse(lineNumber);
  546. mTextArea.Caret.BringCaretToView();
  547. }
  548. }
  549. #endregion
  550. #region Folding
  551. FoldingManager foldingManager;
  552. object foldingStrategy;
  553. void UpdateFoldings()
  554. {
  555. if (foldingStrategy is BraceFoldingStrategy)
  556. {
  557. ((BraceFoldingStrategy)foldingStrategy).UpdateFoldings(foldingManager, textEditor.Document);
  558. }
  559. if (foldingStrategy is XmlFoldingStrategy)
  560. {
  561. ((XmlFoldingStrategy)foldingStrategy).UpdateFoldings(foldingManager, textEditor.Document);
  562. }
  563. }
  564. #endregion
  565. }
  566. }