Console.xaml.cs 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.ObjectModel;
  4. using System.ComponentModel;
  5. using System.IO;
  6. using System.Runtime.CompilerServices;
  7. using System.Text.RegularExpressions;
  8. using System.Windows;
  9. using System.Windows.Controls;
  10. using System.Windows.Data;
  11. using System.Windows.Input;
  12. using System.Windows.Media;
  13. using com.sun.org.glassfish.gmbal;
  14. using FastReport.DataVisualization.Charting;
  15. using Microsoft.Win32;
  16. namespace InABox.Wpf.Console;
  17. public static class ItemsControlExtensions
  18. {
  19. public static void ScrollIntoView(
  20. this ItemsControl control,
  21. object item)
  22. {
  23. var framework =
  24. control.ItemContainerGenerator.ContainerFromItem(item)
  25. as FrameworkElement;
  26. if (framework == null) return;
  27. framework.BringIntoView();
  28. }
  29. public static void ScrollIntoView(this ItemsControl control)
  30. {
  31. var count = control.Items.Count;
  32. if (count == 0) return;
  33. var item = control.Items[count - 1];
  34. control.ScrollIntoView(item);
  35. }
  36. }
  37. /// <summary>
  38. /// Interaction logic for Console.xaml
  39. /// </summary>
  40. public partial class ConsoleControl : UserControl, INotifyPropertyChanged
  41. {
  42. public static readonly DependencyProperty EnabledProperty
  43. = DependencyProperty.Register(nameof(Enabled), typeof(bool), typeof(ConsoleControl));
  44. private CollectionViewSource _filtered;
  45. public CollectionViewSource Filtered
  46. {
  47. get => _filtered;
  48. set
  49. {
  50. _filtered = value;
  51. OnPropertyChanged();
  52. }
  53. }
  54. public readonly ObservableCollection<LogEntry> LogEntries;
  55. private readonly TimeSpan regexTimeOut = TimeSpan.FromMilliseconds(100);
  56. private Regex? searchRegex;
  57. public event PropertyChangedEventHandler? PropertyChanged;
  58. public event Action? OnLoadLog;
  59. public event Action? OnCloseLog;
  60. public bool _allowLoadLogButton = true;
  61. public bool AllowLoadLogButton
  62. {
  63. get => _allowLoadLogButton;
  64. set
  65. {
  66. _allowLoadLogButton = value;
  67. OnPropertyChanged();
  68. OnPropertyChanged(nameof(ShowLoadLogButton));
  69. OnPropertyChanged(nameof(ShowCloseLogButton));
  70. }
  71. }
  72. private bool _loadedLog = false;
  73. public bool LoadedLog
  74. {
  75. get => _loadedLog;
  76. set
  77. {
  78. _loadedLog = value;
  79. OnPropertyChanged();
  80. OnPropertyChanged(nameof(ShowLoadLogButton));
  81. OnPropertyChanged(nameof(ShowCloseLogButton));
  82. }
  83. }
  84. public bool ShowLoadLogButton => !LoadedLog && AllowLoadLogButton;
  85. public bool ShowCloseLogButton => LoadedLog && AllowLoadLogButton;
  86. public bool Enabled
  87. {
  88. get => (bool)GetValue(EnabledProperty);
  89. set => SetValue(EnabledProperty, value);
  90. }
  91. public ConsoleControl()
  92. {
  93. InitializeComponent();
  94. Filtered = new CollectionViewSource();
  95. LogEntries = new ObservableCollection<LogEntry>();
  96. Filtered.Source = LogEntries;
  97. Filtered.Filter += (sender, args) =>
  98. {
  99. var logEntry = (LogEntry)args.Item;
  100. if (ShowImportant.IsChecked == true && !IsImportant(logEntry))
  101. {
  102. args.Accepted = false;
  103. return;
  104. }
  105. if (UseRegEx.IsChecked == true && searchRegex != null)
  106. args.Accepted = string.IsNullOrWhiteSpace(Search.Text)
  107. || searchRegex.IsMatch(logEntry.DateTime)
  108. || searchRegex.IsMatch(logEntry.Type)
  109. || searchRegex.IsMatch(logEntry.User)
  110. || searchRegex.IsMatch(logEntry.Message);
  111. else
  112. args.Accepted = string.IsNullOrWhiteSpace(Search.Text)
  113. || logEntry.DateTime.Contains(Search.Text)
  114. || logEntry.Type.Contains(Search.Text)
  115. || logEntry.User.Contains(Search.Text)
  116. || logEntry.Message.Contains(Search.Text);
  117. };
  118. }
  119. protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
  120. {
  121. PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  122. }
  123. private static bool IsImportant(LogEntry entry)
  124. {
  125. return entry.Type == "IMPTNT";
  126. }
  127. public static LogEntry ParseLogMessage(string logLine)
  128. {
  129. var datetime = logLine.Length > 32 ? logLine[..12] : string.Format("{0:HH:mm:ss.fff}", DateTime.Now);
  130. var type = logLine.Length > 32 ? logLine.Substring(13, 6).Trim() : "INFO";
  131. var user = logLine.Length > 32 ? logLine.Substring(20, 12) : "";
  132. var msg = logLine.Length > 32 ? logLine[33..] : logLine;
  133. return new LogEntry
  134. {
  135. DateTime = datetime,
  136. Type = type,
  137. User = user,
  138. Message = msg
  139. };
  140. }
  141. public void LoadLogEntries(IEnumerable<string> lines)
  142. {
  143. var logEntries = new List<LogEntry>();
  144. var numberSkipped = 0;
  145. foreach (var line in lines)
  146. {
  147. var logEntry = ParseLogMessage(line ?? "");
  148. var logType = logEntry.Type;
  149. if (logType == "ERROR" || logType == "INFO" || logType == "IMPTNT")
  150. logEntries.Add(logEntry);
  151. else if (string.IsNullOrWhiteSpace(logType)) numberSkipped++;
  152. }
  153. if (numberSkipped > 0)
  154. {
  155. if (logEntries.Count == 0)
  156. SetErrorMessage("File does not contain valid log information!");
  157. else
  158. SetErrorMessage(string.Format("Skipped {0} lines that did not contain valid log information", numberSkipped));
  159. }
  160. Filtered.Source = logEntries;
  161. }
  162. public void LoadLogEntry(string line)
  163. {
  164. var logEntry = ParseLogMessage(line);
  165. var logType = logEntry.Type;
  166. if (logType == "INFO" || logType == "ERROR" || logType == "IMPTNT")
  167. {
  168. LogEntries.Insert(0, logEntry);
  169. }
  170. }
  171. private void Search_KeyDown(object sender, KeyEventArgs e)
  172. {
  173. if (e.Key == Key.Enter && UseRegEx.IsChecked == true)
  174. {
  175. try
  176. {
  177. searchRegex = new Regex(Search.Text, RegexOptions.Compiled, regexTimeOut);
  178. }
  179. catch (ArgumentException)
  180. {
  181. searchRegex = null;
  182. }
  183. Filtered.View.Refresh();
  184. SetSearchStyleNormal();
  185. }
  186. }
  187. private void SetSearchStyleChanged()
  188. {
  189. Search.Background = Brushes.White;
  190. }
  191. private void SetSearchStyleNormal()
  192. {
  193. Search.Background = Brushes.LightYellow;
  194. }
  195. private void Search_TextChanged(object sender, TextChangedEventArgs e)
  196. {
  197. if (UseRegEx.IsChecked != true)
  198. {
  199. Filtered.View.Refresh();
  200. }
  201. else
  202. {
  203. if (string.IsNullOrWhiteSpace(Search.Text))
  204. {
  205. searchRegex = null;
  206. Filtered.View.Refresh();
  207. SetSearchStyleNormal();
  208. }
  209. else
  210. {
  211. SetSearchStyleChanged();
  212. }
  213. }
  214. }
  215. private void UseRegEx_Checked(object sender, RoutedEventArgs e)
  216. {
  217. try
  218. {
  219. searchRegex = new Regex(Search.Text, RegexOptions.Compiled, regexTimeOut);
  220. }
  221. catch (ArgumentException ex)
  222. {
  223. searchRegex = null;
  224. }
  225. Filtered.View.Refresh();
  226. }
  227. private void UseRegEx_Unchecked(object sender, RoutedEventArgs e)
  228. {
  229. searchRegex = null;
  230. Filtered.View.Refresh();
  231. SetSearchStyleNormal();
  232. }
  233. public void SetErrorMessage(string? error)
  234. {
  235. if (string.IsNullOrWhiteSpace(error))
  236. {
  237. Error.Content = "";
  238. Error.Visibility = Visibility.Collapsed;
  239. }
  240. else
  241. {
  242. Error.Content = error;
  243. Error.Visibility = Visibility.Visible;
  244. }
  245. }
  246. private void CloseLog_Click(object sender, RoutedEventArgs e)
  247. {
  248. Filtered.Source = LogEntries;
  249. OnCloseLog?.Invoke();
  250. }
  251. private void ShowImportant_Checked(object sender, RoutedEventArgs e)
  252. {
  253. Filtered.View.Refresh();
  254. }
  255. private void ShowImportant_Unchecked(object sender, RoutedEventArgs e)
  256. {
  257. Filtered.View.Refresh();
  258. }
  259. private void LoadLog_Click(object sender, RoutedEventArgs e)
  260. {
  261. OnLoadLog?.Invoke();
  262. }
  263. }
  264. public abstract class Console : Window
  265. {
  266. public ConsoleControl ConsoleControl { get; set; }
  267. private readonly string Description;
  268. public Console(string description)
  269. {
  270. ConsoleControl = new ConsoleControl();
  271. ConsoleControl.OnLoadLog += ConsoleControl_OnLoadLog;
  272. ConsoleControl.OnCloseLog += ConsoleControl_OnCloseLog;
  273. Content = ConsoleControl;
  274. Height = 800;
  275. Width = 1200;
  276. Loaded += Console_Loaded;
  277. Closing += Console_Closing;
  278. Title = description;
  279. Description = description;
  280. }
  281. private void ConsoleControl_OnCloseLog()
  282. {
  283. Title = Description;
  284. ConsoleControl.LoadedLog = false;
  285. }
  286. private void ConsoleControl_OnLoadLog()
  287. {
  288. var dialog = new OpenFileDialog
  289. {
  290. InitialDirectory = GetLogDirectory()
  291. };
  292. if (dialog.ShowDialog() == true)
  293. {
  294. var lines = File.ReadLines(dialog.FileName);
  295. ConsoleControl.LoadLogEntries(lines);
  296. ConsoleControl.LoadedLog = true;
  297. Title = dialog.FileName;
  298. }
  299. }
  300. protected virtual void OnLoaded()
  301. {
  302. }
  303. protected virtual void OnClosing()
  304. {
  305. }
  306. private void Console_Closing(object? sender, CancelEventArgs e)
  307. {
  308. OnClosing();
  309. }
  310. private void Console_Loaded(object sender, RoutedEventArgs e)
  311. {
  312. OnLoaded();
  313. }
  314. protected abstract string GetLogDirectory();
  315. }