From 60f171a55fcbe03901f14466e4967e6104c18289 Mon Sep 17 00:00:00 2001 From: Evaldas Jocys Date: Sat, 26 Aug 2023 13:49:37 +0100 Subject: [PATCH] Update shared files. --- FocusLogger/Controls/DataListControl.xaml | 56 +- FocusLogger/Documents/Step2_app_sign.bat | 39 - FocusLogger/Documents/app_sign.ps1 | 10 + FocusLogger/Documents/app_zip.bat | 30 + .../JocysCom/Collections/CollectionsHelper.cs | 57 + .../ComponentModel/BindingListInvoked.cs | 2 +- .../ComponentModel/PropertyComparer.cs | 2 +- .../ComponentModel/SortableBindingList.cs | 2 +- .../JocysCom/Configuration/AssemblyInfo.cs | 8 +- .../Configuration/ISettingsItemFile.cs | 39 + .../JocysCom/Configuration/SettingsData.cs | 195 ++- .../JocysCom/Configuration/SettingsHelper.cs | 5 +- .../JocysCom/Configuration/SettingsParser.cs | 8 +- .../JocysCom/Controls/ControlsHelper.WPF.cs | 77 +- .../JocysCom/Controls/ControlsHelper.cs | 62 +- .../JocysCom/Controls/InfoControl.xaml | 29 +- .../JocysCom/Controls/InfoControl.xaml.cs | 27 +- .../JocysCom/Controls/InfoHelpProvider.cs | 2 + FocusLogger/JocysCom/Controls/InitHelper.cs | 4 +- .../JocysCom/Controls/TabIndexConverter.cs | 2 +- .../JocysCom/Controls/Themes/Default.xaml | 1370 +++++++++++++---- .../JocysCom/Controls/Themes/Icons.xaml | 223 ++- .../JocysCom/Controls/Themes/Icons.xaml.cs | 2 + FocusLogger/JocysCom/Runtime/RuntimeHelper.cs | 344 +---- FocusLogger/JocysCom/Runtime/Serializer.cs | 32 +- FocusLogger/JocysCom/Text/Helper.cs | 57 +- FocusLogger/MainWindow.xaml | 1 + 27 files changed, 1806 insertions(+), 879 deletions(-) delete mode 100644 FocusLogger/Documents/Step2_app_sign.bat create mode 100644 FocusLogger/Documents/app_sign.ps1 create mode 100644 FocusLogger/Documents/app_zip.bat create mode 100644 FocusLogger/JocysCom/Collections/CollectionsHelper.cs create mode 100644 FocusLogger/JocysCom/Configuration/ISettingsItemFile.cs diff --git a/FocusLogger/Controls/DataListControl.xaml b/FocusLogger/Controls/DataListControl.xaml index c0eff5a..2f7193a 100644 --- a/FocusLogger/Controls/DataListControl.xaml +++ b/FocusLogger/Controls/DataListControl.xaml @@ -21,19 +21,22 @@ - - - - + + + - + + Height="12"> @@ -125,15 +130,17 @@ CanUserResize="False" IsReadOnly="True"> - + + Height="12"> @@ -172,8 +179,7 @@ + Height="12"> @@ -205,15 +211,17 @@ CanUserResize="False" IsReadOnly="True"> - + + Height="12"> diff --git a/FocusLogger/Documents/Step2_app_sign.bat b/FocusLogger/Documents/Step2_app_sign.bat deleted file mode 100644 index e4d3775..0000000 --- a/FocusLogger/Documents/Step2_app_sign.bat +++ /dev/null @@ -1,39 +0,0 @@ -@ECHO OFF -COPY /Y "..\bin\Release\publish\JocysCom.FocusLogger.exe" "JocysCom.FocusLogger.exe" -CALL:SIG "JocysCom.FocusLogger.exe" -echo. -pause - -GOTO:EOF -::============================================================= -:SIG :: Sign and Timestamp Code -::------------------------------------------------------------- -:: SIGNTOOL.EXE Note: -:: Use the Windows 7 Platform SDK web installer that lets you -:: download just the components you need - so just choose the -:: ".NET developer \ Tools" and deselect everything else. -echo. -IF NOT EXIST "%~1" ( - ECHO "%~1" not exist. Skipping. - GOTO:EOF -) -SET sgt=Tools\signtool.exe -IF NOT EXIST "%sgt%" SET sgt=%ProgramFiles(x86)%\Windows Kits\10\App Certification Kit\signtool.exe -IF NOT EXIST "%sgt%" SET sgt=%ProgramFiles(x86)%\Windows Kits\10\bin\x86\signtool.exe -IF NOT EXIST "%sgt%" SET sgt=%ProgramFiles%\Windows Kits\10\bin\x86\signtool.exe -echo %sgt% -echo. -:: Other options. -set pfx=D:\_Backup\Configuration\SSL\Code Sign - Evaldas Jocys\2020\Evaldas_Jocys.pfx -set d=Jocys.com Focus Logger -set du=https://www.jocys.com -set vsg=http://timestamp.comodoca.com -if not exist "%sgt%" CALL:Error "%sgt%" -if not exist "%~1" CALL:Error "%~1" -if not exist "%pfx%" CALL:Error "%pfx%" -"%sgt%" sign /f "%pfx%" /d "%d%" /du "%du%" /fd sha256 /td sha256 /tr "%vsg%" /v "%~1" -GOTO:EOF - -:Error -echo File doesn't Exist: "%~1" -pause diff --git a/FocusLogger/Documents/app_sign.ps1 b/FocusLogger/Documents/app_sign.ps1 new file mode 100644 index 0000000..fc67fff --- /dev/null +++ b/FocusLogger/Documents/app_sign.ps1 @@ -0,0 +1,10 @@ +Import-Module "d:\_Backup\Configuration\SSL\Tools\app_signModule.ps1" -Force + +[string[]]$appFiles = @( + "..\bin\Release\publish\JocysCom.FocusLogger.exe", +) +[string]$appName = "Jocys.com Focus Logger" +[string]$appLink = "https://www.jocys.com" + +ProcessFiles $appName $appLink $appFiles +pause \ No newline at end of file diff --git a/FocusLogger/Documents/app_zip.bat b/FocusLogger/Documents/app_zip.bat new file mode 100644 index 0000000..f4bb3b3 --- /dev/null +++ b/FocusLogger/Documents/app_zip.bat @@ -0,0 +1,30 @@ +@ECHO off +SET wra="%ProgramFiles%\WinRAR\winrar.exe" +IF NOT EXIST %wra% SET wra="%ProgramFiles(x86)%\WinRAR\winrar.exe" +IF NOT EXIST %wra% SET wra="%ProgramW6432%\WinRAR\winrar.exe" +IF NOT EXIST %wra% SET wra="D:\Program Files\WinRAR\winrar.exe" +SET zip=%wra% a -ep +:: --------------------------------------------------------------------------- +IF NOT EXIST Files\nul MKDIR Files +::------------------------------------------------------------- +:: Archive Files +CALL:CRE Files JocysCom.FocusLogger.exe .exe +ECHO. +pause +GOTO:EOF + +::------------------------------------------------------------- +:CRE :: Archive +::------------------------------------------------------------- +SET src=%~1 +SET arc=Files\%~2.zip +ECHO. +IF NOT EXIST "%src%\%~2%~3" ( + ECHO "%src%\%~2%~3" not exist. Skipping. + GOTO:EOF +) +ECHO Creating: %arc% +:: Create Archive. +IF EXIST %arc% DEL %arc% +%zip% %arc% %src%\%~2%~3 +GOTO:EOF \ No newline at end of file diff --git a/FocusLogger/JocysCom/Collections/CollectionsHelper.cs b/FocusLogger/JocysCom/Collections/CollectionsHelper.cs new file mode 100644 index 0000000..9d86e06 --- /dev/null +++ b/FocusLogger/JocysCom/Collections/CollectionsHelper.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using System.Linq; + +namespace JocysCom.ClassLibrary.Collections +{ + public static partial class CollectionsHelper + { + + /// + /// Synchronize source collection to destination. + /// + public static void Synchronize(IList source, IList target, IEqualityComparer comparer = null) + { + comparer = comparer ?? EqualityComparer.Default; + // Create a dictionary for fast lookup in source list + var sourceSet = new Dictionary(); + for (int i = 0; i < source.Count; i++) + sourceSet[source[i]] = i; + // Iterate over the target, remove items not in source + for (int i = target.Count - 1; i >= 0; i--) + if (!sourceSet.Keys.Contains(target[i], comparer)) + target.RemoveAt(i); + // Iterate over source + for (int si = 0; si < source.Count; si++) + { + // If item is not present in target, insert it. + // Note: Only check items that have not been synchornized in the target. + if (!target.Skip(si).Contains(source[si], comparer)) + { + target.Insert(si, source[si]); + continue; + } + // If item is present in target but not at the right position, move it. + int ti = -1; + // Note: Only check items that have not been synchornized in the target. + for (int i = si; i < target.Count; i++) + { + if (comparer.Equals(target[i], source[si])) + { + ti = i; + break; + } + } + if (ti != si) + { + T temp = target[si]; + target[si] = target[ti]; + target[ti] = temp; + } + } + // Remove items at the end of target that exceed source's length + while (target.Count > source.Count) + target.RemoveAt(target.Count - 1); + } + + } +} diff --git a/FocusLogger/JocysCom/ComponentModel/BindingListInvoked.cs b/FocusLogger/JocysCom/ComponentModel/BindingListInvoked.cs index 7261e09..1ca7684 100644 --- a/FocusLogger/JocysCom/ComponentModel/BindingListInvoked.cs +++ b/FocusLogger/JocysCom/ComponentModel/BindingListInvoked.cs @@ -34,7 +34,7 @@ public void AddRange(IEnumerable list) void Invoke(Delegate method, params object[] args) { var so = SynchronizingObject; - if (so == null || !JocysCom.ClassLibrary.Controls.ControlsHelper.InvokeRequired) + if (so is null || !JocysCom.ClassLibrary.Controls.ControlsHelper.InvokeRequired) { DynamicInvoke(method, args); } diff --git a/FocusLogger/JocysCom/ComponentModel/PropertyComparer.cs b/FocusLogger/JocysCom/ComponentModel/PropertyComparer.cs index 8b6b83f..08a022e 100644 --- a/FocusLogger/JocysCom/ComponentModel/PropertyComparer.cs +++ b/FocusLogger/JocysCom/ComponentModel/PropertyComparer.cs @@ -47,7 +47,7 @@ protected int Compare(T x, T y) private int CompareValues(object xValue, object yValue, ListSortDirection direction) { int retValue; - if (xValue == null && yValue == null) + if (xValue is null && yValue is null) retValue = 0; else if (xValue is IComparable) retValue = ((IComparable)xValue).CompareTo(yValue); diff --git a/FocusLogger/JocysCom/ComponentModel/SortableBindingList.cs b/FocusLogger/JocysCom/ComponentModel/SortableBindingList.cs index 86b566c..3c823a7 100644 --- a/FocusLogger/JocysCom/ComponentModel/SortableBindingList.cs +++ b/FocusLogger/JocysCom/ComponentModel/SortableBindingList.cs @@ -96,7 +96,7 @@ private void ApplySortInternal(PropertyComparer comparer) if (_OriginalCollection.Count == 0) _OriginalCollection.AddRange(this); var listRef = Items as List; - if (listRef == null) + if (listRef is null) return; listRef.Sort(comparer); _Sorted = true; diff --git a/FocusLogger/JocysCom/Configuration/AssemblyInfo.cs b/FocusLogger/JocysCom/Configuration/AssemblyInfo.cs index 0837ffb..7d5490b 100644 --- a/FocusLogger/JocysCom/Configuration/AssemblyInfo.cs +++ b/FocusLogger/JocysCom/Configuration/AssemblyInfo.cs @@ -26,7 +26,7 @@ public static AssemblyInfo Entry { lock (_EntryLock) { - if (_Entry == null) + if (_Entry is null) _Entry = new AssemblyInfo(); return _Entry; } @@ -122,7 +122,7 @@ public string RunMode { get { - if (_RunMode == null) + if (_RunMode is null) // TODO: Standardize configuration provider XML, JSON, INI, Registry, etc... // https://docs.microsoft.com/en-us/dotnet/core/extensions/configuration-providers //_RunMode = SettingsParser.Current.Parse("RunMode", ""); @@ -309,7 +309,7 @@ public static DateTime GetBuildDateTime(string filePath) /// public static DateTime GetBuildDateTime(Assembly assembly, TimeZoneInfo tzi = null) { - if (assembly == null) + if (assembly is null) throw new ArgumentNullException(nameof(assembly)); var names = assembly.GetManifestResourceNames(); var dt = default(DateTime); @@ -388,7 +388,7 @@ public string AssemblyPath string GetAttribute(Func value) where T : Attribute { T attribute = (T)Attribute.GetCustomAttribute(Assembly, typeof(T)); - return attribute == null + return attribute is null ? "" : value.Invoke(attribute); } diff --git a/FocusLogger/JocysCom/Configuration/ISettingsItemFile.cs b/FocusLogger/JocysCom/Configuration/ISettingsItemFile.cs new file mode 100644 index 0000000..8877256 --- /dev/null +++ b/FocusLogger/JocysCom/Configuration/ISettingsItemFile.cs @@ -0,0 +1,39 @@ +using System; + +namespace JocysCom.ClassLibrary.Configuration +{ + public interface ISettingsItemFile + { + /// + /// Map to property which contains base file name. + /// + string BaseName { get; set; } + + /// + /// Map to property which contains last time item was modified. + /// Update on INotifyPropertyChanged. + /// + DateTime WriteTime { get; set; } + + /* Example: + + #region ■ ISettingsItemFile + + [XmlIgnore] + string ISettingsItemFile.BaseName { get => Name; set => Name = value; } + + [XmlIgnore] + DateTime ISettingsItemFile.WriteTime { get; set; } + + #endregion + + protected void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + ((ISettingsItemFile)this).WriteTime = DateTime.Now; + } + + */ + + } +} + diff --git a/FocusLogger/JocysCom/Configuration/SettingsData.cs b/FocusLogger/JocysCom/Configuration/SettingsData.cs index 0eb09e5..8e5f6dd 100644 --- a/FocusLogger/JocysCom/Configuration/SettingsData.cs +++ b/FocusLogger/JocysCom/Configuration/SettingsData.cs @@ -10,7 +10,7 @@ using System.Text; using System.Xml; using System.Xml.Serialization; -using System.Xml.Linq; +using JocysCom.ClassLibrary.Collections; #if NETSTANDARD // .NET Standard #elif NETCOREAPP // .NET Core using System.Windows; @@ -99,18 +99,6 @@ private void Initialize(string overrideFileName, bool? userLevel, string comment [XmlIgnore] public bool UseSeparateFiles { get; set; } - [XmlIgnore] - public string FileNamePropertyName - { - get => _FileNamePropertyName; - set { _FileNamePropertyName = value; FileNameProperty = typeof(T).GetProperty(value); } - } - string _FileNamePropertyName; - - - [XmlIgnore] - public PropertyInfo FileNameProperty { get; set; } - [XmlIgnore] public FileInfo XmlFile { get { return _XmlFile; } set { _XmlFile = value; } } @@ -131,7 +119,7 @@ public virtual object SyncRoot { get { - if (_SyncRoot == null) + if (_SyncRoot is null) System.Threading.Interlocked.CompareExchange(ref _SyncRoot, new object(), null); return _SyncRoot; } @@ -189,20 +177,24 @@ public void SaveAs(string path) di.Create(); for (int i = 0; i < items.Length; i++) { - var item = items[i]; - var bytes = Serializer.SerializeToXmlBytes(item, Encoding.UTF8, true, _Comment); - var fileName = GetFileNameWithoutExtension(item) + fi.Extension; + var fileItem = (ISettingsItemFile)items[i]; + var bytes = Serialize(fileItem); + var fileName = RemoveInvalidFileNameChars(fileItem.BaseName) + fi.Extension; var fileFullName = Path.Combine(di.FullName, fileName); if (compress) bytes = SettingsHelper.Compress(bytes); - SettingsHelper.WriteIfDifferent(fileFullName, bytes); + if (SettingsHelper.WriteIfDifferent(fileFullName, bytes)) + { + fi.Refresh(); + fileItem.WriteTime = new FileInfo(fileFullName).LastWriteTime; + } } } else { if (!fi.Directory.Exists) fi.Directory.Create(); - var bytes = Serializer.SerializeToXmlBytes(this, Encoding.UTF8, true, _Comment); + var bytes = Serialize(this); if (compress) bytes = SettingsHelper.Compress(bytes); SettingsHelper.WriteIfDifferent(fi.FullName, bytes); @@ -211,7 +203,7 @@ public void SaveAs(string path) SetFileMonitoring(true); } - static string RemoveInvalidFileNameChars(string name) + public static string RemoveInvalidFileNameChars(string name) { var invalidChars = Path.GetInvalidFileNameChars(); return new string(name.Where(c => !invalidChars.Contains(c)).ToArray()); @@ -301,12 +293,13 @@ public void LoadFrom(string fileName) try { var item = DeserializeItem(bytes, compress); + var itemFile = (ISettingsItemFile)item; + itemFile.WriteTime = file.LastWriteTime; // Set Name property value to the same as the file. - var name = GetFileNameWithoutExtension(item); - var fileNamePropertyValue = (string)FileNameProperty.GetValue(item); + var name = RemoveInvalidFileNameChars(file.Name); var fileBaseName = Path.GetFileNameWithoutExtension(file.Name); - if (fileNamePropertyValue != fileBaseName) - FileNameProperty.SetValue(item, fileBaseName); + if (itemFile.BaseName != fileBaseName) + itemFile.BaseName = fileBaseName; data.Add(item); } catch { } @@ -398,21 +391,14 @@ public string GetItemFileFullName(string fileNameWithoutExtension) return path; } - public string GetFileNameWithoutExtension(T item) - { - var name = (string)FileNameProperty.GetValue(item); - name = RemoveInvalidFileNameChars(name); - return name; - } - /// /// Returns error. /// - public string RenameItem(T item, string newName) + public string RenameItem(ISettingsItemFile itemFile, string newName) { lock (saveReadFileLock) { - var oldName = GetFileNameWithoutExtension(item); + var oldName = RemoveInvalidFileNameChars(itemFile.BaseName); // Case sensitive comparison. if (string.Equals(oldName, newName, StringComparison.Ordinal)) return null; @@ -423,22 +409,36 @@ public string RenameItem(T item, string newName) if (invalidChars.Any()) return $"File name cannot contain invalid characters: {string.Join("", invalidChars)}"; var oldPath = GetItemFileFullName(oldName); - var oldFile = new FileInfo(oldPath); + var file = new FileInfo(oldPath); var newPath = GetItemFileFullName(newName); - // If only case changed then rename to temp file first. - if (string.Equals(oldName, newName, StringComparison.OrdinalIgnoreCase)) + // Disable monitoring in order not to trigger reloading. + SetFileMonitoring(false); + try { - var tempFilePath = Path.Combine(Path.GetDirectoryName(oldPath), Guid.NewGuid().ToString() + Path.GetExtension(oldPath)); - oldFile.MoveTo(tempFilePath); + // If only case changed then rename to temp file first. + if (string.Equals(oldName, newName, StringComparison.OrdinalIgnoreCase)) + { + var tempFilePath = Path.Combine(Path.GetDirectoryName(oldPath), Guid.NewGuid().ToString() + Path.GetExtension(oldPath)); + file.MoveTo(tempFilePath); + } + else if (File.Exists(newPath)) + { + return "File with the same name already exists."; + } + if (file.Exists) + { + file.MoveTo(newPath); + itemFile.BaseName = newName; + itemFile.WriteTime = file.LastWriteTime; + } } - else if (File.Exists(newPath)) + catch (Exception) { - return "File with the same name already exists."; + throw; } - if (oldFile.Exists) + finally { - oldFile.MoveTo(newPath); - FileNameProperty.SetValue(item, newName); + SetFileMonitoring(true); } return null; } @@ -447,39 +447,123 @@ public string RenameItem(T item, string newName) /// /// Returns new name. /// - public void DeleteItem(T item) + public void DeleteItem(ISettingsItemFile itemFile) { lock (saveReadFileLock) { - var oldName = GetFileNameWithoutExtension(item); + var oldName = RemoveInvalidFileNameChars(itemFile.BaseName); var oldPath = GetItemFileFullName(oldName); var fi = new FileInfo(oldPath); if (fi.Exists) fi.Delete(); - Items.Remove(item); + Items.Remove((T)itemFile); } } #endregion + public bool ClearWhenLoading = false; + void LoadAndValidateData(IList data) { - // Clear original data. - Items.Clear(); - if (data == null) + if (data is null) data = new SortableBindingList(); // Filter data if filter method exists. var fl = ValidateData; - var items = (fl == null) + var items = (fl is null) ? data : fl(data); // Filter data if filter method exists. var e = new SettingsDataEventArgs(items); OnValidateData?.Invoke(this, e); - for (int i = 0; i < items.Count; i++) - Items.Add(items[i]); + if (ClearWhenLoading) + { + // Clear original data. + Items.Clear(); + for (int i = 0; i < items.Count; i++) + Items.Add(items[i]); + } + else + { + var oldList = GetHashValues(Items); + var newList = GetHashValues(data).ToArray(); + var newData = new List(); + // Step 1: Update new list with the old items if they are exactly the same. + for (int i = 0; i < newList.Length; i++) + { + var newItem = newList[i]; + // Find same item from the old list. + var oldItem = oldList.FirstOrDefault(x => x.Value.SequenceEqual(newItem.Value)); + // If same item found then use it... + if (oldItem.Key != null) + { + newData.Add(oldItem.Key); + oldList.Remove(oldItem.Key); + } + else + { + newData.Add(newItem.Key); + } + } + CollectionsHelper.Synchronize(newData, Items); + } + } + + #region Synchronize + + /// + /// Synchronize source collection to destination. + /// + /// + /// Same Code: + /// JocysCom\Controls\SearchHelper.cs + /// + public static void Synchronize(IList source, IList target) + { + // Convert to array to avoid modification of collection during processing. + var sList = source.ToArray(); + var t = 0; + for (var s = 0; s < sList.Length; s++) + { + var item = sList[s]; + // If item exists in destination and is in the correct position then continue + if (t < target.Count && target[t].Equals(item)) + { + t++; + continue; + } + // If item is in destination but not at the correct position, remove it. + var indexInDestination = target.IndexOf(item); + if (indexInDestination != -1) + target.RemoveAt(indexInDestination); + // Insert item at the correct position. + target.Insert(s, item); + t = s + 1; + } + // Remove extra items. + while (target.Count > sList.Length) + target.RemoveAt(target.Count - 1); } + + /// + /// Return list of items their SHA256 hash. + /// + Dictionary GetHashValues(IList items) + { + var list = new Dictionary(); + var algorithm = System.Security.Cryptography.SHA256.Create(); + foreach (var item in items) + { + var bytes = Serialize(item); + var byteHash = algorithm.ComputeHash(bytes); + list.Add(item, byteHash); + } + return list; + } + + #endregion + public bool ResetToDefault() { // Clear original data. @@ -514,10 +598,15 @@ public bool ResetToDefault() break; } } - LoadAndValidateData(data == null ? null : data.Items); + LoadAndValidateData(data is null ? null : data.Items); return success; } + byte[] Serialize(object fileItem) + { + return Serializer.SerializeToXmlBytes(fileItem, Encoding.UTF8, true, _Comment); + } + SettingsData DeserializeData(byte[] bytes, bool compressed) { if (compressed) diff --git a/FocusLogger/JocysCom/Configuration/SettingsHelper.cs b/FocusLogger/JocysCom/Configuration/SettingsHelper.cs index 3b2fe44..7c5275c 100644 --- a/FocusLogger/JocysCom/Configuration/SettingsHelper.cs +++ b/FocusLogger/JocysCom/Configuration/SettingsHelper.cs @@ -1,9 +1,11 @@ using System; +using System.Collections.Generic; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Text; +using System.Text.RegularExpressions; namespace JocysCom.ClassLibrary.Configuration { @@ -55,7 +57,7 @@ public static byte[] Decompress(byte[] bytes) public static bool IsDifferent(string name, byte[] bytes) { - if (bytes == null) + if (bytes is null) throw new ArgumentNullException(nameof(bytes)); var fi = new FileInfo(name); var isDifferent = false; @@ -155,6 +157,5 @@ public static uint ComputeCRC32Checksum(byte[] bytes) #endregion - } } diff --git a/FocusLogger/JocysCom/Configuration/SettingsParser.cs b/FocusLogger/JocysCom/Configuration/SettingsParser.cs index 8197af4..6c83602 100644 --- a/FocusLogger/JocysCom/Configuration/SettingsParser.cs +++ b/FocusLogger/JocysCom/Configuration/SettingsParser.cs @@ -32,7 +32,7 @@ public SettingsParser(string configPrefix = "") /// Parse all IConvertible types, like value types, with one function. public T Parse(string name, T defaultValue = default(T)) { - if (_GetValue == null) + if (_GetValue is null) return defaultValue; var v = _GetValue(ConfigPrefix + name); return ParseValue(v, defaultValue); @@ -40,9 +40,9 @@ public SettingsParser(string configPrefix = "") public static object ParseValue(Type t, string v, object defaultValue = null) { - if (t == null) + if (t is null) throw new ArgumentNullException(nameof(t)); - if (v == null) + if (v is null) return defaultValue; if (typeof(System.Drawing.Color).IsAssignableFrom(t)) return System.Drawing.Color.FromName(v); @@ -91,7 +91,7 @@ string GetValue(string name) var key = ConfigPrefix + name; if (!p.Keys.Contains(key)) { - if (EmbeddedAppSettings == null) + if (EmbeddedAppSettings is null) ReadEmbeddedSettings(); if (EmbeddedAppSettings.Keys.Contains(key)) return EmbeddedAppSettings[key]; diff --git a/FocusLogger/JocysCom/Controls/ControlsHelper.WPF.cs b/FocusLogger/JocysCom/Controls/ControlsHelper.WPF.cs index 451107c..e6db64e 100644 --- a/FocusLogger/JocysCom/Controls/ControlsHelper.WPF.cs +++ b/FocusLogger/JocysCom/Controls/ControlsHelper.WPF.cs @@ -70,7 +70,7 @@ public static T Clone(T o) var stringReader = new StringReader(sb.ToString()); var xmlReader = XmlReader.Create(stringReader); var item = System.Windows.Markup.XamlReader.Load(xmlReader); - if (item == null) + if (item is null) throw new ArgumentNullException("Could not be cloned."); return (T)item; } @@ -81,9 +81,9 @@ public static T Clone(T o) /// public static void SetText(Label control, string format, params object[] args) { - if (control == null) + if (control is null) throw new ArgumentNullException(nameof(control)); - var text = (args == null) + var text = (args is null) ? format : string.Format(format, args); if (control.Content as string != text) @@ -97,9 +97,9 @@ public static void SetText(Label control, string format, params object[] args) /// public static void SetText(GroupBox control, string format, params object[] args) { - if (control == null) + if (control is null) throw new ArgumentNullException(nameof(control)); - var text = (args == null) + var text = (args is null) ? format : string.Format(format, args); if (control.Header as string != text) @@ -112,9 +112,9 @@ public static void SetText(GroupBox control, string format, params object[] args /// public static void SetText(TextBox control, string format, params object[] args) { - if (control == null) + if (control is null) throw new ArgumentNullException(nameof(control)); - var text = (args == null) + var text = (args is null) ? format ?? "" : string.Format(format ?? "", args); if (control.Text != text) @@ -127,9 +127,9 @@ public static void SetText(TextBox control, string format, params object[] args) /// public static void SetText(TextBlock control, string format, params object[] args) { - if (control == null) + if (control is null) throw new ArgumentNullException(nameof(control)); - var text = (args == null) + var text = (args is null) ? format ?? "" : string.Format(format ?? "", args); if (control.Text != text) @@ -153,7 +153,7 @@ public static void SetTextFromResource(RichTextBox box, byte[] rtf) /// public static void SetChecked(System.Windows.Controls.Primitives.ToggleButton control, bool check) { - if (control == null) + if (control is null) throw new ArgumentNullException(nameof(control)); if (control.IsChecked != check) control.IsChecked = check; @@ -164,7 +164,7 @@ public static void SetChecked(System.Windows.Controls.Primitives.ToggleButton co /// This helps not to trigger control events when doing frequent events. public static void SetEnabled(UIElement control, bool enabled) { - if (control == null) + if (control is null) throw new ArgumentNullException(nameof(control)); if (control.IsEnabled != enabled) control.IsEnabled = enabled; @@ -175,7 +175,7 @@ public static void SetEnabled(UIElement control, bool enabled) /// This helps not to trigger control events when doing frequent events. public static void SetVisible(UIElement control, bool enabled) { - if (control == null) + if (control is null) throw new ArgumentNullException(nameof(control)); var visibility = enabled ? Visibility.Visible : Visibility.Collapsed; if (control.Visibility != visibility) @@ -184,7 +184,7 @@ public static void SetVisible(UIElement control, bool enabled) public static void SetItemsSource(ItemsControl grid, IBindingList list) { - if (list == null) + if (list is null) { if (grid.ItemsSource is System.Windows.Data.BindingListCollectionView view) { @@ -242,7 +242,7 @@ private static void link_RequestNavigate(object sender, System.Windows.Navigatio public static Point[] GetPoints(Control control, bool relative = false) { - if (control == null) + if (control is null) throw new ArgumentNullException(nameof(control)); var pos = relative ? new Point(0, 0) @@ -268,7 +268,7 @@ public static Point[] GetPoints(Control control, bool relative = false) /* public static bool IsControlVisibleToUser(Control control) { - if (control == null) + if (control is null) throw new ArgumentNullException(nameof(control)); var handle = (PresentationSource.FromVisual(control) as System.Windows.Interop.HwndSource)?.Handle; if (!handle.HasValue) @@ -282,12 +282,12 @@ public static bool IsControlVisibleToUser(Control control) //if (hwnd == IntPtr.Zero) // continue; var result = VisualTreeHelper.HitTest(control, p); - if (result == null) + if (result is null) continue; if (children.Contains(result.VisualHit)) return true; //var other = Control.FromChildHandle(hwnd); - //if (other == null) + //if (other is null) // continue; //if (GetAll(control, null, true).Contains(other)) } @@ -299,7 +299,7 @@ public static bool IsControlVisibleToUser(Control control) /// public static T GetParent(DependencyObject control, bool includeTop = false) where T : class { - if (control == null) + if (control is null) throw new ArgumentNullException(nameof(control)); var parent = control; while (parent != null) @@ -307,7 +307,7 @@ public static T GetParent(DependencyObject control, bool includeTop = false) if (parent is T && (includeTop || parent != control)) return (T)(object)parent; var p = VisualTreeHelper.GetParent(parent); - if (p == null) + if (p is null) p = LogicalTreeHelper.GetParent(parent); parent = p; } @@ -317,7 +317,7 @@ public static T GetParent(DependencyObject control, bool includeTop = false) public static void AddWeakHandlerOnWindowClosing(DependencyObject control, EventHandler handler) { var w = GetParent(control); - if (w == null) + if (w is null) return; WeakEventManager.AddHandler(w, nameof(Window.Closing), handler); } @@ -332,7 +332,7 @@ public static Dictionary GetAll(string path, Dependenc { var controls = _GetAll(path, control, includeTop); // If type is set then... - if (type == null) + if (type is null) return controls; var filtered = type.IsInterface ? controls.Where(x => x.Value.GetType().GetInterfaces().Contains(type)) @@ -343,7 +343,7 @@ public static Dictionary GetAll(string path, Dependenc private static Dictionary _GetAll(string path, DependencyObject control, bool includeTop = false) { - if (control == null) + if (control is null) throw new ArgumentNullException(nameof(control)); // Create new list. var controls = new Dictionary(); @@ -406,7 +406,7 @@ public static IEnumerable GetAll(DependencyObject control, Typ /// public static T[] GetAll(Control control, bool includeTop = false) { - if (control == null) + if (control is null) return new T[0]; return GetAll(control, typeof(T), includeTop).Cast().ToArray(); } @@ -426,7 +426,7 @@ public static void GetActiveControl(FrameworkElement control, out FrameworkEleme while (container != null) { control = System.Windows.Input.FocusManager.GetFocusedElement(control) as FrameworkElement; - if (control == null) + if (control is null) break; Invoke(() => { @@ -445,7 +445,7 @@ public static void GetActiveControl(FrameworkElement control, out FrameworkEleme public static void ApplyBorderStyle(DataGrid grid) { - if (grid == null) + if (grid is null) throw new ArgumentNullException(nameof(grid)); grid.Background = new SolidColorBrush(Color.FromRgb(255, 255, 255)); //grid.BorderThickness = BorderStyle.None; @@ -495,7 +495,7 @@ private static void Grid_CellFormatting(object sender, DataGridViewCellFormattin { var item = row.DataBoundItem; // If grid is virtual then... - if (item == null) + if (item is null) { var list = grid.DataSource as IBindingList; if (list != null) @@ -539,7 +539,7 @@ private static void SetEnabled(object item, bool enabled) private static bool GetEnabled(object item) { var enabledProperty = item.GetType().GetProperties().FirstOrDefault(x => x.Name == "Enabled" || x.Name == "IsEnabled"); - var enabled = enabledProperty == null ? true : (bool)enabledProperty.GetValue(item, null); + var enabled = enabledProperty is null ? true : (bool)enabledProperty.GetValue(item, null); return enabled; } @@ -617,7 +617,7 @@ private static void Grid_CellPainting(object sender, DataGridViewCellPaintingEve /// Primary key name. public static List GetSelection(DataGrid grid, string keyPropertyName = null) { - if (grid == null) + if (grid is null) throw new ArgumentNullException(nameof(grid)); var list = new List(); var items = grid.SelectedItems.Cast().ToArray(); @@ -636,18 +636,22 @@ public static List GetSelection(DataGrid grid, string keyPropertyName = nu return list; } + [Obsolete] public static void RestoreSelection(DataGrid grid, string keyPropertyName, List list, bool selectFirst = true) { - if (grid == null) + RestoreSelection(grid, keyPropertyName, list, selectFirst ? 0 : -1); + } + + public static bool RestoreSelection(DataGrid grid, string keyPropertyName, List list, int selectIndex = 0) + { + if (grid is null) throw new ArgumentNullException(nameof(grid)); - if (list == null) - throw new ArgumentNullException(nameof(list)); var items = grid.Items.Cast().ToArray(); // Return if grid is empty. if (items.Length == 0) - return; + return false; // If something to restore then... - if (list.Count > 0) + if (list?.Count > 0) { var selectedItems = new List(); var pi = GetPropertyInfo(keyPropertyName, items[0]); @@ -673,9 +677,10 @@ public static void RestoreSelection(DataGrid grid, string keyPropertyName, Li grid.SelectedItems.Add(item); } } - // If must select first row and nothing is selected then... - if (selectFirst && grid.SelectedItems.Count == 0) - grid.SelectedItem = items[0]; + // If nothing was selected and must select index then... + if (grid.SelectedItems.Count == 0 && selectIndex >= 0 && selectIndex < grid.Items.Count) + grid.SelectedItem = items[selectIndex]; + return grid.SelectedItems.Count > 0; } #endregion diff --git a/FocusLogger/JocysCom/Controls/ControlsHelper.cs b/FocusLogger/JocysCom/Controls/ControlsHelper.cs index ee60e66..4f9c4d7 100644 --- a/FocusLogger/JocysCom/Controls/ControlsHelper.cs +++ b/FocusLogger/JocysCom/Controls/ControlsHelper.cs @@ -1,14 +1,13 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; -using System.Windows; namespace JocysCom.ClassLibrary.Controls { @@ -131,7 +130,7 @@ public static Task BeginInvoke(Delegate method, params object[] args) /// The action delegate to execute synchronously. public static void Invoke(Action action) { - if (action == null) + if (action is null) throw new ArgumentNullException(nameof(action)); InitInvokeContext(); if (InvokeRequired) @@ -149,7 +148,7 @@ public static void Invoke(Action action) /// The delegate to execute synchronously. public static object Invoke(Delegate method, params object[] args) { - if (method == null) + if (method is null) throw new ArgumentNullException(nameof(method)); // Run method on main Graphical User Interface thread. if (InvokeRequired) @@ -211,24 +210,38 @@ public static void OpenPath(string path, string arguments = null) { try { - var fi = new System.IO.FileInfo(path); - // Brings up the "Windows cannot open this file" dialog if association not found. - var psi = new System.Diagnostics.ProcessStartInfo(path); - psi.UseShellExecute = true; - psi.WorkingDirectory = fi.Directory.FullName; - psi.ErrorDialog = true; - if (arguments != null) - psi.Arguments = arguments; + + var psi = new System.Diagnostics.ProcessStartInfo(); + if (Uri.TryCreate(path, UriKind.Absolute, out Uri uri) && uri.Scheme != Uri.UriSchemeFile) + { + // Open URL + psi.UseShellExecute = true; + psi.FileName = uri.AbsoluteUri; + if (arguments != null) + psi.Arguments = arguments; + psi.WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + } + else + { + // Open file/directory + psi.FileName = path; + if (arguments != null) + psi.Arguments = arguments; + var fi = new System.IO.FileInfo(path); + psi.UseShellExecute = true; + psi.ErrorDialog = true; + psi.WorkingDirectory = fi.Directory?.FullName ?? Environment.CurrentDirectory; + } System.Diagnostics.Process.Start(psi); } - catch (Exception) { } + catch { } } #endregion public static PropertyInfo GetPrimaryKeyPropertyInfo(object item) { - if (item == null) + if (item is null) return null; var t = item.GetType(); PropertyInfo pi = null; @@ -310,7 +323,7 @@ public static bool IsOnCooldown(object control, int? milliseconds = null) { var now = DateTime.Now; // Get expired controls. - var keys = ControlCooldowns.Where(x => now > x.Value).Select(x => x.Key).ToList(); + var keys = ControlCooldowns.Where(x => now > x.Value).Select(x => x.Key).ToList(); // Cleanup the list. foreach (var key in keys) ControlCooldowns.Remove(key); @@ -327,5 +340,24 @@ public static bool IsOnCooldown(object control, int? milliseconds = null) #endregion + #region Enums + + public static string GetStringFromValue(this T value) where T : Enum + { + return Regex.Replace(value.ToString(), "(\\B[A-Z])", " $1"); + } + + public static Dictionary GetDictionary(T[] values = null) where T : Enum + { + if (values == null) + values = (T[])Enum.GetValues(typeof(T)); + var dict = new Dictionary(); + foreach (var value in values) + dict[value] = value.GetStringFromValue(); + return dict; + } + + #endregion + } } diff --git a/FocusLogger/JocysCom/Controls/InfoControl.xaml b/FocusLogger/JocysCom/Controls/InfoControl.xaml index 9c96b3e..f914f2e 100644 --- a/FocusLogger/JocysCom/Controls/InfoControl.xaml +++ b/FocusLogger/JocysCom/Controls/InfoControl.xaml @@ -46,7 +46,7 @@ Margin="8,0,3,3" Padding="0" Content="{StaticResource Icon_Information}" - Style="{StaticResource ButtonContentControl}" /> + Focusable="False" /> + Content="{StaticResource Icon_Information}" /> + +