initial commit

This commit is contained in:
Matthias Heil
2026-04-04 13:30:13 +02:00
commit 6bed9b284c
186 changed files with 10650 additions and 0 deletions
@@ -0,0 +1,26 @@
using System;
using System.Globalization;
using Avalonia.Data.Converters;
using Avalonia.Media;
using SysDrawColor = System.Drawing.Color;
namespace LogViewer.Avalonia.Converters;
public class ChangeColorTypeConverter : IValueConverter
{
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is null)
return new SolidColorBrush((Color)(parameter ?? Colors.Black));
SysDrawColor sysDrawColor = (SysDrawColor)value!;
return new SolidColorBrush(Color.FromArgb(
sysDrawColor.A,
sysDrawColor.R,
sysDrawColor.G,
sysDrawColor.B));
}
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
=> throw new NotImplementedException();
}
@@ -0,0 +1,22 @@
using System.Globalization;
using Avalonia.Data.Converters;
using Microsoft.Extensions.Logging;
namespace LogViewer.Avalonia.Converters;
public class EventIdConverter : IValueConverter
{
public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is null)
return "0";
EventId eventId = (EventId)value;
return eventId.ToString();
}
// If not implemented, an error is thrown
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
=> new EventId(0, value?.ToString() ?? string.Empty);
}
@@ -0,0 +1,18 @@
using LogViewer.Core;
using LogViewer.Core.ViewModels;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using LogDataStore = LogViewer.Avalonia.Logging.LogDataStore;
namespace LogViewer.Avalonia;
public static class ServicesExtension
{
public static HostApplicationBuilder AddLogViewer(this HostApplicationBuilder builder)
{
builder.Services.AddSingleton<ILogDataStore, LogDataStore>();
builder.Services.AddSingleton<LogViewerControlViewModel>();
return builder;
}
}
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="0.10.18" />
<PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.18" />
<PackageReference Include="Avalonia.Desktop" Version="0.10.18" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="0.10.18" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.1.0" />
<PackageReference Include="XamlNameReferenceGenerator" Version="1.6.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Core\LogViewer.Core\LogViewer.Core.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="LogViewerControl.axaml.cs">
<DependentUpon>%(Filename)</DependentUpon>
</Compile>
</ItemGroup>
</Project>
@@ -0,0 +1,58 @@
<UserControl x:Class="LogViewer.Avalonia.LogViewerControl"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="clr-namespace:LogViewer.Avalonia.Converters"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800" DataContextChanged="OnDataContextChanged"
DetachedFromLogicalTree="OnDetachedFromLogicalTree">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.Resources>
<converters:ChangeColorTypeConverter x:Key="ColorConverter" />
<converters:EventIdConverter x:Key="EventIdConverter"/>
<SolidColorBrush x:Key="ColorBlack">Black</SolidColorBrush>
<SolidColorBrush x:Key="ColorTransparent">Transparent</SolidColorBrush>
</Grid.Resources>
<Grid.Styles>
<Style Selector="DataGridRow">
<Setter Property="Padding" Value="0" />
<Setter Property="Foreground" Value="{Binding Color.Foreground,
Converter={StaticResource ColorConverter}, ConverterParameter={StaticResource ColorBlack}}" />
<Setter Property="Background" Value="{Binding Color.Background,
Converter={StaticResource ColorConverter}, ConverterParameter={StaticResource ColorTransparent}}" />
</Style>
<Style Selector="DataGridCell.size">
<Setter Property="FontSize" Value="11" />
<Setter Property="Padding" Value="0" />
</Style>
</Grid.Styles>
<DataGrid x:Name="MyDataGrid"
Items="{Binding DataStore.Entries}" AutoGenerateColumns="False"
CanUserSortColumns="False"
LayoutUpdated="OnLayoutUpdated">
<DataGrid.Columns>
<DataGridTextColumn CellStyleClasses="size" Header="Time" Width="150" Binding="{Binding Timestamp}"/>
<DataGridTextColumn CellStyleClasses="size" Header="Level" Width="90" Binding="{Binding LogLevel}" />
<DataGridTextColumn CellStyleClasses="size" Header="Event Id" Width="120" Binding="{Binding EventId, Converter={StaticResource EventIdConverter}}" />
<DataGridTextColumn CellStyleClasses="size" Header="State" Width="300" Binding="{Binding State}" />
<DataGridTextColumn CellStyleClasses="size" Header="Exception" Width="300" Binding="{Binding Exception}" />
</DataGrid.Columns>
</DataGrid>
<CheckBox x:Name="CanAutoScroll"
FontSize="11"
Content="Auto Scroll log"
IsChecked="True"
Grid.Row="1"
Margin="20 10" />
</Grid>
</UserControl>
@@ -0,0 +1,42 @@
using System.Collections.Specialized;
using Avalonia.Controls;
using Avalonia.LogicalTree;
using LogViewer.Core;
namespace LogViewer.Avalonia;
public partial class LogViewerControl : UserControl
{
public LogViewerControl()
=> InitializeComponent();
private ILogDataStoreImpl? vm;
private LogModel? item;
private void OnDataContextChanged(object? sender, EventArgs e)
{
if (DataContext is null)
return;
vm = (ILogDataStoreImpl)DataContext;
vm.DataStore.Entries.CollectionChanged += OnCollectionChanged;
}
private void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
=> item = MyDataGrid.Items.Cast<LogModel>().LastOrDefault();
private void OnLayoutUpdated(object? sender, EventArgs e)
{
if (CanAutoScroll.IsChecked != true || item is null)
return;
MyDataGrid.ScrollIntoView(item, null);
item = null;
}
private void OnDetachedFromLogicalTree(object? sender, LogicalTreeAttachmentEventArgs e)
{
if (vm is null) return;
vm.DataStore.Entries.CollectionChanged -= OnCollectionChanged;
}
}
@@ -0,0 +1,13 @@
using Avalonia.Threading;
namespace LogViewer.Avalonia.Logging;
public class LogDataStore : Core.LogDataStore
{
#region Methods
public override async void AddEntry(Core.LogModel logModel)
=> await Dispatcher.UIThread.InvokeAsync(() => base.AddEntry(logModel));
#endregion
}