Sicherung

This commit is contained in:
Maier Stephan SI
2023-01-16 16:04:47 +01:00
parent 63512e77aa
commit 0b0508b042
98 changed files with 2454 additions and 188 deletions

View File

@@ -1,9 +1,9 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16 # Visual Studio Version 17
VisualStudioVersion = 16.0.31321.278 VisualStudioVersion = 17.0.32112.339
MinimumVisualStudioVersion = 10.0.40219.1 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 EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution

View File

@@ -34,6 +34,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Management.Infrastructure" Version="2.0.0" /> <PackageReference Include="Microsoft.Management.Infrastructure" Version="2.0.0" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.2.1" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0" /> <PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="7.0.0" /> <PackageReference Include="System.Text.Encoding.CodePages" Version="7.0.0" />
<PackageReference Include="TextCopy" Version="6.2.1" /> <PackageReference Include="TextCopy" Version="6.2.1" />

View File

@@ -1,6 +1,9 @@
using System; using Microsoft.Extensions.Logging;
using NLog;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
@@ -19,11 +22,13 @@ namespace FSI.Lib.Guis.AutoPw
/// </summary> /// </summary>
public partial class FrmMain : Window public partial class FrmMain : Window
{ {
private Logger _log;
public bool CloseAtLostFocus { get; set; } public bool CloseAtLostFocus { get; set; }
public bool PwOk { get; set; } public bool PwOk { get; set; }
public FrmMain() public FrmMain()
{ {
InitializeComponent(); InitializeComponent();
_log = LogManager.GetCurrentClassLogger();
BtnOk.IsEnabled = false; BtnOk.IsEnabled = false;
Deactivated += FrmMain_Deactivated; Deactivated += FrmMain_Deactivated;
@@ -38,6 +43,8 @@ namespace FSI.Lib.Guis.AutoPw
private void BtnOk_Click(object sender, RoutedEventArgs e) private void BtnOk_Click(object sender, RoutedEventArgs e)
{ {
_log.Info(GetType().Namespace);
Close(); Close();
} }
@@ -80,10 +87,5 @@ namespace FSI.Lib.Guis.AutoPw
Close(); Close();
} }
private void TbPw_KeyDown_1(object sender, KeyEventArgs e)
{
}
} }
} }

View File

@@ -148,8 +148,6 @@
<control:FilterDataGrid.InputBindings> <control:FilterDataGrid.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick" <MouseBinding MouseAction="LeftDoubleClick"
Command="{Binding CmdOpen}" /> Command="{Binding CmdOpen}" />
<MouseBinding MouseAction="RightDoubleClick"
Command="{Binding CmdCopyToClip}" />
</control:FilterDataGrid.InputBindings> </control:FilterDataGrid.InputBindings>
<control:FilterDataGrid.Columns> <control:FilterDataGrid.Columns>

View File

