diff --git a/FSI.Lib/FSI.Lib.sln b/FSI.Lib/FSI.Lib.sln index 602fb0e..a5645c1 100644 --- a/FSI.Lib/FSI.Lib.sln +++ b/FSI.Lib/FSI.Lib.sln @@ -1,9 +1,9 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.31321.278 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.32112.339 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FSI.Lib", "FSI.Lib\FSI.Lib.csproj", "{1394B606-4AAC-4D8D-BAF4-DB38E1FCEAAA}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FSI.Lib", "FSI.Lib\FSI.Lib.csproj", "{1394B606-4AAC-4D8D-BAF4-DB38E1FCEAAA}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/FSI.Lib/FSI.Lib/FSI.Lib.csproj b/FSI.Lib/FSI.Lib/FSI.Lib.csproj index 24af52c..8539040 100644 --- a/FSI.Lib/FSI.Lib/FSI.Lib.csproj +++ b/FSI.Lib/FSI.Lib/FSI.Lib.csproj @@ -34,6 +34,7 @@ + diff --git a/FSI.Lib/FSI.Lib/Guis/AutoPw/FrmMain.xaml.cs b/FSI.Lib/FSI.Lib/Guis/AutoPw/FrmMain.xaml.cs index dbdd48a..0d693f3 100644 --- a/FSI.Lib/FSI.Lib/Guis/AutoPw/FrmMain.xaml.cs +++ b/FSI.Lib/FSI.Lib/Guis/AutoPw/FrmMain.xaml.cs @@ -1,6 +1,9 @@ -using System; +using Microsoft.Extensions.Logging; +using NLog; +using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Text; using System.Threading.Tasks; using System.Windows; @@ -19,15 +22,17 @@ namespace FSI.Lib.Guis.AutoPw /// public partial class FrmMain : Window { + private Logger _log; public bool CloseAtLostFocus { get; set; } public bool PwOk { get; set; } public FrmMain() { InitializeComponent(); + _log = LogManager.GetCurrentClassLogger(); BtnOk.IsEnabled = false; Deactivated += FrmMain_Deactivated; - DataContext = new MVVM.ViewModel.CurrentTimeViewModel(); + DataContext = new MVVM.ViewModel.CurrentTimeViewModel(); } private void FrmMain_Deactivated(object sender, System.EventArgs e) @@ -38,6 +43,8 @@ namespace FSI.Lib.Guis.AutoPw private void BtnOk_Click(object sender, RoutedEventArgs e) { + + _log.Info(GetType().Namespace); Close(); } @@ -79,11 +86,6 @@ namespace FSI.Lib.Guis.AutoPw } Close(); - } - - private void TbPw_KeyDown_1(object sender, KeyEventArgs e) - { - - } + } } } diff --git a/FSI.Lib/FSI.Lib/Guis/Folder.Mgt/FrmMain.xaml b/FSI.Lib/FSI.Lib/Guis/Folder.Mgt/FrmMain.xaml index 065826e..5994784 100644 --- a/FSI.Lib/FSI.Lib/Guis/Folder.Mgt/FrmMain.xaml +++ b/FSI.Lib/FSI.Lib/Guis/Folder.Mgt/FrmMain.xaml @@ -148,8 +148,6 @@ - diff --git a/FSI.Lib/FSI.Lib/Guis/Folder.Mgt/ViewModel.cs b/FSI.Lib/FSI.Lib/Guis/Folder.Mgt/ViewModel.cs index de51066..538f021 100644 --- a/FSI.Lib/FSI.Lib/Guis/Folder.Mgt/ViewModel.cs +++ b/FSI.Lib/FSI.Lib/Guis/Folder.Mgt/ViewModel.cs @@ -174,6 +174,9 @@ namespace FSI.Lib.Guis.Folder.Mgt newDatas.Add(newData); } + + newDatas = new ObservableCollection(newDatas.OrderBy(i => i.DescriptionDtl)); + return newDatas; } } diff --git a/FSI.Lib/FSI.Lib/Guis/IbaDirSync/Model.cs b/FSI.Lib/FSI.Lib/Guis/IbaDirSync/Model.cs new file mode 100644 index 0000000..bc89075 --- /dev/null +++ b/FSI.Lib/FSI.Lib/Guis/IbaDirSync/Model.cs @@ -0,0 +1,9 @@ +namespace FSI.Lib.Guis.IbaDirSync +{ + public interface IInterface + { + public bool AutoStart { get; set; } + public string Source { get; set; } + public string Destination { get; set; } + } +} diff --git a/FSI.Lib/FSI.Lib/Guis/IbaDirSync/Model/Iba.cs b/FSI.Lib/FSI.Lib/Guis/IbaDirSync/Model/Iba.cs deleted file mode 100644 index aaa43ee..0000000 --- a/FSI.Lib/FSI.Lib/Guis/IbaDirSync/Model/Iba.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace FSI.Lib.Guis.IbaDirSync.Model -{ - public class Iba - { - public string Source { get; set; } - public string Destination { get; set; } - public bool AutoStart { get; set; } - } -} diff --git a/FSI.Lib/FSI.Lib/Guis/IbaDirSync/ViewModel/ViewModelIba.cs b/FSI.Lib/FSI.Lib/Guis/IbaDirSync/ViewModel.cs similarity index 57% rename from FSI.Lib/FSI.Lib/Guis/IbaDirSync/ViewModel/ViewModelIba.cs rename to FSI.Lib/FSI.Lib/Guis/IbaDirSync/ViewModel.cs index 06a9d3e..ea63b28 100644 --- a/FSI.Lib/FSI.Lib/Guis/IbaDirSync/ViewModel/ViewModelIba.cs +++ b/FSI.Lib/FSI.Lib/Guis/IbaDirSync/ViewModel.cs @@ -1,59 +1,30 @@ -using FSI.Lib.Guis.IbaDirSync.Model; -using FSI.Lib.MVVM; +using FSI.Lib.MVVM; using RoboSharp; using System.Text; using System.Windows.Input; -namespace FSI.Lib.Guis.IbaDirSync.ViewModel +namespace FSI.Lib.Guis.IbaDirSync { - public class ViewModelIba : ViewModelBase + public class ViewModel : ViewModelBase { private ICommand _cmdStart; private ICommand _cmdStop; - private Iba _iba; - - public ViewModelIba() + public ViewModel() { - Iba = new Model.Iba(); - Init(); - } - - public ViewModelIba(string destination, string source, bool autoStart) - { - _iba = new Model.Iba() - { - Destination = destination, - Source = source, - AutoStart = autoStart, - }; - Init(); - } - - private void Init() - { _cmdStart = new RelayCommand(ExecuteStart, CanExecuteStart); _cmdStop = new RelayCommand(ExecuteStop, CanExecuteStop); Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); - RoboCopy = new RoboSharp.RoboCommand(); - - if (_iba.AutoStart) - ExecuteStart(null); + RoboCopy = new RoboSharp.RoboCommand(); } - public Model.Iba Iba + public IInterface Data { get; set; } + + public void Init() { - get + if (Data.AutoStart) { - return _iba; - } - set - { - if (_iba != value & _iba != null) - { - _iba = value; - OnPropertyChanged(); - } + ExecuteStart(null); } } @@ -93,8 +64,8 @@ namespace FSI.Lib.Guis.IbaDirSync.ViewModel private void ExecuteStart(object obj) { - RoboCopy.CopyOptions.Source = Iba.Source; - RoboCopy.CopyOptions.Destination = Iba.Destination; + RoboCopy.CopyOptions.Source = Data.Source; + RoboCopy.CopyOptions.Destination = Data.Destination; RoboCopy.CopyOptions.CopySubdirectories = true; RoboCopy.CopyOptions.MonitorSourceChangesLimit = 1; RoboCopy.StopIfDisposing = true; diff --git a/FSI.Lib/FSI.Lib/Guis/SetSizePosExWindow/Model/Window.cs b/FSI.Lib/FSI.Lib/Guis/SetSizePosExWindow/Model.cs similarity index 78% rename from FSI.Lib/FSI.Lib/Guis/SetSizePosExWindow/Model/Window.cs rename to FSI.Lib/FSI.Lib/Guis/SetSizePosExWindow/Model.cs index 049f490..3d60995 100644 --- a/FSI.Lib/FSI.Lib/Guis/SetSizePosExWindow/Model/Window.cs +++ b/FSI.Lib/FSI.Lib/Guis/SetSizePosExWindow/Model.cs @@ -4,13 +4,14 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace FSI.Lib.Guis.SetSizePosExWindow.Model +namespace FSI.Lib.Guis.SetSizePosExWindow { - public class Window + public class IInterface { public string Bezeichnung { get; set; } public string Name { get; set; } public string ClassName { get; set; } + public bool AutoStart { get; set; } public int Height { get; set; } public int Width { get; set; } public int X { get; set; } diff --git a/FSI.Lib/FSI.Lib/Guis/SetSizePosExWindow/ViewModel/ViewModelWindow.cs b/FSI.Lib/FSI.Lib/Guis/SetSizePosExWindow/ViewModel.cs similarity index 87% rename from FSI.Lib/FSI.Lib/Guis/SetSizePosExWindow/ViewModel/ViewModelWindow.cs rename to FSI.Lib/FSI.Lib/Guis/SetSizePosExWindow/ViewModel.cs index b54cf42..22de213 100644 --- a/FSI.Lib/FSI.Lib/Guis/SetSizePosExWindow/ViewModel/ViewModelWindow.cs +++ b/FSI.Lib/FSI.Lib/Guis/SetSizePosExWindow/ViewModel.cs @@ -1,5 +1,4 @@ -using FSI.Lib.Guis.SetSizePosExWindow.Model; -using FSI.Lib.MVVM; +using FSI.Lib.MVVM; using System; using System.Collections.ObjectModel; using System.ComponentModel; @@ -8,9 +7,9 @@ using System.Threading; using System.Threading.Tasks; using System.Windows.Input; -namespace FSI.Lib.Guis.SetSizePosExWindow.ViewModel +namespace FSI.Lib.Guis.SetSizePosExWindow { - public class ViewModelWindow : ViewModelBase + public class ViewModel : ViewModelBase { [DllImport("user32.dll")] public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); @@ -65,39 +64,15 @@ namespace FSI.Lib.Guis.SetSizePosExWindow.ViewModel private BackgroundWorker backgroundWorker; private ICommand _cmdStart; private ICommand _cmdStop; - private ObservableCollection _windows; + private ObservableCollection _windows; private int _updateIntervall; private bool _autoStart; - private Window _selectedWindow; + private IInterface _selectedWindow; - public ViewModelWindow() + public ViewModel() { - _windows = new ObservableCollection(); + _windows = new ObservableCollection(); - Init(); - - if (AutoStart) - { - ExecuteStart(null); - } - } - - public ViewModelWindow(bool autoStart) - { - _windows = new ObservableCollection(); - - Init(); - - AutoStart = autoStart; - - if (AutoStart) - { - ExecuteStart(null); - } - } - - private void Init() - { _cmdStart = new RelayCommand(ExecuteStart, CanExecuteStart); _cmdStop = new RelayCommand(ExecuteStop, CanExecuteStop); @@ -110,7 +85,17 @@ namespace FSI.Lib.Guis.SetSizePosExWindow.ViewModel backgroundWorker.ProgressChanged += BackgroundWorker_ProgressChanged; } - public ObservableCollection Windows + public IInterface Data { get; set; } + + public void Init() + { + if (Data.AutoStart) + { + ExecuteStart(null); + } + } + + public ObservableCollection Windows { get { @@ -126,7 +111,7 @@ namespace FSI.Lib.Guis.SetSizePosExWindow.ViewModel } } - public Model.Window SelectedWindow + public IInterface SelectedWindow { get { @@ -232,7 +217,7 @@ namespace FSI.Lib.Guis.SetSizePosExWindow.ViewModel } else { - foreach (Window window in Windows) + foreach (IInterface window in Windows) { // Perform a time consuming operation and report progress. Thread.Sleep(UpdateIntervall); diff --git a/FSI.Lib/FSI.Lib/Guis/SieTiaWinCCMsgMgt/FrmMain.xaml.cs b/FSI.Lib/FSI.Lib/Guis/SieTiaWinCCMsgMgt/FrmMain.xaml.cs index cad8808..8ab58bb 100644 --- a/FSI.Lib/FSI.Lib/Guis/SieTiaWinCCMsgMgt/FrmMain.xaml.cs +++ b/FSI.Lib/FSI.Lib/Guis/SieTiaWinCCMsgMgt/FrmMain.xaml.cs @@ -28,7 +28,7 @@ namespace FSI.Lib.Guis.SieTiaWinCCMsgMgt private void btnStart_Click(object sender, RoutedEventArgs e) { - //WinCC.Start(); + // WinCC.Start(); CtrlMgt(); } diff --git a/FSI.Lib/FSI.Lib/Guis/SieTiaWinCCMsgMgt/Model/WinCC.cs b/FSI.Lib/FSI.Lib/Guis/SieTiaWinCCMsgMgt/Model.cs similarity index 57% rename from FSI.Lib/FSI.Lib/Guis/SieTiaWinCCMsgMgt/Model/WinCC.cs rename to FSI.Lib/FSI.Lib/Guis/SieTiaWinCCMsgMgt/Model.cs index b57c29c..033d4c3 100644 --- a/FSI.Lib/FSI.Lib/Guis/SieTiaWinCCMsgMgt/Model/WinCC.cs +++ b/FSI.Lib/FSI.Lib/Guis/SieTiaWinCCMsgMgt/Model.cs @@ -1,12 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Collections.Generic; -namespace FSI.Lib.Guis.SieTiaWinCCMsgMgt.Model +namespace FSI.Lib.Guis.SieTiaWinCCMsgMgt { - public class WinCC + public interface IInterface { public bool AutoStart { get; set; } public int UpdateIntervall { get; set; } @@ -14,4 +10,5 @@ namespace FSI.Lib.Guis.SieTiaWinCCMsgMgt.Model public string WindowsClassName { get; set; } public string ButtonName { get; set; } } + } diff --git a/FSI.Lib/FSI.Lib/Guis/SieTiaWinCCMsgMgt/ViewModel/ViewModelWinCC.cs b/FSI.Lib/FSI.Lib/Guis/SieTiaWinCCMsgMgt/ViewModel.cs similarity index 69% rename from FSI.Lib/FSI.Lib/Guis/SieTiaWinCCMsgMgt/ViewModel/ViewModelWinCC.cs rename to FSI.Lib/FSI.Lib/Guis/SieTiaWinCCMsgMgt/ViewModel.cs index e0ce4d6..f773fae 100644 --- a/FSI.Lib/FSI.Lib/Guis/SieTiaWinCCMsgMgt/ViewModel/ViewModelWinCC.cs +++ b/FSI.Lib/FSI.Lib/Guis/SieTiaWinCCMsgMgt/ViewModel.cs @@ -1,5 +1,4 @@ -using FSI.Lib.Guis.SieTiaWinCCMsgMgt.Model; -using FSI.Lib.MVVM; +using FSI.Lib.MVVM; using System; using System.ComponentModel; using System.Runtime.InteropServices; @@ -7,9 +6,9 @@ using System.Threading; using System.Threading.Tasks; using System.Windows.Input; -namespace FSI.Lib.Guis.SieTiaWinCCMsgMgt.ViewModel +namespace FSI.Lib.Guis.SieTiaWinCCMsgMgt { - public class ViewModelWinCC : MVVM.ViewModelBase + public class ViewModel : MVVM.ViewModelBase { [DllImport("user32.dll")] public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); @@ -29,7 +28,7 @@ namespace FSI.Lib.Guis.SieTiaWinCCMsgMgt.ViewModel [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] - + static extern bool IsWindowVisible(IntPtr hWnd); const int BM_CLICK = 0x00F5; //message for Click which is a constant @@ -37,43 +36,14 @@ namespace FSI.Lib.Guis.SieTiaWinCCMsgMgt.ViewModel const int WM_LBUTTONUP = 0x0202; // message for Mouse up private CancellationTokenSource tokenSource = null; - private CancellationToken token; private Task task = null; - - private BackgroundWorker backgroundWorker; + private BackgroundWorker backgroundWorker; private ICommand _cmdStart; private ICommand _cmdStop; - private WinCC _winCC; - public ViewModelWinCC() - { - WinCC = new Model.WinCC(); - Init(); - } - - public ViewModelWinCC(bool autoStart, int updateIntervall, string windowsName, string windowsClassName, string buttonName) - { - _winCC = new Model.WinCC - { - AutoStart = autoStart, - UpdateIntervall = updateIntervall, - WindowsName = windowsName, - WindowsClassName = windowsClassName, - ButtonName = buttonName - }; - - //_seletctedWinCC = WinCC; - - Init(); - - if (WinCC.AutoStart) - { - ExecuteStart(null); - } - } - - private void Init() + + public ViewModel() { _cmdStart = new RelayCommand(ExecuteStart, CanExecuteStart); _cmdStop = new RelayCommand(ExecuteStop, CanExecuteStop); @@ -84,22 +54,16 @@ namespace FSI.Lib.Guis.SieTiaWinCCMsgMgt.ViewModel WorkerSupportsCancellation = true }; backgroundWorker.DoWork += BackgroundWorker_DoWork; - backgroundWorker.ProgressChanged += BackgroundWorker_ProgressChanged; + backgroundWorker.ProgressChanged += BackgroundWorker_ProgressChanged; ; } + + public IInterface Data { get; set; } - public Model.WinCC WinCC + public void Init() { - get + if (Data.AutoStart) { - return _winCC; - } - set - { - if (_winCC != value & _winCC != null) - { - _winCC = value; - OnPropertyChanged(); - } + ExecuteStart(null); } } @@ -129,7 +93,6 @@ namespace FSI.Lib.Guis.SieTiaWinCCMsgMgt.ViewModel set => _cmdStop = value; } - private bool CanExecuteStart(object obj) { return !Status; @@ -163,44 +126,23 @@ namespace FSI.Lib.Guis.SieTiaWinCCMsgMgt.ViewModel else { // Perform a time consuming operation and report progress. - Thread.Sleep(WinCC.UpdateIntervall); + Thread.Sleep(Data.UpdateIntervall); // Find windos by Name - var windowHandle = FindWindow(WinCC.WindowsClassName, WinCC.WindowsName); + var windowHandle = FindWindow(Data.WindowsClassName, Data.WindowsName); if (windowHandle != IntPtr.Zero && IsWindowVisible(windowHandle)) { SetForegroundWindow(windowHandle); - var btnHandle = FindWindowEx(windowHandle, IntPtr.Zero, null, WinCC.ButtonName); + var btnHandle = FindWindowEx(windowHandle, IntPtr.Zero, null, Data.ButtonName); SendMessage(btnHandle, BM_CLICK, IntPtr.Zero, IntPtr.Zero); } } } } - public bool Status { get; set; } - private void WinCCMsgMgt() - { - while (true) - { - try - { - if (token.IsCancellationRequested) - { - token.ThrowIfCancellationRequested(); - } - //MessageBox.Show("13456"); - Thread.Sleep(5000); - } - catch (OperationCanceledException) - { - break; - } - } - } - protected virtual void Dispose(bool disposing) { bool disposedValue = false; @@ -229,6 +171,5 @@ namespace FSI.Lib.Guis.SieTiaWinCCMsgMgt.ViewModel task = null; } } - } } diff --git a/FSI.Lib/FSI.Lib/Guis/TxtToClip.Mgt/Model.cs b/FSI.Lib/FSI.Lib/Guis/TxtToClip.Mgt/Model.cs index 92d54ae..bb040cc 100644 --- a/FSI.Lib/FSI.Lib/Guis/TxtToClip.Mgt/Model.cs +++ b/FSI.Lib/FSI.Lib/Guis/TxtToClip.Mgt/Model.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Collections.Generic; namespace FSI.Lib.Guis.TxtToClip.Mgt { diff --git a/FSI.Lib/FSI.Lib/Guis/TxtToClip.Mgt/ViewModel.cs b/FSI.Lib/FSI.Lib/Guis/TxtToClip.Mgt/ViewModel.cs index a995ae9..cac8e42 100644 --- a/FSI.Lib/FSI.Lib/Guis/TxtToClip.Mgt/ViewModel.cs +++ b/FSI.Lib/FSI.Lib/Guis/TxtToClip.Mgt/ViewModel.cs @@ -143,6 +143,8 @@ namespace FSI.Lib.Guis.TxtToClip.Mgt newDatas.Add(newData); } + + newDatas = new ObservableCollection(newDatas.OrderBy(i => i.DescriptionDtl)); return newDatas; } } diff --git a/FSI.Lib/NLogViewer/ActionCommand.cs b/FSI.Lib/NLogViewer/ActionCommand.cs new file mode 100644 index 0000000..67db679 --- /dev/null +++ b/FSI.Lib/NLogViewer/ActionCommand.cs @@ -0,0 +1,42 @@ +using System; +using System.Windows.Input; + +namespace DJ +{ + public sealed class ActionCommand : ICommand + { + private readonly Action _Action; + private readonly Action _ObjectAction; + + public ActionCommand(Action action) + { + _Action = action; + } + + public ActionCommand(Action objectAction) + { + _ObjectAction = objectAction; + } + + private event EventHandler CanExecuteChanged; + + event EventHandler ICommand.CanExecuteChanged + { + add => CanExecuteChanged += value; + remove => CanExecuteChanged -= value; + } + + bool ICommand.CanExecute(object parameter) + { + return true; + } + + public void Execute(object parameter) + { + if (_ObjectAction != null) + _ObjectAction(parameter); + else + _Action(); + } + } +} \ No newline at end of file diff --git a/FSI.Lib/NLogViewer/Extensions/DependencyObjectExtensions.cs b/FSI.Lib/NLogViewer/Extensions/DependencyObjectExtensions.cs new file mode 100644 index 0000000..8843d89 --- /dev/null +++ b/FSI.Lib/NLogViewer/Extensions/DependencyObjectExtensions.cs @@ -0,0 +1,72 @@ +using System.Collections.Generic; +using System.Windows; +using System.Windows.Media; + +namespace DJ.Extensions +{ + public static class DependencyObjectExtensions + { + /// + /// Analyzes both visual and logical tree in order to find all elements of a given + /// type that are descendants of the item. + /// + /// The type of the queried items. + /// The root element that marks the source of the search. If the + /// source is already of the requested type, it will not be included in the result. + /// The UID of the + /// All descendants of that match the requested type. + public static T FindChildByUid(this DependencyObject source, string uid) where T : UIElement + { + if (source != null) + { + var childs = GetChildObjects(source); + foreach (DependencyObject child in childs) + { + //analyze if children match the requested type + if (child != null && child is T dependencyObject && dependencyObject.Uid.Equals(uid)) + { + return dependencyObject; + } + + var descendant = FindChildByUid(child, uid); + if (descendant != null) + return descendant; + } + } + + return null; + } + + /// + /// This method is an alternative to WPF's + /// method, which also + /// supports content elements. Keep in mind that for content elements, + /// this method falls back to the logical tree of the element. + /// + /// The item to be processed. + /// The submitted item's child elements, if available. + public static IEnumerable GetChildObjects(this DependencyObject parent) + { + if (parent == null) yield break; + + if (parent is ContentElement || parent is FrameworkElement) + { + //use the logical tree for content / framework elements + foreach (object obj in LogicalTreeHelper.GetChildren(parent)) + { + var depObj = obj as DependencyObject; + if (depObj != null) yield return (DependencyObject) obj; + } + } + else + { + //use the visual tree per default + int count = VisualTreeHelper.GetChildrenCount(parent); + for (int i = 0; i < count; i++) + { + yield return VisualTreeHelper.GetChild(parent, i); + } + } + } + } +} diff --git a/FSI.Lib/NLogViewer/Helper/AutoSizedGridView.cs b/FSI.Lib/NLogViewer/Helper/AutoSizedGridView.cs new file mode 100644 index 0000000..ea2459e --- /dev/null +++ b/FSI.Lib/NLogViewer/Helper/AutoSizedGridView.cs @@ -0,0 +1,43 @@ +using System; +using System.Reactive.Linq; +using System.Threading; +using System.Windows.Controls; +using NLog; + +namespace DJ.Helper +{ + /// + /// Represents a view mode that displays data items in columns for a System.Windows.Controls.ListView control with auto sized columns based on the column content + /// Used to fix the column width: https://stackoverflow.com/questions/60147905/column-width-adjustment-is-broken-if-using-multibinding-on-displaymemberbinding + /// + public class AutoSizedGridView : GridView + { + private int _MaxLoggerNameLength; + + protected override void PrepareItem(ListViewItem item) + { + if (item.DataContext is LogEventInfo info) + { + if (info.LoggerName.Length > _MaxLoggerNameLength) + { + _MaxLoggerNameLength = info.LoggerName.Length; + Observable.Timer(TimeSpan.FromMilliseconds(1)).ObserveOn(SynchronizationContext.Current).Subscribe(l => + { + foreach (GridViewColumn column in Columns) + { + //setting NaN for the column width automatically determines the required width enough to hold the content completely. + //if column width was set to NaN already, set it ActualWidth temporarily and set to NaN. This raises the property change event and re computes the width. + if (double.IsNaN(column.Width)) + { + column.Width = column.ActualWidth; + column.Width = double.NaN; + } + } + }); + } + } + + base.PrepareItem(item); + } + } +} diff --git a/FSI.Lib/NLogViewer/Helper/ListViewLayoutManager/ConverterGridViewColumn.cs b/FSI.Lib/NLogViewer/Helper/ListViewLayoutManager/ConverterGridViewColumn.cs new file mode 100644 index 0000000..54febf0 --- /dev/null +++ b/FSI.Lib/NLogViewer/Helper/ListViewLayoutManager/ConverterGridViewColumn.cs @@ -0,0 +1,59 @@ +using System; +using System.Globalization; +using System.Windows.Controls; +using System.Windows.Data; + +namespace DJ.Helper.ListViewLayoutManager +{ + public abstract class ConverterGridViewColumn : GridViewColumn, IValueConverter + { + public Type BindingType => _BindingType; + private readonly Type _BindingType; + + // ############################################################################################################################## + // Constructor + // ############################################################################################################################## + + #region Constructor + + protected ConverterGridViewColumn(Type bindingType) + { + if (bindingType == null) + { + throw new ArgumentNullException(nameof(bindingType)); + } + + this._BindingType = bindingType; + + Binding binding = new Binding {Mode = BindingMode.OneWay, Converter = this}; + DisplayMemberBinding = binding; + } + + #endregion + + // ############################################################################################################################## + // IValueConverter + // ############################################################################################################################## + + #region IValueConverter + + object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (!_BindingType.IsInstanceOfType(value)) + { + throw new InvalidOperationException(); + } + + return ConvertValue(value); + } + + object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + + protected abstract object ConvertValue(object value); + + #endregion + } +} \ No newline at end of file diff --git a/FSI.Lib/NLogViewer/Helper/ListViewLayoutManager/FixedColumn.cs b/FSI.Lib/NLogViewer/Helper/ListViewLayoutManager/FixedColumn.cs new file mode 100644 index 0000000..5a64ab5 --- /dev/null +++ b/FSI.Lib/NLogViewer/Helper/ListViewLayoutManager/FixedColumn.cs @@ -0,0 +1,61 @@ +using System.Windows; +using System.Windows.Controls; + +namespace DJ.Helper.ListViewLayoutManager +{ + public sealed class FixedColumn : LayoutColumn + { + public static bool IsFixedColumn(GridViewColumn column) + { + if (column == null) + { + return false; + } + + return HasPropertyValue(column, WidthProperty); + } + + public static double? GetFixedWidth(GridViewColumn column) + { + return GetColumnWidth(column, WidthProperty); + } + + public static GridViewColumn ApplyWidth(GridViewColumn gridViewColumn, double width) + { + SetWidth(gridViewColumn, width); + return gridViewColumn; + } + + // ############################################################################################################################## + // AttachedProperties + // ############################################################################################################################## + + #region AttachedProperties + + public static double GetWidth(DependencyObject obj) + { + return (double) obj.GetValue(WidthProperty); + } + + public static void SetWidth(DependencyObject obj, double width) + { + obj.SetValue(WidthProperty, width); + } + + public static readonly DependencyProperty WidthProperty = DependencyProperty.RegisterAttached("Width", typeof(double), typeof(FixedColumn)); + + #endregion + + // ############################################################################################################################## + // Constructor + // ############################################################################################################################## + + #region Constructor + + private FixedColumn() + { + } + + #endregion + } +} \ No newline at end of file diff --git a/FSI.Lib/NLogViewer/Helper/ListViewLayoutManager/ImageGridViewColumn.cs b/FSI.Lib/NLogViewer/Helper/ListViewLayoutManager/ImageGridViewColumn.cs new file mode 100644 index 0000000..cf81e4a --- /dev/null +++ b/FSI.Lib/NLogViewer/Helper/ListViewLayoutManager/ImageGridViewColumn.cs @@ -0,0 +1,58 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Media; + +namespace DJ.Helper.ListViewLayoutManager +{ + public abstract class ImageGridViewColumn : GridViewColumn, IValueConverter + { + // ############################################################################################################################## + // Constructor + // ############################################################################################################################## + + #region Constructor + + protected ImageGridViewColumn(Stretch imageStretch) + { + FrameworkElementFactory imageElement = new FrameworkElementFactory(typeof(Image)); + + Binding imageSourceBinding = new Binding {Converter = this, Mode = BindingMode.OneWay}; + imageElement.SetBinding(Image.SourceProperty, imageSourceBinding); + + Binding imageStretchBinding = new Binding {Source = imageStretch}; + imageElement.SetBinding(Image.StretchProperty, imageStretchBinding); + + DataTemplate template = new DataTemplate {VisualTree = imageElement}; + CellTemplate = template; + } + + protected ImageGridViewColumn() : this(Stretch.None) + { + } + + #endregion + + // ############################################################################################################################## + // IValueConverter + // ############################################################################################################################## + + #region IValueConverter + + object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return GetImageSource(value); + } + + object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + + protected abstract ImageSource GetImageSource(object value); + + #endregion + } +} \ No newline at end of file diff --git a/FSI.Lib/NLogViewer/Helper/ListViewLayoutManager/LayoutColumn.cs b/FSI.Lib/NLogViewer/Helper/ListViewLayoutManager/LayoutColumn.cs new file mode 100644 index 0000000..d94d28d --- /dev/null +++ b/FSI.Lib/NLogViewer/Helper/ListViewLayoutManager/LayoutColumn.cs @@ -0,0 +1,41 @@ +using System; +using System.Windows; +using System.Windows.Controls; + +namespace DJ.Helper.ListViewLayoutManager +{ + public abstract class LayoutColumn + { + protected static bool HasPropertyValue(GridViewColumn column, DependencyProperty dp) + { + if (column == null) + { + throw new ArgumentNullException(nameof(column)); + } + + object value = column.ReadLocalValue(dp); + if (value?.GetType() == dp.PropertyType) + { + return true; + } + + return false; + } + + protected static double? GetColumnWidth(GridViewColumn column, DependencyProperty dp) + { + if (column == null) + { + throw new ArgumentNullException(nameof(column)); + } + + object value = column.ReadLocalValue(dp); + if (value?.GetType() == dp.PropertyType) + { + return (double) value; + } + + return null; + } + } +} diff --git a/FSI.Lib/NLogViewer/Helper/ListViewLayoutManager/ListViewLayoutManager.cs b/FSI.Lib/NLogViewer/Helper/ListViewLayoutManager/ListViewLayoutManager.cs new file mode 100644 index 0000000..a850ed9 --- /dev/null +++ b/FSI.Lib/NLogViewer/Helper/ListViewLayoutManager/ListViewLayoutManager.cs @@ -0,0 +1,623 @@ +using System; +using System.ComponentModel; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Input; +using System.Windows.Media; + +namespace DJ.Helper.ListViewLayoutManager +{ + public class ListViewLayoutManager + { + // ############################################################################################################################## + // Properties + // ############################################################################################################################## + + #region Properties + + // ########################################################################################## + // Public Properties + // ########################################################################################## + + public ListView ListView => _ListView; + + public ScrollBarVisibility VerticalScrollBarVisibility + { + get => _VerticalScrollBarVisibility; + set => _VerticalScrollBarVisibility = value; + } + + // ########################################################################################## + // Private Properties + // ########################################################################################## + + private readonly ListView _ListView; + private ScrollViewer _ScrollViewer; + private bool _Loaded; + private bool _Resizing; + private Cursor _ResizeCursor; + private ScrollBarVisibility _VerticalScrollBarVisibility = ScrollBarVisibility.Auto; + private GridViewColumn _AutoSizedColumn; + + private const double _ZERO_WIDTH_RANGE = 0.1; + + #endregion + + // ############################################################################################################################## + // AttachedProperties + // ############################################################################################################################## + + #region AttachedProperties + + public static void SetEnabled(DependencyObject dependencyObject, bool enabled) + { + dependencyObject.SetValue(EnabledProperty, enabled); + } + + public static readonly DependencyProperty EnabledProperty = DependencyProperty.RegisterAttached("Enabled", + typeof(bool), typeof(ListViewLayoutManager), new FrameworkPropertyMetadata(_OnLayoutManagerEnabledChanged)); + + #endregion + + // ############################################################################################################################## + // Constructor + // ############################################################################################################################## + + #region Constructor + + public ListViewLayoutManager(ListView listView) + { + _ListView = listView ?? throw new ArgumentNullException(nameof(listView)); + _ListView.Loaded += ListView_Loaded; + _ListView.Unloaded += ListView_Unloaded; + } + + private void ListView_Loaded(object sender, RoutedEventArgs e) + { + _RegisterEvents(_ListView); + _InitColumns(); + _DoResizeColumns(); + _Loaded = true; + } + + + private void ListView_Unloaded(object sender, RoutedEventArgs e) + { + if (!_Loaded) + { + return; + } + + _UnRegisterEvents(_ListView); + _Loaded = false; + } + + #endregion + + // ############################################################################################################################## + // public methods + // ############################################################################################################################## + + #region public methods + + public void Refresh() + { + _InitColumns(); + _DoResizeColumns(); + } + + protected virtual void ResizeColumns() + { + GridView view = _ListView.View as GridView; + if (view == null || view.Columns.Count == 0) + { + return; + } + + // listview width + double actualWidth = double.PositiveInfinity; + if (_ScrollViewer != null) + { + actualWidth = _ScrollViewer.ViewportWidth; + } + + if (double.IsInfinity(actualWidth)) + { + actualWidth = _ListView.ActualWidth; + } + + if (double.IsInfinity(actualWidth) || actualWidth <= 0) + { + return; + } + + double resizeableRegionCount = 0; + double otherColumnsWidth = 0; + // determine column sizes + foreach (GridViewColumn gridViewColumn in view.Columns) + { + if (ProportionalColumn.IsProportionalColumn(gridViewColumn)) + { + double? proportionalWidth = ProportionalColumn.GetProportionalWidth(gridViewColumn); + if (proportionalWidth != null) + { + resizeableRegionCount += proportionalWidth.Value; + } + } + else + { + otherColumnsWidth += gridViewColumn.ActualWidth; + } + } + + if (resizeableRegionCount <= 0) + { + // no proportional columns present: commit the regulation to the scroll viewer + if (_ScrollViewer != null) + { + _ScrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto; + } + + // search the first fill column + GridViewColumn fillColumn = null; + for (int i = 0; i < view.Columns.Count; i++) + { + GridViewColumn gridViewColumn = view.Columns[i]; + if (_IsFillColumn(gridViewColumn)) + { + fillColumn = gridViewColumn; + break; + } + } + + if (fillColumn != null) + { + double otherColumnsWithoutFillWidth = otherColumnsWidth - fillColumn.ActualWidth; + double fillWidth = actualWidth - otherColumnsWithoutFillWidth; + if (fillWidth > 0) + { + double? minWidth = RangeColumn.GetRangeMinWidth(fillColumn); + double? maxWidth = RangeColumn.GetRangeMaxWidth(fillColumn); + + bool setWidth = !(minWidth.HasValue && fillWidth < minWidth.Value); + if (maxWidth.HasValue && fillWidth > maxWidth.Value) + { + setWidth = false; + } + + if (setWidth) + { + if (_ScrollViewer != null) + { + _ScrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden; + } + + fillColumn.Width = fillWidth; + } + } + } + + return; + } + + double resizeableColumnsWidth = actualWidth - otherColumnsWidth; + if (resizeableColumnsWidth <= 0) + { + return; // missing space + } + + // resize columns + double resizeableRegionWidth = resizeableColumnsWidth / resizeableRegionCount; + foreach (GridViewColumn gridViewColumn in view.Columns) + { + if (ProportionalColumn.IsProportionalColumn(gridViewColumn)) + { + double? proportionalWidth = ProportionalColumn.GetProportionalWidth(gridViewColumn); + if (proportionalWidth != null) + { + gridViewColumn.Width = proportionalWidth.Value * resizeableRegionWidth; + } + } + } + } + + #endregion + + // ############################################################################################################################## + // private methods + // ############################################################################################################################## + + #region private methods + + private void _DoResizeColumns() + { + if (_Resizing) + { + return; + } + + _Resizing = true; + try + { + ResizeColumns(); + } + finally + { + _Resizing = false; + } + } + + private void _RegisterEvents(DependencyObject start) + { + for (int i = 0; i < VisualTreeHelper.GetChildrenCount(start); i++) + { + Visual childVisual = VisualTreeHelper.GetChild(start, i) as Visual; + if (childVisual is Thumb) + { + GridViewColumn gridViewColumn = _FindParentColumn(childVisual); + if (gridViewColumn != null) + { + Thumb thumb = childVisual as Thumb; + if (ProportionalColumn.IsProportionalColumn(gridViewColumn) || + FixedColumn.IsFixedColumn(gridViewColumn) || _IsFillColumn(gridViewColumn)) + { + thumb.IsHitTestVisible = false; + } + else + { + thumb.PreviewMouseMove += _ThumbPreviewMouseMove; + thumb.PreviewMouseLeftButtonDown += + _ThumbPreviewMouseLeftButtonDown; + DependencyPropertyDescriptor.FromProperty( + GridViewColumn.WidthProperty, + typeof(GridViewColumn)).AddValueChanged(gridViewColumn, _GridColumnWidthChanged); + } + } + } + else if (childVisual is GridViewColumnHeader) + { + GridViewColumnHeader columnHeader = childVisual as GridViewColumnHeader; + columnHeader.SizeChanged += _GridColumnHeaderSizeChanged; + } + else if (_ScrollViewer == null && childVisual is ScrollViewer) + { + _ScrollViewer = childVisual as ScrollViewer; + _ScrollViewer.ScrollChanged += _ScrollViewerScrollChanged; + // assume we do the regulation of the horizontal scrollbar + _ScrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden; + _ScrollViewer.VerticalScrollBarVisibility = _VerticalScrollBarVisibility; + } + + _RegisterEvents(childVisual); + } + } + + private void _UnRegisterEvents(DependencyObject start) + { + for (int i = 0; i < VisualTreeHelper.GetChildrenCount(start); i++) + { + Visual childVisual = VisualTreeHelper.GetChild(start, i) as Visual; + if (childVisual is Thumb) + { + GridViewColumn gridViewColumn = _FindParentColumn(childVisual); + if (gridViewColumn != null) + { + Thumb thumb = childVisual as Thumb; + if (ProportionalColumn.IsProportionalColumn(gridViewColumn) || + FixedColumn.IsFixedColumn(gridViewColumn) || _IsFillColumn(gridViewColumn)) + { + thumb.IsHitTestVisible = true; + } + else + { + thumb.PreviewMouseMove -= _ThumbPreviewMouseMove; + thumb.PreviewMouseLeftButtonDown -= + _ThumbPreviewMouseLeftButtonDown; + DependencyPropertyDescriptor.FromProperty( + GridViewColumn.WidthProperty, + typeof(GridViewColumn)).RemoveValueChanged(gridViewColumn, _GridColumnWidthChanged); + } + } + } + else if (childVisual is GridViewColumnHeader) + { + GridViewColumnHeader columnHeader = childVisual as GridViewColumnHeader; + columnHeader.SizeChanged -= _GridColumnHeaderSizeChanged; + } + else if (_ScrollViewer == null && childVisual is ScrollViewer) + { + _ScrollViewer = childVisual as ScrollViewer; + _ScrollViewer.ScrollChanged -= _ScrollViewerScrollChanged; + } + + _UnRegisterEvents(childVisual); + } + } + + private GridViewColumn _FindParentColumn(DependencyObject element) + { + if (element == null) + { + return null; + } + + while (element != null) + { + GridViewColumnHeader gridViewColumnHeader = element as GridViewColumnHeader; + if (gridViewColumnHeader != null) + { + return (gridViewColumnHeader).Column; + } + + element = VisualTreeHelper.GetParent(element); + } + + return null; + } + + private GridViewColumnHeader _FindColumnHeader(DependencyObject start, GridViewColumn gridViewColumn) + { + for (int i = 0; i < VisualTreeHelper.GetChildrenCount(start); i++) + { + Visual childVisual = VisualTreeHelper.GetChild(start, i) as Visual; + if (childVisual is GridViewColumnHeader) + { + GridViewColumnHeader gridViewHeader = childVisual as GridViewColumnHeader; + if (gridViewHeader.Column == gridViewColumn) + { + return gridViewHeader; + } + } + + GridViewColumnHeader childGridViewHeader = _FindColumnHeader(childVisual, gridViewColumn); + if (childGridViewHeader != null) + { + return childGridViewHeader; + } + } + + return null; + } + + private void _InitColumns() + { + GridView view = _ListView.View as GridView; + if (view == null) + { + return; + } + + foreach (GridViewColumn gridViewColumn in view.Columns) + { + if (!RangeColumn.IsRangeColumn(gridViewColumn)) + { + continue; + } + + double? minWidth = RangeColumn.GetRangeMinWidth(gridViewColumn); + double? maxWidth = RangeColumn.GetRangeMaxWidth(gridViewColumn); + if (!minWidth.HasValue && !maxWidth.HasValue) + { + continue; + } + + GridViewColumnHeader columnHeader = _FindColumnHeader(_ListView, gridViewColumn); + if (columnHeader == null) + { + continue; + } + + double actualWidth = columnHeader.ActualWidth; + if (minWidth.HasValue) + { + columnHeader.MinWidth = minWidth.Value; + if (!double.IsInfinity(actualWidth) && actualWidth < columnHeader.MinWidth) + { + gridViewColumn.Width = columnHeader.MinWidth; + } + } + + if (maxWidth.HasValue) + { + columnHeader.MaxWidth = maxWidth.Value; + if (!double.IsInfinity(actualWidth) && actualWidth > columnHeader.MaxWidth) + { + gridViewColumn.Width = columnHeader.MaxWidth; + } + } + } + } + + // returns the delta + private double _SetRangeColumnToBounds(GridViewColumn gridViewColumn) + { + double startWidth = gridViewColumn.Width; + + double? minWidth = RangeColumn.GetRangeMinWidth(gridViewColumn); + double? maxWidth = RangeColumn.GetRangeMaxWidth(gridViewColumn); + + if ((minWidth.HasValue && maxWidth.HasValue) && (minWidth > maxWidth)) + { + return 0; // invalid case + } + + if (minWidth.HasValue && gridViewColumn.Width < minWidth.Value) + { + gridViewColumn.Width = minWidth.Value; + } + else if (maxWidth.HasValue && gridViewColumn.Width > maxWidth.Value) + { + gridViewColumn.Width = maxWidth.Value; + } + + return gridViewColumn.Width - startWidth; + } + + private bool _IsFillColumn(GridViewColumn gridViewColumn) + { + if (gridViewColumn == null) + { + return false; + } + + GridView view = _ListView.View as GridView; + if (view == null || view.Columns.Count == 0) + { + return false; + } + + bool? isFillColumn = RangeColumn.GetRangeIsFillColumn(gridViewColumn); + return isFillColumn.HasValue && isFillColumn.Value; + } + + private void _ThumbPreviewMouseMove(object sender, MouseEventArgs e) + { + Thumb thumb = sender as Thumb; + if (thumb == null) + { + return; + } + + GridViewColumn gridViewColumn = _FindParentColumn(thumb); + if (gridViewColumn == null) + { + return; + } + + // suppress column resizing for proportional, fixed and range fill columns + if (ProportionalColumn.IsProportionalColumn(gridViewColumn) || + FixedColumn.IsFixedColumn(gridViewColumn) || + _IsFillColumn(gridViewColumn)) + { + thumb.Cursor = null; + return; + } + + // check range column bounds + if (thumb.IsMouseCaptured && RangeColumn.IsRangeColumn(gridViewColumn)) + { + double? minWidth = RangeColumn.GetRangeMinWidth(gridViewColumn); + double? maxWidth = RangeColumn.GetRangeMaxWidth(gridViewColumn); + + if ((minWidth.HasValue && maxWidth.HasValue) && (minWidth > maxWidth)) + { + return; // invalid case + } + + if (_ResizeCursor == null) + { + _ResizeCursor = thumb.Cursor; // save the resize cursor + } + + if (minWidth.HasValue && gridViewColumn.Width <= minWidth.Value) + { + thumb.Cursor = Cursors.No; + } + else if (maxWidth.HasValue && gridViewColumn.Width >= maxWidth.Value) + { + thumb.Cursor = Cursors.No; + } + else + { + thumb.Cursor = _ResizeCursor; // between valid min/max + } + } + } + + private void _ThumbPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) + { + Thumb thumb = sender as Thumb; + GridViewColumn gridViewColumn = _FindParentColumn(thumb); + + // suppress column resizing for proportional, fixed and range fill columns + if (ProportionalColumn.IsProportionalColumn(gridViewColumn) || + FixedColumn.IsFixedColumn(gridViewColumn) || + _IsFillColumn(gridViewColumn)) + { + e.Handled = true; + } + } + + private void _GridColumnWidthChanged(object sender, EventArgs e) + { + if (!_Loaded) + { + return; + } + + GridViewColumn gridViewColumn = sender as GridViewColumn; + + // suppress column resizing for proportional and fixed columns + if (ProportionalColumn.IsProportionalColumn(gridViewColumn) || FixedColumn.IsFixedColumn(gridViewColumn)) + { + return; + } + + // ensure range column within the bounds + if (RangeColumn.IsRangeColumn(gridViewColumn)) + { + // special case: auto column width - maybe conflicts with min/max range + if (gridViewColumn != null && gridViewColumn.Width.Equals(double.NaN)) + { + _AutoSizedColumn = gridViewColumn; + return; // handled by the change header size event + } + + // ensure column bounds + if (Math.Abs(_SetRangeColumnToBounds(gridViewColumn) - 0) > _ZERO_WIDTH_RANGE) + { + return; + } + } + + _DoResizeColumns(); + } + + // handle autosized column + private void _GridColumnHeaderSizeChanged(object sender, SizeChangedEventArgs e) + { + if (_AutoSizedColumn == null) + { + return; + } + + GridViewColumnHeader gridViewColumnHeader = sender as GridViewColumnHeader; + if (gridViewColumnHeader != null && gridViewColumnHeader.Column == _AutoSizedColumn) + { + if (gridViewColumnHeader.Width.Equals(double.NaN)) + { + // sync column with + gridViewColumnHeader.Column.Width = gridViewColumnHeader.ActualWidth; + _DoResizeColumns(); + } + + _AutoSizedColumn = null; + } + } + + private void _ScrollViewerScrollChanged(object sender, ScrollChangedEventArgs e) + { + if (_Loaded && Math.Abs(e.ViewportWidthChange - 0) > _ZERO_WIDTH_RANGE) + { + _DoResizeColumns(); + } + } + + private static void _OnLayoutManagerEnabledChanged(DependencyObject dependencyObject, + DependencyPropertyChangedEventArgs e) + { + ListView listView = dependencyObject as ListView; + if (listView != null) + { + bool enabled = (bool) e.NewValue; + if (enabled) + { + new ListViewLayoutManager(listView); + } + } + } + + #endregion + } +} \ No newline at end of file diff --git a/FSI.Lib/NLogViewer/Helper/ListViewLayoutManager/ProportionalColumn.cs b/FSI.Lib/NLogViewer/Helper/ListViewLayoutManager/ProportionalColumn.cs new file mode 100644 index 0000000..f43fea7 --- /dev/null +++ b/FSI.Lib/NLogViewer/Helper/ListViewLayoutManager/ProportionalColumn.cs @@ -0,0 +1,61 @@ +using System.Windows; +using System.Windows.Controls; + +namespace DJ.Helper.ListViewLayoutManager +{ + public sealed class ProportionalColumn : LayoutColumn + { + public static bool IsProportionalColumn(GridViewColumn column) + { + if (column == null) + { + return false; + } + + return HasPropertyValue(column, WidthProperty); + } + + public static double? GetProportionalWidth(GridViewColumn column) + { + return GetColumnWidth(column, WidthProperty); + } + + public static GridViewColumn ApplyWidth(GridViewColumn gridViewColumn, double width) + { + SetWidth(gridViewColumn, width); + return gridViewColumn; + } + + // ############################################################################################################################## + // AttachedProperties + // ############################################################################################################################## + + #region AttachedProperties + + public static double GetWidth(DependencyObject obj) + { + return (double) obj.GetValue(WidthProperty); + } + + public static void SetWidth(DependencyObject obj, double width) + { + obj.SetValue(WidthProperty, width); + } + + public static readonly DependencyProperty WidthProperty = DependencyProperty.RegisterAttached("Width", typeof(double), typeof(ProportionalColumn)); + + #endregion + + // ############################################################################################################################## + // Constructor + // ############################################################################################################################## + + #region Constructor + + private ProportionalColumn() + { + } + + #endregion + } +} \ No newline at end of file diff --git a/FSI.Lib/NLogViewer/Helper/ListViewLayoutManager/RangeColumn.cs b/FSI.Lib/NLogViewer/Helper/ListViewLayoutManager/RangeColumn.cs new file mode 100644 index 0000000..66df102 --- /dev/null +++ b/FSI.Lib/NLogViewer/Helper/ListViewLayoutManager/RangeColumn.cs @@ -0,0 +1,136 @@ +using System; +using System.Windows; +using System.Windows.Controls; + +namespace DJ.Helper.ListViewLayoutManager +{ + + + public sealed class RangeColumn : LayoutColumn + { + + + public static readonly DependencyProperty MinWidthProperty = + DependencyProperty.RegisterAttached( + "MinWidth", + typeof( double ), + typeof( RangeColumn ) ); + + + public static readonly DependencyProperty MaxWidthProperty = + DependencyProperty.RegisterAttached( + "MaxWidth", + typeof( double ), + typeof( RangeColumn ) ); + + + public static readonly DependencyProperty IsFillColumnProperty = + DependencyProperty.RegisterAttached( + "IsFillColumn", + typeof( bool ), + typeof( RangeColumn ) ); + + + private RangeColumn() + { + } // RangeColumn + + + public static double GetMinWidth( DependencyObject obj ) + { + return (double)obj.GetValue( MinWidthProperty ); + } // GetMinWidth + + + public static void SetMinWidth( DependencyObject obj, double minWidth ) + { + obj.SetValue( MinWidthProperty, minWidth ); + } // SetMinWidth + + + public static double GetMaxWidth( DependencyObject obj ) + { + return (double)obj.GetValue( MaxWidthProperty ); + } // GetMaxWidth + + + public static void SetMaxWidth( DependencyObject obj, double maxWidth ) + { + obj.SetValue( MaxWidthProperty, maxWidth ); + } // SetMaxWidth + + + public static bool GetIsFillColumn( DependencyObject obj ) + { + return (bool)obj.GetValue( IsFillColumnProperty ); + } // GetIsFillColumn + + + public static void SetIsFillColumn( DependencyObject obj, bool isFillColumn ) + { + obj.SetValue( IsFillColumnProperty, isFillColumn ); + } // SetIsFillColumn + + + public static bool IsRangeColumn( GridViewColumn column ) + { + if ( column == null ) + { + return false; + } + return + HasPropertyValue( column, MinWidthProperty ) || + HasPropertyValue( column, MaxWidthProperty ) || + HasPropertyValue( column, IsFillColumnProperty ); + } // IsRangeColumn + + + public static double? GetRangeMinWidth( GridViewColumn column ) + { + return GetColumnWidth( column, MinWidthProperty ); + } // GetRangeMinWidth + + + public static double? GetRangeMaxWidth( GridViewColumn column ) + { + return GetColumnWidth( column, MaxWidthProperty ); + } // GetRangeMaxWidth + + + public static bool? GetRangeIsFillColumn( GridViewColumn column ) + { + if ( column == null ) + { + throw new ArgumentNullException( nameof(column) ); + } + object value = column.ReadLocalValue( IsFillColumnProperty ); + if ( value != null && value.GetType() == IsFillColumnProperty.PropertyType ) + { + return (bool)value; + } + + return null; + } // GetRangeIsFillColumn + + + public static GridViewColumn ApplyWidth( GridViewColumn gridViewColumn, double minWidth, + double width, double maxWidth ) + { + return ApplyWidth( gridViewColumn, minWidth, width, maxWidth, false ); + } // ApplyWidth + + + public static GridViewColumn ApplyWidth( GridViewColumn gridViewColumn, double minWidth, + double width, double maxWidth, bool isFillColumn ) + { + SetMinWidth( gridViewColumn, minWidth ); + gridViewColumn.Width = width; + SetMaxWidth( gridViewColumn, maxWidth ); + SetIsFillColumn( gridViewColumn, isFillColumn ); + return gridViewColumn; + } // ApplyWidth + + } // class RangeColumn + +} // namespace Itenso.Windows.Controls.ListViewLayout +// -- EOF ------------------------------------------------------------------- diff --git a/FSI.Lib/NLogViewer/Helper/PredicateBuilder.cs b/FSI.Lib/NLogViewer/Helper/PredicateBuilder.cs new file mode 100644 index 0000000..b69f93d --- /dev/null +++ b/FSI.Lib/NLogViewer/Helper/PredicateBuilder.cs @@ -0,0 +1,28 @@ +using System; +using System.Linq; +using System.Linq.Expressions; + +namespace DJ.Helper +{ + public static class PredicateBuilder + { + public static Expression> True () { return f => true; } + public static Expression> False () { return f => false; } + + public static Expression> Or (this Expression> expr1, + Expression> expr2) + { + var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast ()); + return Expression.Lambda> + (Expression.OrElse (expr1.Body, invokedExpr), expr1.Parameters); + } + + public static Expression> And (this Expression> expr1, + Expression> expr2) + { + var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast ()); + return Expression.Lambda> + (Expression.AndAlso (expr1.Body, invokedExpr), expr1.Parameters); + } + } +} diff --git a/FSI.Lib/NLogViewer/Images/Glyphs/SortDownArrow.png b/FSI.Lib/NLogViewer/Images/Glyphs/SortDownArrow.png new file mode 100644 index 0000000..7e5a1a5 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Glyphs/SortDownArrow.png differ diff --git a/FSI.Lib/NLogViewer/Images/Glyphs/SortUpArrow.png b/FSI.Lib/NLogViewer/Images/Glyphs/SortUpArrow.png new file mode 100644 index 0000000..3c0cae4 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Glyphs/SortUpArrow.png differ diff --git a/FSI.Lib/NLogViewer/Images/Large/Add.png b/FSI.Lib/NLogViewer/Images/Large/Add.png new file mode 100644 index 0000000..98598c8 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Large/Add.png differ diff --git a/FSI.Lib/NLogViewer/Images/Large/Context.png b/FSI.Lib/NLogViewer/Images/Large/Context.png new file mode 100644 index 0000000..0c68107 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Large/Context.png differ diff --git a/FSI.Lib/NLogViewer/Images/Large/Debug.png b/FSI.Lib/NLogViewer/Images/Large/Debug.png new file mode 100644 index 0000000..8b898fb Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Large/Debug.png differ diff --git a/FSI.Lib/NLogViewer/Images/Large/DebugSource.png b/FSI.Lib/NLogViewer/Images/Large/DebugSource.png new file mode 100644 index 0000000..dba0007 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Large/DebugSource.png differ diff --git a/FSI.Lib/NLogViewer/Images/Large/Error.png b/FSI.Lib/NLogViewer/Images/Large/Error.png new file mode 100644 index 0000000..30da157 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Large/Error.png differ diff --git a/FSI.Lib/NLogViewer/Images/Large/Exception.png b/FSI.Lib/NLogViewer/Images/Large/Exception.png new file mode 100644 index 0000000..9abd814 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Large/Exception.png differ diff --git a/FSI.Lib/NLogViewer/Images/Large/Exit.png b/FSI.Lib/NLogViewer/Images/Large/Exit.png new file mode 100644 index 0000000..d8c9a13 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Large/Exit.png differ diff --git a/FSI.Lib/NLogViewer/Images/Large/Export.png b/FSI.Lib/NLogViewer/Images/Large/Export.png new file mode 100644 index 0000000..dcd779d Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Large/Export.png differ diff --git a/FSI.Lib/NLogViewer/Images/Large/Fatal.png b/FSI.Lib/NLogViewer/Images/Large/Fatal.png new file mode 100644 index 0000000..6ffb187 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Large/Fatal.png differ diff --git a/FSI.Lib/NLogViewer/Images/Large/Info.png b/FSI.Lib/NLogViewer/Images/Large/Info.png new file mode 100644 index 0000000..a1ac7d6 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Large/Info.png differ diff --git a/FSI.Lib/NLogViewer/Images/Large/Network.png b/FSI.Lib/NLogViewer/Images/Large/Network.png new file mode 100644 index 0000000..717fa09 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Large/Network.png differ diff --git a/FSI.Lib/NLogViewer/Images/Large/Open.png b/FSI.Lib/NLogViewer/Images/Large/Open.png new file mode 100644 index 0000000..bc7199f Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Large/Open.png differ diff --git a/FSI.Lib/NLogViewer/Images/Large/Save.png b/FSI.Lib/NLogViewer/Images/Large/Save.png new file mode 100644 index 0000000..b260fb9 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Large/Save.png differ diff --git a/FSI.Lib/NLogViewer/Images/Large/Settings.png b/FSI.Lib/NLogViewer/Images/Large/Settings.png new file mode 100644 index 0000000..080ea12 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Large/Settings.png differ diff --git a/FSI.Lib/NLogViewer/Images/Large/Thread.png b/FSI.Lib/NLogViewer/Images/Large/Thread.png new file mode 100644 index 0000000..f799e88 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Large/Thread.png differ diff --git a/FSI.Lib/NLogViewer/Images/Large/Trace.png b/FSI.Lib/NLogViewer/Images/Large/Trace.png new file mode 100644 index 0000000..09d9b3c Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Large/Trace.png differ diff --git a/FSI.Lib/NLogViewer/Images/Large/Unknown.png b/FSI.Lib/NLogViewer/Images/Large/Unknown.png new file mode 100644 index 0000000..b1378bd Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Large/Unknown.png differ diff --git a/FSI.Lib/NLogViewer/Images/Large/Warning.png b/FSI.Lib/NLogViewer/Images/Large/Warning.png new file mode 100644 index 0000000..a37c5a1 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Large/Warning.png differ diff --git a/FSI.Lib/NLogViewer/Images/Medium/Add.png b/FSI.Lib/NLogViewer/Images/Medium/Add.png new file mode 100644 index 0000000..a8c521d Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Medium/Add.png differ diff --git a/FSI.Lib/NLogViewer/Images/Medium/Debug.png b/FSI.Lib/NLogViewer/Images/Medium/Debug.png new file mode 100644 index 0000000..96d8d79 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Medium/Debug.png differ diff --git a/FSI.Lib/NLogViewer/Images/Medium/Error.png b/FSI.Lib/NLogViewer/Images/Medium/Error.png new file mode 100644 index 0000000..f1dce1e Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Medium/Error.png differ diff --git a/FSI.Lib/NLogViewer/Images/Medium/Exception.png b/FSI.Lib/NLogViewer/Images/Medium/Exception.png new file mode 100644 index 0000000..341ed5d Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Medium/Exception.png differ diff --git a/FSI.Lib/NLogViewer/Images/Medium/Exit.png b/FSI.Lib/NLogViewer/Images/Medium/Exit.png new file mode 100644 index 0000000..d9ff54a Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Medium/Exit.png differ diff --git a/FSI.Lib/NLogViewer/Images/Medium/Export.png b/FSI.Lib/NLogViewer/Images/Medium/Export.png new file mode 100644 index 0000000..9b4fa6e Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Medium/Export.png differ diff --git a/FSI.Lib/NLogViewer/Images/Medium/Fatal.png b/FSI.Lib/NLogViewer/Images/Medium/Fatal.png new file mode 100644 index 0000000..75c32aa Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Medium/Fatal.png differ diff --git a/FSI.Lib/NLogViewer/Images/Medium/Info.png b/FSI.Lib/NLogViewer/Images/Medium/Info.png new file mode 100644 index 0000000..b5346c3 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Medium/Info.png differ diff --git a/FSI.Lib/NLogViewer/Images/Medium/Network.png b/FSI.Lib/NLogViewer/Images/Medium/Network.png new file mode 100644 index 0000000..8ce533b Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Medium/Network.png differ diff --git a/FSI.Lib/NLogViewer/Images/Medium/Open.png b/FSI.Lib/NLogViewer/Images/Medium/Open.png new file mode 100644 index 0000000..b7b59b8 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Medium/Open.png differ diff --git a/FSI.Lib/NLogViewer/Images/Medium/Save.png b/FSI.Lib/NLogViewer/Images/Medium/Save.png new file mode 100644 index 0000000..6179bfb Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Medium/Save.png differ diff --git a/FSI.Lib/NLogViewer/Images/Medium/Settings.png b/FSI.Lib/NLogViewer/Images/Medium/Settings.png new file mode 100644 index 0000000..6395b1c Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Medium/Settings.png differ diff --git a/FSI.Lib/NLogViewer/Images/Medium/Thread.png b/FSI.Lib/NLogViewer/Images/Medium/Thread.png new file mode 100644 index 0000000..a7c7020 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Medium/Thread.png differ diff --git a/FSI.Lib/NLogViewer/Images/Medium/Trace.png b/FSI.Lib/NLogViewer/Images/Medium/Trace.png new file mode 100644 index 0000000..12aae2c Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Medium/Trace.png differ diff --git a/FSI.Lib/NLogViewer/Images/Medium/Unknown.png b/FSI.Lib/NLogViewer/Images/Medium/Unknown.png new file mode 100644 index 0000000..4d78178 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Medium/Unknown.png differ diff --git a/FSI.Lib/NLogViewer/Images/Medium/Warning.png b/FSI.Lib/NLogViewer/Images/Medium/Warning.png new file mode 100644 index 0000000..135a710 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Medium/Warning.png differ diff --git a/FSI.Lib/NLogViewer/Images/Small/Add.png b/FSI.Lib/NLogViewer/Images/Small/Add.png new file mode 100644 index 0000000..a0f4cd9 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Small/Add.png differ diff --git a/FSI.Lib/NLogViewer/Images/Small/Clear.png b/FSI.Lib/NLogViewer/Images/Small/Clear.png new file mode 100644 index 0000000..375b8bf Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Small/Clear.png differ diff --git a/FSI.Lib/NLogViewer/Images/Small/Clock.png b/FSI.Lib/NLogViewer/Images/Small/Clock.png new file mode 100644 index 0000000..1cfffb2 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Small/Clock.png differ diff --git a/FSI.Lib/NLogViewer/Images/Small/Context.png b/FSI.Lib/NLogViewer/Images/Small/Context.png new file mode 100644 index 0000000..7d79c0a Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Small/Context.png differ diff --git a/FSI.Lib/NLogViewer/Images/Small/Debug.png b/FSI.Lib/NLogViewer/Images/Small/Debug.png new file mode 100644 index 0000000..2d5fb90 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Small/Debug.png differ diff --git a/FSI.Lib/NLogViewer/Images/Small/DebugSource.png b/FSI.Lib/NLogViewer/Images/Small/DebugSource.png new file mode 100644 index 0000000..2f4fe4f Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Small/DebugSource.png differ diff --git a/FSI.Lib/NLogViewer/Images/Small/Error.png b/FSI.Lib/NLogViewer/Images/Small/Error.png new file mode 100644 index 0000000..3e5ae9f Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Small/Error.png differ diff --git a/FSI.Lib/NLogViewer/Images/Small/Exception.png b/FSI.Lib/NLogViewer/Images/Small/Exception.png new file mode 100644 index 0000000..bc2ec84 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Small/Exception.png differ diff --git a/FSI.Lib/NLogViewer/Images/Small/Exit.png b/FSI.Lib/NLogViewer/Images/Small/Exit.png new file mode 100644 index 0000000..cf87d2c Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Small/Exit.png differ diff --git a/FSI.Lib/NLogViewer/Images/Small/Export.png b/FSI.Lib/NLogViewer/Images/Small/Export.png new file mode 100644 index 0000000..35f3c39 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Small/Export.png differ diff --git a/FSI.Lib/NLogViewer/Images/Small/Fatal.png b/FSI.Lib/NLogViewer/Images/Small/Fatal.png new file mode 100644 index 0000000..c26cd44 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Small/Fatal.png differ diff --git a/FSI.Lib/NLogViewer/Images/Small/Info.png b/FSI.Lib/NLogViewer/Images/Small/Info.png new file mode 100644 index 0000000..12cd1ae Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Small/Info.png differ diff --git a/FSI.Lib/NLogViewer/Images/Small/Layout.png b/FSI.Lib/NLogViewer/Images/Small/Layout.png new file mode 100644 index 0000000..374cd67 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Small/Layout.png differ diff --git a/FSI.Lib/NLogViewer/Images/Small/MonoLightning.png b/FSI.Lib/NLogViewer/Images/Small/MonoLightning.png new file mode 100644 index 0000000..7bf0b77 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Small/MonoLightning.png differ diff --git a/FSI.Lib/NLogViewer/Images/Small/Network.png b/FSI.Lib/NLogViewer/Images/Small/Network.png new file mode 100644 index 0000000..9802450 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Small/Network.png differ diff --git a/FSI.Lib/NLogViewer/Images/Small/Open.png b/FSI.Lib/NLogViewer/Images/Small/Open.png new file mode 100644 index 0000000..f49c4df Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Small/Open.png differ diff --git a/FSI.Lib/NLogViewer/Images/Small/Pause.png b/FSI.Lib/NLogViewer/Images/Small/Pause.png new file mode 100644 index 0000000..ebfefa8 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Small/Pause.png differ diff --git a/FSI.Lib/NLogViewer/Images/Small/Save.png b/FSI.Lib/NLogViewer/Images/Small/Save.png new file mode 100644 index 0000000..a6cf4c9 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Small/Save.png differ diff --git a/FSI.Lib/NLogViewer/Images/Small/ScrollDown.png b/FSI.Lib/NLogViewer/Images/Small/ScrollDown.png new file mode 100644 index 0000000..5891219 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Small/ScrollDown.png differ diff --git a/FSI.Lib/NLogViewer/Images/Small/Settings.png b/FSI.Lib/NLogViewer/Images/Small/Settings.png new file mode 100644 index 0000000..a4fd920 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Small/Settings.png differ diff --git a/FSI.Lib/NLogViewer/Images/Small/Thread.png b/FSI.Lib/NLogViewer/Images/Small/Thread.png new file mode 100644 index 0000000..22bcf3a Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Small/Thread.png differ diff --git a/FSI.Lib/NLogViewer/Images/Small/Trace.png b/FSI.Lib/NLogViewer/Images/Small/Trace.png new file mode 100644 index 0000000..330e7f8 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Small/Trace.png differ diff --git a/FSI.Lib/NLogViewer/Images/Small/Unknown.png b/FSI.Lib/NLogViewer/Images/Small/Unknown.png new file mode 100644 index 0000000..74f0f57 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Small/Unknown.png differ diff --git a/FSI.Lib/NLogViewer/Images/Small/Warning.png b/FSI.Lib/NLogViewer/Images/Small/Warning.png new file mode 100644 index 0000000..7616208 Binary files /dev/null and b/FSI.Lib/NLogViewer/Images/Small/Warning.png differ diff --git a/FSI.Lib/NLogViewer/NLogViewer.csproj b/FSI.Lib/NLogViewer/NLogViewer.csproj new file mode 100644 index 0000000..51cb887 --- /dev/null +++ b/FSI.Lib/NLogViewer/NLogViewer.csproj @@ -0,0 +1,154 @@ + + + + net6-windows + true + DJ + true + Sentinel.NLogViewer + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MSBuild:Compile + + + + diff --git a/FSI.Lib/NLogViewer/NLogViewer.xaml b/FSI.Lib/NLogViewer/NLogViewer.xaml new file mode 100644 index 0000000..664f7fc --- /dev/null +++ b/FSI.Lib/NLogViewer/NLogViewer.xaml @@ -0,0 +1,213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/FSI.Lib/NLogViewer/NLogViewer.xaml.cs b/FSI.Lib/NLogViewer/NLogViewer.xaml.cs new file mode 100644 index 0000000..0cc1cf6 --- /dev/null +++ b/FSI.Lib/NLogViewer/NLogViewer.xaml.cs @@ -0,0 +1,543 @@ +using System; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; +using System.Reactive.Concurrency; +using System.Reactive.Linq; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Threading; +using DJ.Extensions; +using DJ.Resolver; +using DJ.Targets; +using NLog; + +namespace DJ +{ + /// + /// Interaktionslogik für NLogViewer.xaml + /// + public partial class NLogViewer : UserControl + { + // ############################################################################################################################## + // Dependency Properties + // ############################################################################################################################## + + #region Dependency Properties + + // ########################################################################################## + // Colors + // ########################################################################################## + + #region Colors + + /// + /// The background for the trace output + /// + [Category("NLogViewerColors")] + public Brush TraceBackground + { + get => (Brush) GetValue(TraceBackgroundProperty); + set => SetValue(TraceBackgroundProperty, value); + } + + /// + /// The DependencyProperty. + /// + public static readonly DependencyProperty TraceBackgroundProperty = + DependencyProperty.Register("TraceBackground", typeof(Brush), typeof(NLogViewer), + new PropertyMetadata((Brush) (new BrushConverter().ConvertFrom("#D3D3D3")))); + + /// + /// The foreground for the trace output + /// + [Category("NLogViewerColors")] + public Brush TraceForeground + { + get => (Brush) GetValue(TraceForegroundProperty); + set => SetValue(TraceForegroundProperty, value); + } + + /// + /// The DependencyProperty. + /// + public static readonly DependencyProperty TraceForegroundProperty = + DependencyProperty.Register("TraceForeground", typeof(Brush), typeof(NLogViewer), + new PropertyMetadata((Brush) (new BrushConverter().ConvertFrom("#042271")))); + + /// + /// The background for the debug output + /// + [Category("NLogViewerColors")] + public Brush DebugBackground + { + get => (Brush) GetValue(DebugBackgroundProperty); + set => SetValue(DebugBackgroundProperty, value); + } + + /// + /// The DependencyProperty. + /// + public static readonly DependencyProperty DebugBackgroundProperty = + DependencyProperty.Register("DebugBackground", typeof(Brush), typeof(NLogViewer), + new PropertyMetadata((Brush) (new BrushConverter().ConvertFrom("#90EE90")))); + + /// + /// The foreground for the debug output + /// + [Category("NLogViewerColors")] + public Brush DebugForeground + { + get => (Brush) GetValue(DebugForegroundProperty); + set => SetValue(DebugForegroundProperty, value); + } + + /// + /// The DependencyProperty. + /// + public static readonly DependencyProperty DebugForegroundProperty = + DependencyProperty.Register("DebugForeground", typeof(Brush), typeof(NLogViewer), + new PropertyMetadata((Brush) (new BrushConverter().ConvertFrom("#042271")))); + + /// + /// The background for the info output + /// + [Category("NLogViewerColors")] + public Brush InfoBackground + { + get => (Brush) GetValue(InfoBackgroundProperty); + set => SetValue(InfoBackgroundProperty, value); + } + + /// + /// The DependencyProperty. + /// + public static readonly DependencyProperty InfoBackgroundProperty = DependencyProperty.Register("InfoBackground", + typeof(Brush), typeof(NLogViewer), + new PropertyMetadata((Brush) (new BrushConverter().ConvertFrom("#0000FF")))); + + /// + /// The foreground for the info output + /// + [Category("NLogViewerColors")] + public Brush InfoForeground + { + get => (Brush) GetValue(InfoForegroundProperty); + set => SetValue(InfoForegroundProperty, value); + } + + /// + /// The DependencyProperty. + /// + public static readonly DependencyProperty InfoForegroundProperty = DependencyProperty.Register("InfoForeground", + typeof(Brush), typeof(NLogViewer), new PropertyMetadata(Brushes.White)); + + /// + /// The background for the warn output + /// + [Category("NLogViewerColors")] + public Brush WarnBackground + { + get => (Brush) GetValue(WarnBackgroundProperty); + set => SetValue(WarnBackgroundProperty, value); + } + + /// + /// The DependencyProperty. + /// + public static readonly DependencyProperty WarnBackgroundProperty = DependencyProperty.Register("WarnBackground", + typeof(Brush), typeof(NLogViewer), + new PropertyMetadata((Brush) (new BrushConverter().ConvertFrom("#FFFF00")))); + + /// + /// The foreground for the warn output + /// + [Category("NLogViewerColors")] + public Brush WarnForeground + { + get => (Brush) GetValue(WarnForegroundProperty); + set => SetValue(WarnForegroundProperty, value); + } + + /// + /// The DependencyProperty. + /// + public static readonly DependencyProperty WarnForegroundProperty = DependencyProperty.Register("WarnForeground", + typeof(Brush), typeof(NLogViewer), + new PropertyMetadata((Brush) (new BrushConverter().ConvertFrom("#324B5C")))); + + /// + /// The background for the error output + /// + [Category("NLogViewerColors")] + public Brush ErrorBackground + { + get => (Brush) GetValue(ErrorBackgroundProperty); + set => SetValue(ErrorBackgroundProperty, value); + } + + /// + /// The DependencyProperty. + /// + public static readonly DependencyProperty ErrorBackgroundProperty = + DependencyProperty.Register("ErrorBackground", typeof(Brush), typeof(NLogViewer), + new PropertyMetadata(Brushes.Red)); + + /// + /// The foreground for the error output + /// + [Category("NLogViewerColors")] + public Brush ErrorForeground + { + get => (Brush) GetValue(ErrorForegroundProperty); + set => SetValue(ErrorForegroundProperty, value); + } + + /// + /// The DependencyProperty. + /// + public static readonly DependencyProperty ErrorForegroundProperty = + DependencyProperty.Register("ErrorForeground", typeof(Brush), typeof(NLogViewer), + new PropertyMetadata(Brushes.White)); + + /// + /// The background for the fatal output + /// + [Category("NLogViewerColors")] + public Brush FatalBackground + { + get => (Brush) GetValue(FatalBackgroundProperty); + set => SetValue(FatalBackgroundProperty, value); + } + + /// + /// The DependencyProperty. + /// + public static readonly DependencyProperty FatalBackgroundProperty = + DependencyProperty.Register("FatalBackground", typeof(Brush), typeof(NLogViewer), + new PropertyMetadata(Brushes.Black)); + + /// + /// The foreground for the fatal output + /// + [Category("NLogViewerColors")] + public Brush FatalForeground + { + get => (Brush) GetValue(FatalForegroundProperty); + set => SetValue(FatalForegroundProperty, value); + } + + /// + /// The DependencyProperty. + /// + public static readonly DependencyProperty FatalForegroundProperty = + DependencyProperty.Register("FatalForeground", typeof(Brush), typeof(NLogViewer), + new PropertyMetadata(Brushes.Yellow)); + + #endregion + + // ########################################################################################## + // NLogViewer + // ########################################################################################## + + #region NLogViewer + + /// + /// Is looking if any target with this name is configured and tries to link it + /// + [Category("NLogViewer")] + public string TargetName + { + get => (string)GetValue(TargetNameProperty); + set => SetValue(TargetNameProperty, value); + } + + /// + /// The DependencyProperty. + /// + public static readonly DependencyProperty TargetNameProperty = DependencyProperty.Register("TargetName", typeof(string), typeof(NLogViewer), new PropertyMetadata(null)); + + /// + /// Private DP to bind to the gui + /// + [Category("NLogViewer")] + public CollectionViewSource LogEvents + { + get => (CollectionViewSource) GetValue(LogEventsProperty); + private set => SetValue(LogEventsProperty, value); + } + + /// + /// The DependencyProperty. + /// + public static readonly DependencyProperty LogEventsProperty = DependencyProperty.Register("LogEvents", + typeof(CollectionViewSource), typeof(NLogViewer), new PropertyMetadata(null)); + + /// + /// Automatically scroll to the newest entry + /// + [Category("NLogViewer")] + public bool AutoScroll + { + get => (bool)GetValue(AutoScrollProperty); + set => SetValue(AutoScrollProperty, value); + } + + /// + /// The DependencyProperty. + /// + public static readonly DependencyProperty AutoScrollProperty = DependencyProperty.Register("AutoScroll", typeof(bool), typeof(NLogViewer), new PropertyMetadata(true, AutoScrollChangedCallback)); + + private static void AutoScrollChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs args) + { + if (d is NLogViewer instance) + { + instance.OnAutoScrollChanged(); + } + } + + protected virtual void OnAutoScrollChanged() + { + if (AutoScroll) + ListView?.ScrollToEnd(); + } + + /// + /// Delele all entries + /// + [Category("NLogViewer")] + public ICommand ClearCommand + { + get => (ICommand) GetValue(ClearCommandProperty); + set => SetValue(ClearCommandProperty, value); + } + + /// + /// The DependencyProperty. + /// + public static readonly DependencyProperty ClearCommandProperty = DependencyProperty.Register("ClearCommand", + typeof(ICommand), typeof(NLogViewer), new PropertyMetadata(null)); + + /// + /// Stop logging + /// + [Category("NLogViewer")] + public bool Pause + { + get => (bool)GetValue(PauseProperty); + set => SetValue(PauseProperty, value); + } + + /// + /// The DependencyProperty. + /// + public static readonly DependencyProperty PauseProperty = DependencyProperty.Register("Pause", typeof(bool), typeof(NLogViewer), new PropertyMetadata(false)); + + /// + /// The maximum number of entries before automatic cleaning is performed. There is a hysteresis of 100 entries which must be exceeded. + /// Example: is '1000'. Then after '1100' entries, everything until '1000' is deleted. + /// If set to '0' or less, it is deactivated + /// + [Category("NLogViewer")] + public int MaxCount + { + get => (int)GetValue(MaxCountProperty); + set => SetValue(MaxCountProperty, value); + } + + /// + /// The DependencyProperty. + /// + public static readonly DependencyProperty MaxCountProperty = DependencyProperty.Register("MaxCount", typeof(int), typeof(NLogViewer), new PropertyMetadata(5000)); + + + #endregion + + // ########################################################################################## + // Resolver + // ########################################################################################## + + #region Resolver + + /// + /// The to format the id + /// + [Category("NLogViewerResolver")] + public ILogEventInfoResolver IdResolver + { + get => (ILogEventInfoResolver)GetValue(IdResolverProperty); + set => SetValue(IdResolverProperty, value); + } + + /// + /// The DependencyProperty. + /// + public static readonly DependencyProperty IdResolverProperty = DependencyProperty.Register("IdResolver", typeof(ILogEventInfoResolver), typeof(NLogViewer), new PropertyMetadata(new IdResolver())); + + /// + /// The to format the timestamp output + /// + [Category("NLogViewerResolver")] + public ILogEventInfoResolver TimeStampResolver + { + get => (ILogEventInfoResolver)GetValue(TimeStampResolverProperty); + set => SetValue(TimeStampResolverProperty, value); + } + + /// + /// The DependencyProperty. + /// + public static readonly DependencyProperty TimeStampResolverProperty = DependencyProperty.Register("TimeStampResolver", typeof(ILogEventInfoResolver), typeof(NLogViewer), new PropertyMetadata(new TimeStampResolver())); + + /// + /// The to format the loggername + /// + [Category("NLogViewerResolver")] + public ILogEventInfoResolver LoggerNameResolver + { + get => (ILogEventInfoResolver)GetValue(LoggerNameResolverProperty); + set => SetValue(LoggerNameResolverProperty, value); + } + + /// + /// The DependencyProperty. + /// + public static readonly DependencyProperty LoggerNameResolverProperty = DependencyProperty.Register("LoggerNameResolver", typeof(ILogEventInfoResolver), typeof(NLogViewer), new PropertyMetadata(new LoggerNameResolver())); + + /// + /// The to format the message + /// + [Category("NLogViewerResolver")] + public ILogEventInfoResolver MessageResolver + { + get => (ILogEventInfoResolver)GetValue(MessageResolverProperty); + set => SetValue(MessageResolverProperty, value); + } + + /// + /// The DependencyProperty. + /// + public static readonly DependencyProperty MessageResolverProperty = DependencyProperty.Register("MessageResolver", typeof(ILogEventInfoResolver), typeof(NLogViewer), new PropertyMetadata(new MessageResolver())); + + #endregion + + + #endregion + + // ############################################################################################################################## + // Properties + // ############################################################################################################################## + + #region Properties + + // ########################################################################################## + // Public Properties + // ########################################################################################## + + + + // ########################################################################################## + // Private Properties + // ########################################################################################## + + private ObservableCollection _LogEventInfos { get; } = new ObservableCollection(); + private IDisposable _Subscription; + private Window _ParentWindow; + + #endregion + + // ############################################################################################################################## + // Constructor + // ############################################################################################################################## + + #region Constructor + + public NLogViewer() + { + InitializeComponent(); + DataContext = this; + + // save instance UID + Uid = GetHashCode().ToString(); + + if (DesignerProperties.GetIsInDesignMode(this)) + return; + + LogEvents = new CollectionViewSource {Source = _LogEventInfos}; + + Loaded += _OnLoaded; + Unloaded += _OnUnloaded; + ClearCommand = new ActionCommand(_LogEventInfos.Clear); + } + + private void _OnUnloaded(object sender, RoutedEventArgs e) + { + // look in logical and visual tree if the control has been removed + // If there is no parent window found before, we have a special case (https://github.com/dojo90/NLogViewer/issues/30) and just dispose it anyway + if (_ParentWindow.FindChildByUid(Uid) == null) + { + _Dispose(); + } + } + + private void _ParentWindowOnClosed(object sender, EventArgs e) + { + _Dispose(); + } + + private void _Dispose() + { + _Subscription?.Dispose(); + } + + private void _OnLoaded(object sender, RoutedEventArgs e) + { + // removed loaded handler to prevent duplicate subscribing + Loaded -= _OnLoaded; + + // add hook to parent window to dispose subscription + // use case: + // NLogViewer is used in a new window inside of a TabControl. If you switch the TabItems, + // the unloaded event is called and would dispose the subscription, even if the control is still alive. + if (Window.GetWindow(this) is { } window) + { + _ParentWindow = window; + _ParentWindow.Closed += _ParentWindowOnClosed; + } + + ListView.ScrollToEnd(); + var target = CacheTarget.GetInstance(targetName: TargetName); + + _Subscription = target.Cache.SubscribeOn(Scheduler.Default).Buffer(TimeSpan.FromMilliseconds(100)).Where (x => x.Any()).ObserveOn(new DispatcherSynchronizationContext(_ParentWindow.Dispatcher)).Subscribe(infos => + { + if (Pause) return; + using (LogEvents.DeferRefresh()) + { + foreach (LogEventInfo info in infos) + { + _LogEventInfos.Add(info); + } + if (MaxCount >= 0 & _LogEventInfos.Count - 100 > MaxCount) + { + for (int i = 0; i < _LogEventInfos.Count - MaxCount; i++) + { + _LogEventInfos.RemoveAt(0); + } + } + } + + if (AutoScroll) + { + ListView?.ScrollToEnd(); + } + }); + } + + #endregion + } +} diff --git a/FSI.Lib/NLogViewer/Resolver/ILogEventInfoResolver.cs b/FSI.Lib/NLogViewer/Resolver/ILogEventInfoResolver.cs new file mode 100644 index 0000000..ea0e2f2 --- /dev/null +++ b/FSI.Lib/NLogViewer/Resolver/ILogEventInfoResolver.cs @@ -0,0 +1,12 @@ +using NLog; + +namespace DJ.Resolver +{ + /// + /// Default interface to resolve a for your personal needs + /// + public interface ILogEventInfoResolver + { + string Resolve(LogEventInfo logEventInfo); + } +} diff --git a/FSI.Lib/NLogViewer/Resolver/IdResolver.cs b/FSI.Lib/NLogViewer/Resolver/IdResolver.cs new file mode 100644 index 0000000..e427d29 --- /dev/null +++ b/FSI.Lib/NLogViewer/Resolver/IdResolver.cs @@ -0,0 +1,12 @@ +using NLog; + +namespace DJ.Resolver +{ + public class IdResolver : ILogEventInfoResolver + { + public string Resolve(LogEventInfo logEventInfo) + { + return logEventInfo.SequenceID.ToString(); + } + } +} \ No newline at end of file diff --git a/FSI.Lib/NLogViewer/Resolver/LoggerNameResolver.cs b/FSI.Lib/NLogViewer/Resolver/LoggerNameResolver.cs new file mode 100644 index 0000000..c8773a8 --- /dev/null +++ b/FSI.Lib/NLogViewer/Resolver/LoggerNameResolver.cs @@ -0,0 +1,12 @@ +using NLog; + +namespace DJ.Resolver +{ + public class LoggerNameResolver : ILogEventInfoResolver + { + public string Resolve(LogEventInfo logEventInfo) + { + return logEventInfo.LoggerName; + } + } +} \ No newline at end of file diff --git a/FSI.Lib/NLogViewer/Resolver/MessageResolver.cs b/FSI.Lib/NLogViewer/Resolver/MessageResolver.cs new file mode 100644 index 0000000..e6907e7 --- /dev/null +++ b/FSI.Lib/NLogViewer/Resolver/MessageResolver.cs @@ -0,0 +1,21 @@ +using System.Text; +using NLog; + +namespace DJ.Resolver +{ + public class MessageResolver : ILogEventInfoResolver + { + public string Resolve(LogEventInfo logEventInfo) + { + StringBuilder builder = new StringBuilder(); + builder.Append(logEventInfo.FormattedMessage); + + if (logEventInfo.Exception != null) + { + builder.AppendLine().Append(logEventInfo.Exception); + } + + return builder.ToString(); + } + } +} \ No newline at end of file diff --git a/FSI.Lib/NLogViewer/Resolver/TimeStampResolver.cs b/FSI.Lib/NLogViewer/Resolver/TimeStampResolver.cs new file mode 100644 index 0000000..45ac642 --- /dev/null +++ b/FSI.Lib/NLogViewer/Resolver/TimeStampResolver.cs @@ -0,0 +1,12 @@ +using NLog; + +namespace DJ.Resolver +{ + public class TimeStampResolver : ILogEventInfoResolver + { + public string Resolve(LogEventInfo logEventInfo) + { + return logEventInfo.TimeStamp.ToString("dd-MM-yyyy hh:mm:ss.fff"); + } + } +} \ No newline at end of file diff --git a/FSI.Lib/NLogViewer/ScrollingHelper.cs b/FSI.Lib/NLogViewer/ScrollingHelper.cs new file mode 100644 index 0000000..ee70112 --- /dev/null +++ b/FSI.Lib/NLogViewer/ScrollingHelper.cs @@ -0,0 +1,44 @@ +using System; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; + +namespace DJ +{ + public static class ScrollingHelper + { + public static void ScrollToEnd(this ListView listView) + { + var scrollViewer = GetDescendantByType(listView, typeof(ScrollViewer)) as ScrollViewer; + scrollViewer?.ScrollToEnd(); + } + + public static Visual GetDescendantByType(Visual element, Type type) + { + if (element != null) + { + if (element.GetType() != type) + { + Visual foundElement = null; + (element as FrameworkElement)?.ApplyTemplate(); + + for (var i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++) + { + var visual = VisualTreeHelper.GetChild(element, i) as Visual; + foundElement = GetDescendantByType(visual, type); + if (foundElement != null) + { + break; + } + } + + return foundElement; + } + + return element; + } + + return null; + } + } +} \ No newline at end of file diff --git a/FSI.Lib/NLogViewer/Targets/CacheTarget.cs b/FSI.Lib/NLogViewer/Targets/CacheTarget.cs new file mode 100644 index 0000000..103f1f5 --- /dev/null +++ b/FSI.Lib/NLogViewer/Targets/CacheTarget.cs @@ -0,0 +1,93 @@ +using System; +using System.Linq; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using DJ.Helper; +using NLog; +using NLog.Config; +using NLog.Targets; + +namespace DJ.Targets +{ + [Target(nameof(CacheTarget))] + public class CacheTarget : Target + { + /// + /// Look in the NLog.config if any target is already defined and returns it, otherwise a new one is registered + /// + /// The maximum entries which should be buffered. Is only used if no target is defined + /// The name of the target you want to link with + /// + public static CacheTarget GetInstance(int defaultMaxCount = 0, string targetName = null) + { + if(LogManager.Configuration == null) + LogManager.Configuration = new LoggingConfiguration(); + + var predicate = PredicateBuilder.True().And(t => t is CacheTarget); + if (!string.IsNullOrEmpty(targetName)) + { + predicate = predicate.And(t => t.Name.Equals(targetName, StringComparison.CurrentCultureIgnoreCase) ||t.Name.Equals($"{targetName}_wrapped", StringComparison.CurrentCultureIgnoreCase)); + } + + var target = (CacheTarget)LogManager.Configuration.AllTargets.FirstOrDefault(predicate.Compile()); + if (target == null) + { + target = new CacheTarget { MaxCount = defaultMaxCount, Name = targetName ?? nameof(CacheTarget)}; + LogManager.Configuration.AddTarget(target.Name, target); + LogManager.Configuration.LoggingRules.Insert(0, new LoggingRule("*", LogLevel.FromString("Trace"), target)); + LogManager.ReconfigExistingLoggers(); + } + return target; + } + + // ############################################################################################################################## + // Properties + // ############################################################################################################################## + + #region Properties + + // ########################################################################################## + // Public Properties + // ########################################################################################## + + /// + /// The maximum amount of entries held in buffer/cache + /// + public int MaxCount { get; set; } = 100; + + public IObservable Cache => _CacheSubject.AsObservable(); + private readonly ReplaySubject _CacheSubject; + + // ########################################################################################## + // Private Properties + // ########################################################################################## + + #endregion + + // ############################################################################################################################## + // Constructor + // ############################################################################################################################## + + #region Constructor + + public CacheTarget() + { + _CacheSubject = new ReplaySubject(MaxCount); + } + + #endregion + + // ############################################################################################################################## + // override + // ############################################################################################################################## + + #region override + + protected override void Write(LogEventInfo logEvent) + { + _CacheSubject.OnNext(logEvent); + } + + #endregion + } +} \ No newline at end of file diff --git a/FSI.Lib/NLogViewer/XamlMultiValueConverter/ILogEventResolverToStringConverter.cs b/FSI.Lib/NLogViewer/XamlMultiValueConverter/ILogEventResolverToStringConverter.cs new file mode 100644 index 0000000..e1bf42f --- /dev/null +++ b/FSI.Lib/NLogViewer/XamlMultiValueConverter/ILogEventResolverToStringConverter.cs @@ -0,0 +1,23 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using DJ.Resolver; +using NLog; + +namespace DJ.XamlMultiValueConverter +{ + public class ILogEventResolverToStringConverter : IMultiValueConverter + { + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + if (values[0] is LogEventInfo logEventInfo && values[1] is ILogEventInfoResolver resolver) + return resolver.Resolve(logEventInfo); + return "####"; + } + + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/FSI.Lib/NLogViewer/global.json b/FSI.Lib/NLogViewer/global.json new file mode 100644 index 0000000..f45c840 --- /dev/null +++ b/FSI.Lib/NLogViewer/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "6.0.101", + "rollForward": "disable" + } +} \ No newline at end of file