@@ -174,6 +174,9 @@ namespace FSI.Lib.Guis.Folder.Mgt
newDatas.Add(newData); newDatas.Add(newData);
} }
newDatas = new ObservableCollection<Model>(newDatas.OrderBy(i => i.DescriptionDtl));
return newDatas; return newDatas;
} }
} }

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -1,60 +1,31 @@
using FSI.Lib.Guis.IbaDirSync.Model; using FSI.Lib.MVVM;
using FSI.Lib.MVVM;
using RoboSharp; using RoboSharp;
using System.Text; using System.Text;
using System.Windows.Input; 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 _cmdStart;
private ICommand _cmdStop; private ICommand _cmdStop;
private Iba _iba; public ViewModel()
public ViewModelIba()
{
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<object>(ExecuteStart, CanExecuteStart); _cmdStart = new RelayCommand<object>(ExecuteStart, CanExecuteStart);
_cmdStop = new RelayCommand<object>(ExecuteStop, CanExecuteStop); _cmdStop = new RelayCommand<object>(ExecuteStop, CanExecuteStop);
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
RoboCopy = new RoboSharp.RoboCommand(); RoboCopy = new RoboSharp.RoboCommand();
}
if (_iba.AutoStart) public IInterface Data { get; set; }
public void Init()
{
if (Data.AutoStart)
{
ExecuteStart(null); ExecuteStart(null);
} }
public Model.Iba Iba
{
get
{
return _iba;
}
set
{
if (_iba != value & _iba != null)
{
_iba = value;
OnPropertyChanged();
}
}
} }
public RoboCommand RoboCopy { get; set; } public RoboCommand RoboCopy { get; set; }
@@ -93,8 +64,8 @@ namespace FSI.Lib.Guis.IbaDirSync.ViewModel
private void ExecuteStart(object obj) private void ExecuteStart(object obj)
{ {
RoboCopy.CopyOptions.Source = Iba.Source; RoboCopy.CopyOptions.Source = Data.Source;
RoboCopy.CopyOptions.Destination = Iba.Destination; RoboCopy.CopyOptions.Destination = Data.Destination;
RoboCopy.CopyOptions.CopySubdirectories = true; RoboCopy.CopyOptions.CopySubdirectories = true;
RoboCopy.CopyOptions.MonitorSourceChangesLimit = 1; RoboCopy.CopyOptions.MonitorSourceChangesLimit = 1;
RoboCopy.StopIfDisposing = true; RoboCopy.StopIfDisposing = true;

View File

@@ -4,13 +4,14 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; 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 Bezeichnung { get; set; }
public string Name { get; set; } public string Name { get; set; }
public string ClassName { get; set; } public string ClassName { get; set; }
public bool AutoStart { get; set; }
public int Height { get; set; } public int Height { get; set; }
public int Width { get; set; } public int Width { get; set; }
public int X { get; set; } public int X { get; set; }

View File

@@ -1,5 +1,4 @@
using FSI.Lib.Guis.SetSizePosExWindow.Model; using FSI.Lib.MVVM;
using FSI.Lib.MVVM;
using System; using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.ComponentModel; using System.ComponentModel;
@@ -8,9 +7,9 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Input; 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")] [DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
@@ -65,39 +64,15 @@ namespace FSI.Lib.Guis.SetSizePosExWindow.ViewModel
private BackgroundWorker backgroundWorker; private BackgroundWorker backgroundWorker;
private ICommand _cmdStart; private ICommand _cmdStart;
private ICommand _cmdStop; private ICommand _cmdStop;
private ObservableCollection<Model.Window> _windows; private ObservableCollection<IInterface> _windows;
private int _updateIntervall; private int _updateIntervall;
private bool _autoStart; private bool _autoStart;
private Window _selectedWindow; private IInterface _selectedWindow;
public ViewModelWindow() public ViewModel()
{ {
_windows = new ObservableCollection<Model.Window>(); _windows = new ObservableCollection<IInterface>();
Init();
if (AutoStart)
{
ExecuteStart(null);
}
}
public ViewModelWindow(bool autoStart)
{
_windows = new ObservableCollection<Model.Window>();
Init();
AutoStart = autoStart;
if (AutoStart)
{
ExecuteStart(null);
}
}
private void Init()
{
_cmdStart = new RelayCommand<object>(ExecuteStart, CanExecuteStart); _cmdStart = new RelayCommand<object>(ExecuteStart, CanExecuteStart);
_cmdStop = new RelayCommand<object>(ExecuteStop, CanExecuteStop); _cmdStop = new RelayCommand<object>(ExecuteStop, CanExecuteStop);
@@ -110,7 +85,17 @@ namespace FSI.Lib.Guis.SetSizePosExWindow.ViewModel
backgroundWorker.ProgressChanged += BackgroundWorker_ProgressChanged; backgroundWorker.ProgressChanged += BackgroundWorker_ProgressChanged;
} }
public ObservableCollection<Model.Window> Windows public IInterface Data { get; set; }
public void Init()
{
if (Data.AutoStart)
{
ExecuteStart(null);
}
}
public ObservableCollection<IInterface> Windows
{ {
get get
{ {
@@ -126,7 +111,7 @@ namespace FSI.Lib.Guis.SetSizePosExWindow.ViewModel
} }
} }
public Model.Window SelectedWindow public IInterface SelectedWindow
{ {
get get
{ {
@@ -232,7 +217,7 @@ namespace FSI.Lib.Guis.SetSizePosExWindow.ViewModel
} }
else else
{ {
foreach (Window window in Windows) foreach (IInterface window in Windows)
{ {
// Perform a time consuming operation and report progress. // Perform a time consuming operation and report progress.
Thread.Sleep(UpdateIntervall); Thread.Sleep(UpdateIntervall);

View File

@@ -28,7 +28,7 @@ namespace FSI.Lib.Guis.SieTiaWinCCMsgMgt
private void btnStart_Click(object sender, RoutedEventArgs e) private void btnStart_Click(object sender, RoutedEventArgs e)
{ {
//WinCC.Start(); // WinCC.Start();
CtrlMgt(); CtrlMgt();
} }

View File

@@ -1,12 +1,8 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FSI.Lib.Guis.SieTiaWinCCMsgMgt.Model namespace FSI.Lib.Guis.SieTiaWinCCMsgMgt
{ {
public class WinCC public interface IInterface
{ {
public bool AutoStart { get; set; } public bool AutoStart { get; set; }
public int UpdateIntervall { get; set; } public int UpdateIntervall { get; set; }
@@ -14,4 +10,5 @@ namespace FSI.Lib.Guis.SieTiaWinCCMsgMgt.Model
public string WindowsClassName { get; set; } public string WindowsClassName { get; set; }
public string ButtonName { get; set; } public string ButtonName { get; set; }
} }
} }

View File

@@ -1,5 +1,4 @@
using FSI.Lib.Guis.SieTiaWinCCMsgMgt.Model; using FSI.Lib.MVVM;
using FSI.Lib.MVVM;
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -7,9 +6,9 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Input; 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")] [DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
@@ -37,43 +36,14 @@ namespace FSI.Lib.Guis.SieTiaWinCCMsgMgt.ViewModel
const int WM_LBUTTONUP = 0x0202; // message for Mouse up const int WM_LBUTTONUP = 0x0202; // message for Mouse up
private CancellationTokenSource tokenSource = null; private CancellationTokenSource tokenSource = null;
private CancellationToken token;
private Task task = null; private Task task = null;
private BackgroundWorker backgroundWorker; private BackgroundWorker backgroundWorker;
private ICommand _cmdStart; private ICommand _cmdStart;
private ICommand _cmdStop; 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) public ViewModel()
{
_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()
{ {
_cmdStart = new RelayCommand<object>(ExecuteStart, CanExecuteStart); _cmdStart = new RelayCommand<object>(ExecuteStart, CanExecuteStart);
_cmdStop = new RelayCommand<object>(ExecuteStop, CanExecuteStop); _cmdStop = new RelayCommand<object>(ExecuteStop, CanExecuteStop);
@@ -84,22 +54,16 @@ namespace FSI.Lib.Guis.SieTiaWinCCMsgMgt.ViewModel
WorkerSupportsCancellation = true WorkerSupportsCancellation = true
}; };
backgroundWorker.DoWork += BackgroundWorker_DoWork; backgroundWorker.DoWork += BackgroundWorker_DoWork;
backgroundWorker.ProgressChanged += BackgroundWorker_ProgressChanged; backgroundWorker.ProgressChanged += BackgroundWorker_ProgressChanged; ;
} }
public Model.WinCC WinCC public IInterface Data { get; set; }
public void Init()
{ {
get if (Data.AutoStart)
{ {
return _winCC; ExecuteStart(null);
}
set
{
if (_winCC != value & _winCC != null)
{
_winCC = value;
OnPropertyChanged();
}
} }
} }
@@ -129,7 +93,6 @@ namespace FSI.Lib.Guis.SieTiaWinCCMsgMgt.ViewModel
set => _cmdStop = value; set => _cmdStop = value;
} }
private bool CanExecuteStart(object obj) private bool CanExecuteStart(object obj)
{ {
return !Status; return !Status;
@@ -163,44 +126,23 @@ namespace FSI.Lib.Guis.SieTiaWinCCMsgMgt.ViewModel
else else
{ {
// Perform a time consuming operation and report progress. // Perform a time consuming operation and report progress.
Thread.Sleep(WinCC.UpdateIntervall); Thread.Sleep(Data.UpdateIntervall);
// Find windos by Name // Find windos by Name
var windowHandle = FindWindow(WinCC.WindowsClassName, WinCC.WindowsName); var windowHandle = FindWindow(Data.WindowsClassName, Data.WindowsName);
if (windowHandle != IntPtr.Zero && IsWindowVisible(windowHandle)) if (windowHandle != IntPtr.Zero && IsWindowVisible(windowHandle))
{ {
SetForegroundWindow(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); SendMessage(btnHandle, BM_CLICK, IntPtr.Zero, IntPtr.Zero);
} }
} }
} }
} }
public bool Status { get; set; } 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) protected virtual void Dispose(bool disposing)
{ {
bool disposedValue = false; bool disposedValue = false;
@@ -229,6 +171,5 @@ namespace FSI.Lib.Guis.SieTiaWinCCMsgMgt.ViewModel
task = null; task = null;
} }
} }
} }
} }

View File

@@ -1,8 +1,4 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FSI.Lib.Guis.TxtToClip.Mgt namespace FSI.Lib.Guis.TxtToClip.Mgt
{ {

View File

@@ -143,6 +143,8 @@ namespace FSI.Lib.Guis.TxtToClip.Mgt
newDatas.Add(newData); newDatas.Add(newData);
} }
newDatas = new ObservableCollection<Model>(newDatas.OrderBy(i => i.DescriptionDtl));
return newDatas; return newDatas;
} }
} }

View File

@@ -0,0 +1,42 @@
using System;
using System.Windows.Input;
namespace DJ
{
public sealed class ActionCommand : ICommand
{
private readonly Action _Action;
private readonly Action<object> _ObjectAction;
public ActionCommand(Action action)
{
_Action = action;
}
public ActionCommand(Action<object> 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();
}
}
}

View File

@@ -0,0 +1,72 @@
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
namespace DJ.Extensions
{
public static class DependencyObjectExtensions
{
/// <summary>
/// Analyzes both visual and logical tree in order to find all elements of a given
/// type that are descendants of the <paramref name="source"/> item.
/// </summary>
/// <typeparam name="T">The type of the queried items.</typeparam>
/// <param name="source">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.</param>
/// <param name="uid">The UID of the <see cref="UIElement"/></param>
/// <returns>All descendants of <paramref name="source"/> that match the requested type.</returns>
public static T FindChildByUid<T>(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<T>(child, uid);
if (descendant != null)
return descendant;
}
}
return null;
}
/// <summary>
/// This method is an alternative to WPF's
/// <see cref="VisualTreeHelper.GetChild"/> method, which also
/// supports content elements. Keep in mind that for content elements,
/// this method falls back to the logical tree of the element.
/// </summary>
/// <param name="parent">The item to be processed.</param>
/// <returns>The submitted item's child elements, if available.</returns>
public static IEnumerable<DependencyObject> 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);
}
}
}
}
}

View File

@@ -0,0 +1,43 @@
using System;
using System.Reactive.Linq;
using System.Threading;
using System.Windows.Controls;
using NLog;
namespace DJ.Helper
{
/// <summary>
/// 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
/// </summary>
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);
}
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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;
}
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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 -------------------------------------------------------------------

View File

@@ -0,0 +1,28 @@
using System;
using System.Linq;
using System.Linq.Expressions;
namespace DJ.Helper
{
public static class PredicateBuilder
{
public static Expression<Func<T, bool>> True<T> () { return f => true; }
public static Expression<Func<T, bool>> False<T> () { return f => false; }
public static Expression<Func<T, bool>> Or<T> (this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
return Expression.Lambda<Func<T, bool>>
(Expression.OrElse (expr1.Body, invokedExpr), expr1.Parameters);
}
public static Expression<Func<T, bool>> And<T> (this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
return Expression.Lambda<Func<T, bool>>
(Expression.AndAlso (expr1.Body, invokedExpr), expr1.Parameters);
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 984 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 787 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 475 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 451 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 774 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 782 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 943 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 629 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 589 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 778 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 520 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 506 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 623 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 631 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 813 B

View File

@@ -0,0 +1,154 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6-windows</TargetFramework>
<UseWPF>true</UseWPF>
<RootNamespace>DJ</RootNamespace>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageId>Sentinel.NLogViewer</PackageId>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<ItemGroup>
<None Remove="Images\Glyphs\SortDownArrow.png" />
<None Remove="Images\Glyphs\SortUpArrow.png" />
<None Remove="Images\Large\Add.png" />
<None Remove="Images\Large\Context.png" />
<None Remove="Images\Large\Debug.png" />
<None Remove="Images\Large\DebugSource.png" />
<None Remove="Images\Large\Error.png" />
<None Remove="Images\Large\Exception.png" />
<None Remove="Images\Large\Exit.png" />
<None Remove="Images\Large\Export.png" />
<None Remove="Images\Large\Fatal.png" />
<None Remove="Images\Large\Info.png" />
<None Remove="Images\Large\Network.png" />
<None Remove="Images\Large\Open.png" />
<None Remove="Images\Large\Save.png" />
<None Remove="Images\Large\Settings.png" />
<None Remove="Images\Large\Thread.png" />
<None Remove="Images\Large\Trace.png" />
<None Remove="Images\Large\Unknown.png" />
<None Remove="Images\Large\Warning.png" />
<None Remove="Images\Medium\Add.png" />
<None Remove="Images\Medium\Debug.png" />
<None Remove="Images\Medium\Error.png" />
<None Remove="Images\Medium\Exception.png" />
<None Remove="Images\Medium\Exit.png" />
<None Remove="Images\Medium\Export.png" />
<None Remove="Images\Medium\Fatal.png" />
<None Remove="Images\Medium\Info.png" />
<None Remove="Images\Medium\Network.png" />
<None Remove="Images\Medium\Open.png" />
<None Remove="Images\Medium\Save.png" />
<None Remove="Images\Medium\Settings.png" />
<None Remove="Images\Medium\Thread.png" />
<None Remove="Images\Medium\Trace.png" />
<None Remove="Images\Medium\Unknown.png" />
<None Remove="Images\Medium\Warning.png" />
<None Remove="Images\Small\Add.png" />
<None Remove="Images\Small\Clear.png" />
<None Remove="Images\Small\Clock.png" />
<None Remove="Images\Small\Context.png" />
<None Remove="Images\Small\Debug.png" />
<None Remove="Images\Small\DebugSource.png" />
<None Remove="Images\Small\Error.png" />
<None Remove="Images\Small\Exception.png" />
<None Remove="Images\Small\Exit.png" />
<None Remove="Images\Small\Export.png" />
<None Remove="Images\Small\Fatal.png" />
<None Remove="Images\Small\Info.png" />
<None Remove="Images\Small\Layout.png" />
<None Remove="Images\Small\MonoLightning.png" />
<None Remove="Images\Small\Network.png" />
<None Remove="Images\Small\Open.png" />
<None Remove="Images\Small\Pause.png" />
<None Remove="Images\Small\Save.png" />
<None Remove="Images\Small\ScrollDown.png" />
<None Remove="Images\Small\Settings.png" />
<None Remove="Images\Small\Thread.png" />
<None Remove="Images\Small\Trace.png" />
<None Remove="Images\Small\Unknown.png" />
<None Remove="Images\Small\Warning.png" />
<None Remove="NLogViewer.xaml" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="GitVersion.MsBuild" Version="5.10.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="NLog" Version="5.0.1" />
<PackageReference Include="System.Reactive" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<Resource Include="Images\Glyphs\SortDownArrow.png" />
<Resource Include="Images\Glyphs\SortUpArrow.png" />
<Resource Include="Images\Large\Add.png" />
<Resource Include="Images\Large\Context.png" />
<Resource Include="Images\Large\Debug.png" />
<Resource Include="Images\Large\DebugSource.png" />
<Resource Include="Images\Large\Error.png" />
<Resource Include="Images\Large\Exception.png" />
<Resource Include="Images\Large\Exit.png" />
<Resource Include="Images\Large\Export.png" />
<Resource Include="Images\Large\Fatal.png" />
<Resource Include="Images\Large\Info.png" />
<Resource Include="Images\Large\Network.png" />
<Resource Include="Images\Large\Open.png" />
<Resource Include="Images\Large\Save.png" />
<Resource Include="Images\Large\Settings.png" />
<Resource Include="Images\Large\Thread.png" />
<Resource Include="Images\Large\Trace.png" />
<Resource Include="Images\Large\Unknown.png" />
<Resource Include="Images\Large\Warning.png" />
<Resource Include="Images\Medium\Add.png" />
<Resource Include="Images\Medium\Debug.png" />
<Resource Include="Images\Medium\Error.png" />
<Resource Include="Images\Medium\Exception.png" />
<Resource Include="Images\Medium\Exit.png" />
<Resource Include="Images\Medium\Export.png" />
<Resource Include="Images\Medium\Fatal.png" />
<Resource Include="Images\Medium\Info.png" />
<Resource Include="Images\Medium\Network.png" />
<Resource Include="Images\Medium\Open.png" />
<Resource Include="Images\Medium\Save.png" />
<Resource Include="Images\Medium\Settings.png" />
<Resource Include="Images\Medium\Thread.png" />
<Resource Include="Images\Medium\Trace.png" />
<Resource Include="Images\Medium\Unknown.png" />
<Resource Include="Images\Medium\Warning.png" />
<Resource Include="Images\Small\Add.png" />
<Resource Include="Images\Small\Clear.png" />
<Resource Include="Images\Small\Clock.png" />
<Resource Include="Images\Small\Context.png" />
<Resource Include="Images\Small\Debug.png" />
<Resource Include="Images\Small\DebugSource.png" />
<Resource Include="Images\Small\Error.png" />
<Resource Include="Images\Small\Exception.png" />
<Resource Include="Images\Small\Exit.png" />
<Resource Include="Images\Small\Export.png" />
<Resource Include="Images\Small\Fatal.png" />
<Resource Include="Images\Small\Info.png" />
<Resource Include="Images\Small\Layout.png" />
<Resource Include="Images\Small\MonoLightning.png" />
<Resource Include="Images\Small\Network.png" />
<Resource Include="Images\Small\Open.png" />
<Resource Include="Images\Small\Pause.png" />
<Resource Include="Images\Small\Save.png" />
<Resource Include="Images\Small\ScrollDown.png" />
<Resource Include="Images\Small\Settings.png" />
<Resource Include="Images\Small\Thread.png" />
<Resource Include="Images\Small\Trace.png" />
<Resource Include="Images\Small\Unknown.png" />
<Resource Include="Images\Small\Warning.png" />
</ItemGroup>
<ItemGroup>
<None Update="NLogView.xaml">
<Generator>MSBuild:Compile</Generator>
</None>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,213 @@
<UserControl x:Class="DJ.NLogViewer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:dj="clr-namespace:DJ"
xmlns:nLog="clr-namespace:NLog;assembly=NLog"
xmlns:listViewLayoutManager="clr-namespace:DJ.Helper.ListViewLayoutManager"
xmlns:xamlMultiValueConverter="clr-namespace:DJ.XamlMultiValueConverter"
xmlns:helper="clr-namespace:DJ.Helper"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel
Grid.Row="1"
Orientation="Horizontal"
Margin="0,5,0,5">
<ToggleButton
Width="120"
Height="20"
IsChecked="{Binding Path=AutoScroll, Mode=TwoWay}">
<ToggleButton.Content>
<StackPanel Orientation="Horizontal">
<Image Source="Images/Small/ScrollDown.png"
RenderOptions.BitmapScalingMode="HighQuality" Stretch="Uniform" />
<TextBlock Text="Auto-Scroll" VerticalAlignment="Center" Margin="5,0,0,0" />
</StackPanel>
</ToggleButton.Content>
</ToggleButton>
<Button
Margin="5,0,0,0"
Width="120"
Height="20"
Command="{Binding Path=ClearCommand}">
<Button.Content>
<StackPanel Orientation="Horizontal">
<Image Source="Images/Small/Clear.png"
RenderOptions.BitmapScalingMode="HighQuality" Stretch="Uniform" />
<TextBlock Text="Clear" VerticalAlignment="Center" Margin="5,0,0,0" />
</StackPanel>
</Button.Content>
</Button>
<ToggleButton
Margin="5,0,0,0"
Width="120"
Height="20"
IsChecked="{Binding Path=Pause, Mode=TwoWay}">
<ToggleButton.Content>
<StackPanel Orientation="Horizontal">
<Image Source="Images/Small/Pause.png"
RenderOptions.BitmapScalingMode="NearestNeighbor" Stretch="Uniform" />
<TextBlock Text="Pause" VerticalAlignment="Center" Margin="5,0,0,0" />
</StackPanel>
</ToggleButton.Content>
</ToggleButton>
</StackPanel>
<ListView
x:Name="ListView"
Grid.Row="2"
ItemsSource="{Binding Path=LogEvents.View, IsAsync=True}"
BorderThickness="0"
ScrollViewer.CanContentScroll="True"
listViewLayoutManager:ListViewLayoutManager.Enabled="true">
<ListView.Resources>
<xamlMultiValueConverter:ILogEventResolverToStringConverter x:Key="ILogEventResolverToStringConverter"/>
</ListView.Resources>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="BorderBrush" Value="Black" />
<Setter Property="BorderThickness" Value="1" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Level}"
Value="{x:Static nLog:LogLevel.Trace}">
<Setter Property="Background"
Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=dj:NLogViewer}, Path=TraceBackground}" />
<Setter Property="Foreground"
Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=dj:NLogViewer}, Path=TraceForeground}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=Level}"
Value="{x:Static nLog:LogLevel.Debug}">
<Setter Property="Foreground"
Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=dj:NLogViewer}, Path=DebugForeground}" />
<Setter Property="Background"
Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=dj:NLogViewer}, Path=DebugBackground}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=Level}"
Value="{x:Static nLog:LogLevel.Info}">
<Setter Property="Foreground"
Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=dj:NLogViewer}, Path=InfoForeground}" />
<Setter Property="Background"
Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=dj:NLogViewer}, Path=InfoBackground}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=Level}"
Value="{x:Static nLog:LogLevel.Warn}">
<Setter Property="Foreground"
Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=dj:NLogViewer}, Path=WarnForeground}" />
<Setter Property="Background"
Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=dj:NLogViewer}, Path=WarnBackground}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=Level}"
Value="{x:Static nLog:LogLevel.Error}">
<Setter Property="Foreground"
Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=dj:NLogViewer}, Path=ErrorForeground}" />
<Setter Property="Background"
Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=dj:NLogViewer}, Path=ErrorBackground}" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=Level}"
Value="{x:Static nLog:LogLevel.Fatal}">
<Setter Property="Foreground"
Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=dj:NLogViewer}, Path=FatalForeground}" />
<Setter Property="Background"
Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=dj:NLogViewer}, Path=FatalBackground}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<helper:AutoSizedGridView>
<GridViewColumn Header="ID" Width="40">
<GridViewColumn.DisplayMemberBinding>
<MultiBinding Converter="{StaticResource ILogEventResolverToStringConverter}">
<Binding Path="."/>
<Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=dj:NLogViewer}" Path="IdResolver"/>
</MultiBinding>
</GridViewColumn.DisplayMemberBinding>
</GridViewColumn>
<GridViewColumn Header="Level" Width="Auto">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Image x:Name="Image" RenderOptions.BitmapScalingMode="Linear"
Height="18" VerticalAlignment="Center" HorizontalAlignment="Center" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=Level}"
Value="{x:Static nLog:LogLevel.Trace}">
<Setter TargetName="Image" Property="Source"
Value="Images/Small/Trace.png" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=Level}"
Value="{x:Static nLog:LogLevel.Debug}">
<Setter TargetName="Image" Property="Source"
Value="Images/Small/Debug.png" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=Level}"
Value="{x:Static nLog:LogLevel.Info}">
<Setter TargetName="Image" Property="Source"
Value="Images/Small/Info.png" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=Level}"
Value="{x:Static nLog:LogLevel.Warn}">
<Setter TargetName="Image" Property="Source"
Value="Images/Small/Warning.png" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=Level}"
Value="{x:Static nLog:LogLevel.Error}">
<Setter TargetName="Image" Property="Source"
Value="Images/Small/Error.png" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=Level}"
Value="{x:Static nLog:LogLevel.Fatal}">
<Setter TargetName="Image" Property="Source"
Value="Images/Small/Fatal.png" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="TimeStamp" Width="Auto">
<GridViewColumn.DisplayMemberBinding>
<MultiBinding Converter="{StaticResource ILogEventResolverToStringConverter}">
<Binding Path="."/>
<Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=dj:NLogViewer}" Path="TimeStampResolver"/>
</MultiBinding>
</GridViewColumn.DisplayMemberBinding>
</GridViewColumn>
<GridViewColumn Header="LoggerName" Width="Auto">
<GridViewColumn.DisplayMemberBinding>
<MultiBinding Converter="{StaticResource ILogEventResolverToStringConverter}">
<Binding Path="."/>
<Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=dj:NLogViewer}" Path="LoggerNameResolver"/>
</MultiBinding>
</GridViewColumn.DisplayMemberBinding>
</GridViewColumn>
<GridViewColumn Header="Message" Width="Auto" listViewLayoutManager:RangeColumn.IsFillColumn="True">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox
IsReadOnly="True"
Background="Transparent"
BorderThickness="0"
TextWrapping="Wrap"
Foreground="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListViewItem}, Path=Foreground}">
<TextBox.Text>
<MultiBinding Converter="{StaticResource ILogEventResolverToStringConverter}">
<Binding Path="." Mode="OneWay"/>
<Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=dj:NLogViewer}" Path="MessageResolver"/>
</MultiBinding>
</TextBox.Text>
</TextBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</helper:AutoSizedGridView>
</ListView.View>
</ListView>
</Grid>
</UserControl>

View File

@@ -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
{
/// <summary>
/// Interaktionslogik für NLogViewer.xaml
/// </summary>
public partial class NLogViewer : UserControl
{
// ##############################################################################################################################
// Dependency Properties
// ##############################################################################################################################
#region Dependency Properties
// ##########################################################################################
// Colors
// ##########################################################################################
#region Colors
/// <summary>
/// The background for the trace output
/// </summary>
[Category("NLogViewerColors")]
public Brush TraceBackground
{
get => (Brush) GetValue(TraceBackgroundProperty);
set => SetValue(TraceBackgroundProperty, value);
}
/// <summary>
/// The <see cref="TraceBackground"/> DependencyProperty.
/// </summary>
public static readonly DependencyProperty TraceBackgroundProperty =
DependencyProperty.Register("TraceBackground", typeof(Brush), typeof(NLogViewer),
new PropertyMetadata((Brush) (new BrushConverter().ConvertFrom("#D3D3D3"))));
/// <summary>
/// The foreground for the trace output
/// </summary>
[Category("NLogViewerColors")]
public Brush TraceForeground
{
get => (Brush) GetValue(TraceForegroundProperty);
set => SetValue(TraceForegroundProperty, value);
}
/// <summary>
/// The <see cref="TraceForeground"/> DependencyProperty.
/// </summary>
public static readonly DependencyProperty TraceForegroundProperty =
DependencyProperty.Register("TraceForeground", typeof(Brush), typeof(NLogViewer),
new PropertyMetadata((Brush) (new BrushConverter().ConvertFrom("#042271"))));
/// <summary>
/// The background for the debug output
/// </summary>
[Category("NLogViewerColors")]
public Brush DebugBackground
{
get => (Brush) GetValue(DebugBackgroundProperty);
set => SetValue(DebugBackgroundProperty, value);
}
/// <summary>
/// The <see cref="DebugBackground"/> DependencyProperty.
/// </summary>
public static readonly DependencyProperty DebugBackgroundProperty =
DependencyProperty.Register("DebugBackground", typeof(Brush), typeof(NLogViewer),
new PropertyMetadata((Brush) (new BrushConverter().ConvertFrom("#90EE90"))));
/// <summary>
/// The foreground for the debug output
/// </summary>
[Category("NLogViewerColors")]
public Brush DebugForeground
{
get => (Brush) GetValue(DebugForegroundProperty);
set => SetValue(DebugForegroundProperty, value);
}
/// <summary>
/// The <see cref="DebugForeground"/> DependencyProperty.
/// </summary>
public static readonly DependencyProperty DebugForegroundProperty =
DependencyProperty.Register("DebugForeground", typeof(Brush), typeof(NLogViewer),
new PropertyMetadata((Brush) (new BrushConverter().ConvertFrom("#042271"))));
/// <summary>
/// The background for the info output
/// </summary>
[Category("NLogViewerColors")]
public Brush InfoBackground
{
get => (Brush) GetValue(InfoBackgroundProperty);
set => SetValue(InfoBackgroundProperty, value);
}
/// <summary>
/// The <see cref="InfoBackground"/> DependencyProperty.
/// </summary>
public static readonly DependencyProperty InfoBackgroundProperty = DependencyProperty.Register("InfoBackground",
typeof(Brush), typeof(NLogViewer),
new PropertyMetadata((Brush) (new BrushConverter().ConvertFrom("#0000FF"))));
/// <summary>
/// The foreground for the info output
/// </summary>
[Category("NLogViewerColors")]
public Brush InfoForeground
{
get => (Brush) GetValue(InfoForegroundProperty);
set => SetValue(InfoForegroundProperty, value);
}
/// <summary>
/// The <see cref="InfoForeground"/> DependencyProperty.
/// </summary>
public static readonly DependencyProperty InfoForegroundProperty = DependencyProperty.Register("InfoForeground",
typeof(Brush), typeof(NLogViewer), new PropertyMetadata(Brushes.White));
/// <summary>
/// The background for the warn output
/// </summary>
[Category("NLogViewerColors")]
public Brush WarnBackground
{
get => (Brush) GetValue(WarnBackgroundProperty);
set => SetValue(WarnBackgroundProperty, value);
}
/// <summary>
/// The <see cref="WarnBackground"/> DependencyProperty.
/// </summary>
public static readonly DependencyProperty WarnBackgroundProperty = DependencyProperty.Register("WarnBackground",
typeof(Brush), typeof(NLogViewer),
new PropertyMetadata((Brush) (new BrushConverter().ConvertFrom("#FFFF00"))));
/// <summary>
/// The foreground for the warn output
/// </summary>
[Category("NLogViewerColors")]
public Brush WarnForeground
{
get => (Brush) GetValue(WarnForegroundProperty);
set => SetValue(WarnForegroundProperty, value);
}
/// <summary>
/// The <see cref="WarnForeground"/> DependencyProperty.
/// </summary>
public static readonly DependencyProperty WarnForegroundProperty = DependencyProperty.Register("WarnForeground",
typeof(Brush), typeof(NLogViewer),
new PropertyMetadata((Brush) (new BrushConverter().ConvertFrom("#324B5C"))));
/// <summary>
/// The background for the error output
/// </summary>
[Category("NLogViewerColors")]
public Brush ErrorBackground
{
get => (Brush) GetValue(ErrorBackgroundProperty);
set => SetValue(ErrorBackgroundProperty, value);
}
/// <summary>
/// The <see cref="ErrorBackground"/> DependencyProperty.
/// </summary>
public static readonly DependencyProperty ErrorBackgroundProperty =
DependencyProperty.Register("ErrorBackground", typeof(Brush), typeof(NLogViewer),
new PropertyMetadata(Brushes.Red));
/// <summary>
/// The foreground for the error output
/// </summary>
[Category("NLogViewerColors")]
public Brush ErrorForeground
{
get => (Brush) GetValue(ErrorForegroundProperty);
set => SetValue(ErrorForegroundProperty, value);
}
/// <summary>
/// The <see cref="ErrorForeground"/> DependencyProperty.
/// </summary>
public static readonly DependencyProperty ErrorForegroundProperty =
DependencyProperty.Register("ErrorForeground", typeof(Brush), typeof(NLogViewer),
new PropertyMetadata(Brushes.White));
/// <summary>
/// The background for the fatal output
/// </summary>
[Category("NLogViewerColors")]
public Brush FatalBackground
{
get => (Brush) GetValue(FatalBackgroundProperty);
set => SetValue(FatalBackgroundProperty, value);
}
/// <summary>
/// The <see cref="FatalBackground"/> DependencyProperty.
/// </summary>
public static readonly DependencyProperty FatalBackgroundProperty =
DependencyProperty.Register("FatalBackground", typeof(Brush), typeof(NLogViewer),
new PropertyMetadata(Brushes.Black));
/// <summary>
/// The foreground for the fatal output
/// </summary>
[Category("NLogViewerColors")]
public Brush FatalForeground
{
get => (Brush) GetValue(FatalForegroundProperty);
set => SetValue(FatalForegroundProperty, value);
}
/// <summary>
/// The <see cref="FatalForeground"/> DependencyProperty.
/// </summary>
public static readonly DependencyProperty FatalForegroundProperty =
DependencyProperty.Register("FatalForeground", typeof(Brush), typeof(NLogViewer),
new PropertyMetadata(Brushes.Yellow));
#endregion
// ##########################################################################################
// NLogViewer
// ##########################################################################################
#region NLogViewer
/// <summary>
/// Is looking if any target with this name is configured and tries to link it
/// </summary>
[Category("NLogViewer")]
public string TargetName
{
get => (string)GetValue(TargetNameProperty);
set => SetValue(TargetNameProperty, value);
}
/// <summary>
/// The <see cref="TargetName"/> DependencyProperty.
/// </summary>
public static readonly DependencyProperty TargetNameProperty = DependencyProperty.Register("TargetName", typeof(string), typeof(NLogViewer), new PropertyMetadata(null));
/// <summary>
/// Private DP to bind to the gui
/// </summary>
[Category("NLogViewer")]
public CollectionViewSource LogEvents
{
get => (CollectionViewSource) GetValue(LogEventsProperty);
private set => SetValue(LogEventsProperty, value);
}
/// <summary>
/// The <see cref="LogEvents"/> DependencyProperty.
/// </summary>
public static readonly DependencyProperty LogEventsProperty = DependencyProperty.Register("LogEvents",
typeof(CollectionViewSource), typeof(NLogViewer), new PropertyMetadata(null));
/// <summary>
/// Automatically scroll to the newest entry
/// </summary>
[Category("NLogViewer")]
public bool AutoScroll
{
get => (bool)GetValue(AutoScrollProperty);
set => SetValue(AutoScrollProperty, value);
}
/// <summary>
/// The <see cref="AutoScroll"/> DependencyProperty.
/// </summary>
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();
}
/// <summary>
/// Delele all entries
/// </summary>
[Category("NLogViewer")]
public ICommand ClearCommand
{
get => (ICommand) GetValue(ClearCommandProperty);
set => SetValue(ClearCommandProperty, value);
}
/// <summary>
/// The <see cref="ClearCommand"/> DependencyProperty.
/// </summary>
public static readonly DependencyProperty ClearCommandProperty = DependencyProperty.Register("ClearCommand",
typeof(ICommand), typeof(NLogViewer), new PropertyMetadata(null));
/// <summary>
/// Stop logging
/// </summary>
[Category("NLogViewer")]
public bool Pause
{
get => (bool)GetValue(PauseProperty);
set => SetValue(PauseProperty, value);
}
/// <summary>
/// The <see cref="Pause"/> DependencyProperty.
/// </summary>
public static readonly DependencyProperty PauseProperty = DependencyProperty.Register("Pause", typeof(bool), typeof(NLogViewer), new PropertyMetadata(false));
/// <summary>
/// The maximum number of entries before automatic cleaning is performed. There is a hysteresis of 100 entries which must be exceeded.
/// Example: <see cref="MaxCount"/> is '1000'. Then after '1100' entries, everything until '1000' is deleted.
/// If set to '0' or less, it is deactivated
/// </summary>
[Category("NLogViewer")]
public int MaxCount
{
get => (int)GetValue(MaxCountProperty);
set => SetValue(MaxCountProperty, value);
}
/// <summary>
/// The <see cref="MaxCount"/> DependencyProperty.
/// </summary>
public static readonly DependencyProperty MaxCountProperty = DependencyProperty.Register("MaxCount", typeof(int), typeof(NLogViewer), new PropertyMetadata(5000));
#endregion
// ##########################################################################################
// Resolver
// ##########################################################################################
#region Resolver
/// <summary>
/// The <see cref="ILogEventInfoResolver"/> to format the id
/// </summary>
[Category("NLogViewerResolver")]
public ILogEventInfoResolver IdResolver
{
get => (ILogEventInfoResolver)GetValue(IdResolverProperty);
set => SetValue(IdResolverProperty, value);
}
/// <summary>
/// The <see cref="IdResolver"/> DependencyProperty.
/// </summary>
public static readonly DependencyProperty IdResolverProperty = DependencyProperty.Register("IdResolver", typeof(ILogEventInfoResolver), typeof(NLogViewer), new PropertyMetadata(new IdResolver()));
/// <summary>
/// The <see cref="ILogEventInfoResolver"/> to format the timestamp output
/// </summary>
[Category("NLogViewerResolver")]
public ILogEventInfoResolver TimeStampResolver
{
get => (ILogEventInfoResolver)GetValue(TimeStampResolverProperty);
set => SetValue(TimeStampResolverProperty, value);
}
/// <summary>
/// The <see cref="TimeStampResolver"/> DependencyProperty.
/// </summary>
public static readonly DependencyProperty TimeStampResolverProperty = DependencyProperty.Register("TimeStampResolver", typeof(ILogEventInfoResolver), typeof(NLogViewer), new PropertyMetadata(new TimeStampResolver()));
/// <summary>
/// The <see cref="ILogEventInfoResolver"/> to format the loggername
/// </summary>
[Category("NLogViewerResolver")]
public ILogEventInfoResolver LoggerNameResolver
{
get => (ILogEventInfoResolver)GetValue(LoggerNameResolverProperty);
set => SetValue(LoggerNameResolverProperty, value);
}
/// <summary>
/// The <see cref="LoggerNameResolver"/> DependencyProperty.
/// </summary>
public static readonly DependencyProperty LoggerNameResolverProperty = DependencyProperty.Register("LoggerNameResolver", typeof(ILogEventInfoResolver), typeof(NLogViewer), new PropertyMetadata(new LoggerNameResolver()));
/// <summary>
/// The <see cref="ILogEventInfoResolver"/> to format the message
/// </summary>
[Category("NLogViewerResolver")]
public ILogEventInfoResolver MessageResolver
{
get => (ILogEventInfoResolver)GetValue(MessageResolverProperty);
set => SetValue(MessageResolverProperty, value);
}
/// <summary>
/// The <see cref="MessageResolver"/> DependencyProperty.
/// </summary>
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<LogEventInfo> _LogEventInfos { get; } = new ObservableCollection<LogEventInfo>();
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<NLogViewer>(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
}
}

View File

@@ -0,0 +1,12 @@
using NLog;
namespace DJ.Resolver
{
/// <summary>
/// Default interface to resolve a <see cref="LogEventInfo"/> for your personal needs
/// </summary>
public interface ILogEventInfoResolver
{
string Resolve(LogEventInfo logEventInfo);
}
}

View File

@@ -0,0 +1,12 @@
using NLog;
namespace DJ.Resolver
{
public class IdResolver : ILogEventInfoResolver
{
public string Resolve(LogEventInfo logEventInfo)
{
return logEventInfo.SequenceID.ToString();
}
}
}

View File

@@ -0,0 +1,12 @@
using NLog;
namespace DJ.Resolver
{
public class LoggerNameResolver : ILogEventInfoResolver
{
public string Resolve(LogEventInfo logEventInfo)
{
return logEventInfo.LoggerName;
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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");
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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
{
/// <summary>
/// Look in the NLog.config if any target is already defined and returns it, otherwise a new one is registered
/// </summary>
/// <param name="defaultMaxCount">The maximum entries which should be buffered. Is only used if no target is defined</param>
/// <param name="targetName">The name of the target you want to link with</param>
/// <returns></returns>
public static CacheTarget GetInstance(int defaultMaxCount = 0, string targetName = null)
{
if(LogManager.Configuration == null)
LogManager.Configuration = new LoggingConfiguration();
var predicate = PredicateBuilder.True<Target>().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
// ##########################################################################################
/// <summary>
/// The maximum amount of entries held in buffer/cache
/// </summary>
public int MaxCount { get; set; } = 100;
public IObservable<LogEventInfo> Cache => _CacheSubject.AsObservable();
private readonly ReplaySubject<LogEventInfo> _CacheSubject;
// ##########################################################################################
// Private Properties
// ##########################################################################################
#endregion
// ##############################################################################################################################
// Constructor
// ##############################################################################################################################
#region Constructor
public CacheTarget()
{
_CacheSubject = new ReplaySubject<LogEventInfo>(MaxCount);
}
#endregion
// ##############################################################################################################################
// override
// ##############################################################################################################################
#region override
protected override void Write(LogEventInfo logEvent)
{
_CacheSubject.OnNext(logEvent);
}
#endregion
}
}

View File

@@ -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();
}
}
}

View File

@@ -0,0 +1,6 @@
{
"sdk": {
"version": "6.0.101",
"rollForward": "disable"
}
}