├── .gitattributes ├── .gitignore ├── AvaloniaDemos.sln ├── BlockPatternAnimation ├── App.axaml ├── App.axaml.cs ├── Assets │ ├── avalonia-logo.ico │ └── demoScreenCapture.gif ├── BlockPatternAnimation.csproj ├── Program.cs ├── README.md └── Views │ ├── MainWindow.axaml │ └── MainWindow.axaml.cs ├── Debugging ├── App.axaml ├── App.axaml.cs ├── Assets │ ├── FontAwesome │ │ ├── Font Awesome 6 Brands-Regular-400.otf │ │ ├── Font Awesome 6 Free-Regular-400.otf │ │ └── Font Awesome 6 Free-Solid-900.otf │ ├── JetBrainsMono │ │ ├── JetBrainsMono-Bold-Italic.ttf │ │ ├── JetBrainsMono-Bold.ttf │ │ ├── JetBrainsMono-ExtraBold-Italic.ttf │ │ ├── JetBrainsMono-ExtraBold.ttf │ │ ├── JetBrainsMono-Italic.ttf │ │ ├── JetBrainsMono-Medium-Italic.ttf │ │ ├── JetBrainsMono-Medium.ttf │ │ └── JetBrainsMono-Regular.ttf │ ├── assetScreenCapture.gif │ ├── avalonia-logo.ico │ └── logFindScreenCapture.gif ├── DebugPlus │ ├── DebugPlus.cs │ ├── DebugPlusLogExtensions.cs │ ├── DebugPlusLogSink.cs │ ├── Native │ │ └── HBNative.cs │ └── StringBuilderCache.cs ├── Debugging.csproj ├── Program.cs ├── README.md ├── ViewLocator.cs ├── ViewModels │ ├── MainWindowViewModel.cs │ └── ViewModelBase.cs ├── Views │ ├── MainWindow.axaml │ └── MainWindow.axaml.cs └── app.manifest ├── Dialogs.Abstractions ├── DialogViewModel.cs ├── Dialogs.Abstractions.csproj ├── IInteractionService.cs ├── IRequestMediator.cs ├── Prompts.cs └── README.md ├── Dialogs.Todo ├── App.axaml ├── App.axaml.cs ├── Assets │ ├── avalonia-logo.ico │ ├── todoadditem.gif │ └── todoeditsaveload.gif ├── Converters │ ├── AppConverters.cs │ └── EnumToBooleanConverter.cs ├── Dialogs.Todo.csproj ├── Mappers │ └── TodoMappers.cs ├── Models │ └── TodoModel.cs ├── PresentationFactory.cs ├── Program.cs ├── README.md ├── Resources │ └── AppIcons.cs ├── Services │ ├── FileRequestService.cs │ ├── InteractionService.cs │ └── TodoService.cs ├── Styles │ └── AppStyles.axaml ├── ViewLocator.cs ├── ViewModels │ ├── MainWindowViewModel.cs │ ├── TodoCollectionViewModel.cs │ ├── TodoEditorViewModel.cs │ └── TodoViewModel.cs ├── Views │ ├── MainWindow.axaml │ ├── MainWindow.axaml.cs │ ├── TodoEditorView.axaml │ └── TodoEditorView.axaml.cs ├── _data │ └── default.json └── app.manifest ├── Directory.Build.props ├── Directory.Packages.props ├── ImageHotspot ├── App.axaml ├── App.axaml.cs ├── Assets │ ├── controller.jpg │ ├── controller.svg │ └── demoScreenCapture.gif ├── ImageHotspot.csproj ├── MainWindow.axaml ├── MainWindow.axaml.cs ├── Program.cs ├── README.md └── app.manifest ├── LICENSE.md ├── NoSchemaCsv.Immutable ├── App.axaml ├── App.axaml.cs ├── Assets │ ├── avalonia-logo.ico │ └── noschemacsv-immutable.gif ├── Messages.cs ├── NoSchemaCsv.Immutable.csproj ├── Program.cs ├── README.md ├── Services │ ├── CsvGeneratorService.cs │ └── CsvService.cs ├── ViewLocator.cs ├── ViewModels │ ├── MainWindowViewModel.cs │ └── ViewModelBase.cs ├── Views │ ├── MainWindow.axaml │ └── MainWindow.axaml.cs └── app.manifest ├── NuGet.config ├── README.md ├── RealTimeBitmapAdapter ├── App.axaml ├── App.axaml.cs ├── Assets │ ├── avalonia-logo.ico │ └── demoScreenCapture.png ├── ImageAdapters │ ├── BitmapAdapter.cs │ └── WriteableBitmapAdapter.cs ├── Models │ ├── ColorScaleImage.cs │ └── ImageBase.cs ├── Program.cs ├── README.md ├── RealTimeBitmapAdapter.csproj ├── Rng │ └── XoshiroRandom.cs ├── ViewLocator.cs ├── ViewModels │ ├── MainWindowViewModel.cs │ └── ViewModelBase.cs └── Views │ ├── MainWindow.axaml │ └── MainWindow.axaml.cs ├── Settings.XamlStyler ├── SkiaBitmapAdapter ├── App.axaml ├── App.axaml.cs ├── Assets │ ├── avalonia-logo.ico │ └── skbitmapscroll.gif ├── Program.cs ├── README.md ├── SkiaBitmapAdapter.csproj ├── ViewModels │ ├── MainWindowViewModel.cs │ └── SkiaBitmapViewModel.cs ├── Views │ ├── MainWindow.axaml │ ├── MainWindow.axaml.cs │ ├── SkiaBitmapView.axaml │ └── SkiaBitmapView.axaml.cs └── app.manifest ├── SkiaRendering.ExpandingCircles ├── App.axaml ├── App.axaml.cs ├── AppConverters.cs ├── Assets │ ├── avalonia-logo.ico │ └── expandingcircles.gif ├── Messages.cs ├── Program.cs ├── README.md ├── SkiaRendering.ExpandingCircles.csproj ├── ViewModels │ ├── CircleViewModel.cs │ ├── MainWindowViewModel.cs │ ├── ObservableElement.cs │ ├── ViewModelBase.cs │ └── WorldViewModel.cs ├── Views │ ├── MainWindow.axaml │ └── MainWindow.axaml.cs └── app.manifest ├── SkiaRendering.InfiniteCanvas ├── InfiniteCanvas.cs ├── InfiniteCanvas.props.cs ├── README.md ├── SKPaintSurfaceEventArgs.cs ├── SkiaRendering.InfiniteCanvas.csproj └── UpdateStateEventArgs.cs ├── SwipeNavigation ├── App.axaml ├── App.axaml.cs ├── Assets │ └── swipenavigation.gif ├── Gestures │ ├── CustomGestures.cs │ ├── SwipeGestureEndedEventArgs.cs │ ├── SwipeGestureEventArgs.cs │ ├── SwipeGestureRecognizer.cs │ └── SwipeGestureRecognizer.props.cs ├── MainView.axaml ├── MainView.axaml.cs ├── MainWindow.axaml ├── MainWindow.axaml.cs ├── Pages │ ├── PageOne.axaml │ ├── PageOne.axaml.cs │ ├── PageThree.axaml │ ├── PageThree.axaml.cs │ ├── PageTwo.axaml │ └── PageTwo.axaml.cs ├── Program.cs ├── README.md ├── SwipeNavigation.csproj ├── TransitioningPageControl │ ├── TransitioningPageControl.cs │ └── TransitioningPageControl.props.cs └── app.manifest ├── TabStripViewCaching ├── App.axaml ├── App.axaml.cs ├── Assets │ ├── avalonia-logo.ico │ └── tabstripcaching.gif ├── Program.cs ├── README.md ├── TabStripViewCaching.csproj ├── ViewLocator.cs ├── ViewModels │ ├── MainWindowViewModel.cs │ ├── PersonViewModel.cs │ └── TabViewModel.cs ├── Views │ ├── MainWindow.axaml │ ├── MainWindow.axaml.cs │ ├── PersonView.axaml │ └── PersonView.axaml.cs └── app.manifest ├── TextHighlighting ├── App.axaml ├── App.axaml.cs ├── Assets │ ├── textblockhighlighting.gif │ └── textboxhighlighting.gif ├── Highlighting │ ├── HighlightRangeCache.cs │ └── IHighlighter.cs ├── MainWindow.axaml ├── MainWindow.axaml.cs ├── Program.cs ├── README.md ├── TextHighlightBlock │ ├── TextHighlightBlock.cs │ └── TextHighlightBlock.props.cs ├── TextHighlightBox │ ├── HighlightingTextPresenter.cs │ ├── HighlightingTextPresenter.props.cs │ ├── StringBuilderCache.cs │ ├── TextHighlightBox.cs │ ├── TextHighlightBox.props.cs │ └── TextHighlightBoxTheme.axaml ├── TextHighlighting.csproj └── app.manifest ├── TextOutlineAnimation ├── App.axaml ├── App.axaml.cs ├── Assets │ ├── avalonia-logo.ico │ └── demoScreenCapture.gif ├── Controls │ ├── OutlinedText.cs │ └── OutlinedText.props.cs ├── Program.cs ├── README.md ├── TextOutlineAnimation.csproj ├── ViewLocator.cs ├── ViewModels │ ├── MainWindowViewModel.cs │ └── ViewModelBase.cs └── Views │ ├── MainWindow.axaml │ └── MainWindow.axaml.cs └── TransparentBrushTransitions ├── App.axaml ├── App.axaml.cs ├── Assets └── transparentbrushtransitions.gif ├── LerpHelpers.cs ├── MainWindow.axaml ├── MainWindow.axaml.cs ├── Program.cs ├── README.md ├── TransparentBrushTransition.cs ├── TransparentBrushTransitions.csproj └── app.manifest /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /BlockPatternAnimation/App.axaml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /BlockPatternAnimation/App.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls.ApplicationLifetimes; 3 | using Avalonia.Markup.Xaml; 4 | using BlockPatternAnimation.Views; 5 | 6 | namespace BlockPatternAnimation; 7 | public partial class App : Application 8 | { 9 | public override void Initialize() 10 | { 11 | AvaloniaXamlLoader.Load(this); 12 | } 13 | 14 | public override void OnFrameworkInitializationCompleted() 15 | { 16 | if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) 17 | { 18 | desktop.MainWindow = new MainWindow(); 19 | } 20 | 21 | base.OnFrameworkInitializationCompleted(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /BlockPatternAnimation/Assets/avalonia-logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevemonaco/AvaloniaDemos/2a64e0037da9902cb65729ec83ecfe3c2bef2fb9/BlockPatternAnimation/Assets/avalonia-logo.ico -------------------------------------------------------------------------------- /BlockPatternAnimation/Assets/demoScreenCapture.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevemonaco/AvaloniaDemos/2a64e0037da9902cb65729ec83ecfe3c2bef2fb9/BlockPatternAnimation/Assets/demoScreenCapture.gif -------------------------------------------------------------------------------- /BlockPatternAnimation/BlockPatternAnimation.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | WinExe 4 | true 5 | true 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /BlockPatternAnimation/Program.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using System; 3 | 4 | namespace BlockPatternAnimation; 5 | internal class Program 6 | { 7 | // Initialization code. Don't use any Avalonia, third-party APIs or any 8 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized 9 | // yet and stuff might break. 10 | [STAThread] 11 | public static void Main(string[] args) => BuildAvaloniaApp() 12 | .StartWithClassicDesktopLifetime(args); 13 | 14 | // Avalonia configuration, don't remove; also used by visual designer. 15 | public static AppBuilder BuildAvaloniaApp() 16 | => AppBuilder.Configure() 17 | .UsePlatformDetect() 18 | .LogToTrace(); 19 | } 20 | -------------------------------------------------------------------------------- /BlockPatternAnimation/README.md: -------------------------------------------------------------------------------- 1 | ![Pattern](Assets/demoScreenCapture.gif) 2 | 3 | Effect is implemented with multiple stacked `Grid`s, `VisualBrush`, and `TranslateTransform`: 4 | 5 | - Static layer with background solid color 6 | - Static layer with large squares 7 | - Animated layer of square-in-squares that move left and up 8 | - Animated layer of square-in-squares that move right and down 9 | 10 | [Original implementation](https://codepen.io/t_afif/full/OJvBbxm) in CSS by Temani Afif 11 | 12 | Thanks to [Starlk](https://github.com/starlkyt) for `Margin` suggestion to remove a grid resizing hack 13 | -------------------------------------------------------------------------------- /BlockPatternAnimation/Views/MainWindow.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | 3 | namespace BlockPatternAnimation.Views; 4 | public partial class MainWindow : Window 5 | { 6 | public MainWindow() 7 | { 8 | InitializeComponent(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Debugging/App.axaml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | avares://Debugging/Assets/JetBrainsMono#JetBrains Mono 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Debugging/App.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls.ApplicationLifetimes; 3 | using Avalonia.Data.Core.Plugins; 4 | using Avalonia.Markup.Xaml; 5 | using Debugging.ViewModels; 6 | using Debugging.Views; 7 | 8 | namespace Debugging; 9 | public partial class App : Application 10 | { 11 | public override void Initialize() 12 | { 13 | AvaloniaXamlLoader.Load(this); 14 | } 15 | 16 | public override void OnFrameworkInitializationCompleted() 17 | { 18 | if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) 19 | { 20 | // Line below is needed to remove Avalonia data validation. 21 | // Without this line you will get duplicate validations from both Avalonia and CT 22 | BindingPlugins.DataValidators.RemoveAt(0); 23 | desktop.MainWindow = new MainWindow 24 | { 25 | DataContext = new MainWindowViewModel(), 26 | }; 27 | } 28 | 29 | base.OnFrameworkInitializationCompleted(); 30 | } 31 | } -------------------------------------------------------------------------------- /Debugging/Assets/FontAwesome/Font Awesome 6 Brands-Regular-400.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevemonaco/AvaloniaDemos/2a64e0037da9902cb65729ec83ecfe3c2bef2fb9/Debugging/Assets/FontAwesome/Font Awesome 6 Brands-Regular-400.otf -------------------------------------------------------------------------------- /Debugging/Assets/FontAwesome/Font Awesome 6 Free-Regular-400.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevemonaco/AvaloniaDemos/2a64e0037da9902cb65729ec83ecfe3c2bef2fb9/Debugging/Assets/FontAwesome/Font Awesome 6 Free-Regular-400.otf -------------------------------------------------------------------------------- /Debugging/Assets/FontAwesome/Font Awesome 6 Free-Solid-900.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevemonaco/AvaloniaDemos/2a64e0037da9902cb65729ec83ecfe3c2bef2fb9/Debugging/Assets/FontAwesome/Font Awesome 6 Free-Solid-900.otf -------------------------------------------------------------------------------- /Debugging/Assets/JetBrainsMono/JetBrainsMono-Bold-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevemonaco/AvaloniaDemos/2a64e0037da9902cb65729ec83ecfe3c2bef2fb9/Debugging/Assets/JetBrainsMono/JetBrainsMono-Bold-Italic.ttf -------------------------------------------------------------------------------- /Debugging/Assets/JetBrainsMono/JetBrainsMono-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevemonaco/AvaloniaDemos/2a64e0037da9902cb65729ec83ecfe3c2bef2fb9/Debugging/Assets/JetBrainsMono/JetBrainsMono-Bold.ttf -------------------------------------------------------------------------------- /Debugging/Assets/JetBrainsMono/JetBrainsMono-ExtraBold-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevemonaco/AvaloniaDemos/2a64e0037da9902cb65729ec83ecfe3c2bef2fb9/Debugging/Assets/JetBrainsMono/JetBrainsMono-ExtraBold-Italic.ttf -------------------------------------------------------------------------------- /Debugging/Assets/JetBrainsMono/JetBrainsMono-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevemonaco/AvaloniaDemos/2a64e0037da9902cb65729ec83ecfe3c2bef2fb9/Debugging/Assets/JetBrainsMono/JetBrainsMono-ExtraBold.ttf -------------------------------------------------------------------------------- /Debugging/Assets/JetBrainsMono/JetBrainsMono-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevemonaco/AvaloniaDemos/2a64e0037da9902cb65729ec83ecfe3c2bef2fb9/Debugging/Assets/JetBrainsMono/JetBrainsMono-Italic.ttf -------------------------------------------------------------------------------- /Debugging/Assets/JetBrainsMono/JetBrainsMono-Medium-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevemonaco/AvaloniaDemos/2a64e0037da9902cb65729ec83ecfe3c2bef2fb9/Debugging/Assets/JetBrainsMono/JetBrainsMono-Medium-Italic.ttf -------------------------------------------------------------------------------- /Debugging/Assets/JetBrainsMono/JetBrainsMono-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevemonaco/AvaloniaDemos/2a64e0037da9902cb65729ec83ecfe3c2bef2fb9/Debugging/Assets/JetBrainsMono/JetBrainsMono-Medium.ttf -------------------------------------------------------------------------------- /Debugging/Assets/JetBrainsMono/JetBrainsMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevemonaco/AvaloniaDemos/2a64e0037da9902cb65729ec83ecfe3c2bef2fb9/Debugging/Assets/JetBrainsMono/JetBrainsMono-Regular.ttf -------------------------------------------------------------------------------- /Debugging/Assets/assetScreenCapture.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevemonaco/AvaloniaDemos/2a64e0037da9902cb65729ec83ecfe3c2bef2fb9/Debugging/Assets/assetScreenCapture.gif -------------------------------------------------------------------------------- /Debugging/Assets/avalonia-logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevemonaco/AvaloniaDemos/2a64e0037da9902cb65729ec83ecfe3c2bef2fb9/Debugging/Assets/avalonia-logo.ico -------------------------------------------------------------------------------- /Debugging/Assets/logFindScreenCapture.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stevemonaco/AvaloniaDemos/2a64e0037da9902cb65729ec83ecfe3c2bef2fb9/Debugging/Assets/logFindScreenCapture.gif -------------------------------------------------------------------------------- /Debugging/DebugPlus/DebugPlusLogExtensions.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Logging; 3 | using Microsoft.Extensions.Logging; 4 | 5 | namespace Monaco.Debugging; 6 | public static class DebugPlusLogExtensions 7 | { 8 | public static AppBuilder LogToDebugPlus(this AppBuilder builder, 9 | LogEventLevel level = LogEventLevel.Warning, 10 | ILogger? logger = null, 11 | params string[] areas) 12 | { 13 | Logger.Sink = new DebugPlusLogSink(level, areas, logger); 14 | return builder; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Debugging/DebugPlus/Native/HBNative.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using HarfBuzzSharp; 3 | 4 | namespace Monaco.Debugging.Native; 5 | 6 | // Taken from HarfBuzzSharp's internal generated bindings 7 | public unsafe static class HBNative 8 | { 9 | #if __IOS__ || __TVOS__ 10 | private const string _harfBuzz = "@rpath/libHarfBuzzSharp.framework/libHarfBuzzSharp"; 11 | #else 12 | private const string _harfBuzz = "libHarfBuzzSharp"; 13 | #endif 14 | 15 | [DllImport(_harfBuzz, CallingConvention = CallingConvention.Cdecl)] 16 | internal static extern uint hb_ot_name_get_utf8(nint face, OpenTypeNameId name_id, nint language, uint* text_size, /* char */ void* text); 17 | 18 | [DllImport(_harfBuzz, CallingConvention = CallingConvention.Cdecl)] 19 | internal static extern nint hb_language_from_string([MarshalAs(UnmanagedType.LPStr)] string str, int len); 20 | } 21 | -------------------------------------------------------------------------------- /Debugging/DebugPlus/StringBuilderCache.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the MIT license. 3 | using System; 4 | using System.Text; 5 | 6 | namespace Monaco.Debugging; 7 | 8 | /// Provide a cached reusable instance of stringbuilder per thread. 9 | internal static class StringBuilderCache 10 | { 11 | // The value 360 was chosen in discussion with performance experts as a compromise between using 12 | // as little memory per thread as possible and still covering a large part of short-lived 13 | // StringBuilder creations on the startup path of VS designers. 14 | internal const int MaxBuilderSize = 360; 15 | private const int DefaultCapacity = 16; // == StringBuilder.DefaultCapacity 16 | 17 | [ThreadStatic] 18 | private static StringBuilder? t_cachedInstance; 19 | 20 | /// Get a StringBuilder for the specified capacity. 21 | /// If a StringBuilder of an appropriate size is cached, it will be returned and the cache emptied. 22 | public static StringBuilder Acquire(int capacity = DefaultCapacity) 23 | { 24 | if (capacity <= MaxBuilderSize) 25 | { 26 | StringBuilder? sb = t_cachedInstance; 27 | if (sb != null) 28 | { 29 | // Avoid stringbuilder block fragmentation by getting a new StringBuilder 30 | // when the requested size is larger than the current capacity 31 | if (capacity <= sb.Capacity) 32 | { 33 | t_cachedInstance = null; 34 | sb.Clear(); 35 | return sb; 36 | } 37 | } 38 | } 39 | 40 | return new StringBuilder(capacity); 41 | } 42 | 43 | /// Place the specified builder in the cache if it is not too big. 44 | public static void Release(StringBuilder sb) 45 | { 46 | if (sb.Capacity <= MaxBuilderSize) 47 | { 48 | t_cachedInstance = sb; 49 | } 50 | } 51 | 52 | /// ToString() the stringbuilder, Release it to the cache, and return the resulting string. 53 | public static string GetStringAndRelease(StringBuilder sb) 54 | { 55 | string result = sb.ToString(); 56 | Release(sb); 57 | return result; 58 | } 59 | } -------------------------------------------------------------------------------- /Debugging/Debugging.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | WinExe 4 | true 5 | app.manifest 6 | true 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Debugging/Program.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Monaco.Debugging; 3 | using System; 4 | 5 | namespace Debugging; 6 | 7 | internal sealed class Program 8 | { 9 | [STAThread] 10 | public static void Main(string[] args) => BuildAvaloniaApp() 11 | .StartWithClassicDesktopLifetime(args); 12 | 13 | public static AppBuilder BuildAvaloniaApp() 14 | => AppBuilder.Configure() 15 | .UsePlatformDetect() 16 | .WithInterFont() 17 | .LogToDebugPlus(); // Register custom ILogSink 18 | } 19 | -------------------------------------------------------------------------------- /Debugging/README.md: -------------------------------------------------------------------------------- 1 | Collection of small debugging improvements over what's available in DevTools. It is necessary to add these into your own app, there's no attachment like DevTools. Most functionality is in 2 | the DebugPlus folder. 3 | 4 | ## App Binary Diagnostics 5 | 6 | * Assembly Listing 7 | * Asset Listing 8 | * Font Name IDs Listing 9 | 10 | Knowing what binaries your app relies upon is important. This demonstrates how to list all assemblies and assets for the current app while it is running. 11 | 12 | Fonts have long been a confusing part of XAML as the rules are a bit different. The filename has no effect on the Uri that needs to be referenced. The key name is created from 13 | the Typographic Family Name (or fallback to Font Family Name if none available) which are two fields within the Name IDs section. This feature predicts what the full Uri 14 | should be by reading the Name IDs from a font included as an asset and provides the XAML necessary to create a `FontFamily`. 15 | 16 | [FontDrop](https://fontdrop.info/#/?darkmode=true) and [msdocs](https://learn.microsoft.com/en-us/typography/opentype/spec/name#name-ids) were helpful in the font area. 17 | 18 | ![Binary Diagnostics](Assets/assetScreenCapture.gif) 19 | 20 | ## Improved Locating for Bad Bindings 21 | 22 | * Customized ILogSink with Visual Tree Information 23 | * Lookup Control via Hash ID 24 | 25 | Locating the source of bad bindings has been a long-running problem, mitigated by use of compiled bindings more recently. However, it's still occasionally an issue. Avalonia logs 26 | these binding errors and prints out the Hash ID when the Control containing the bad binding doesn't have a name. `DebugPlusLogSink` changes this so that all events will 27 | log the Hash ID, Name (if available), and the Visual Tree hierarchy. 28 | 29 | When you have the Control Hash ID, you can locate the actual Control by searching the Visual Tree. The capture below demonstrates this. 30 | 31 | ![Bad Binding Lookup](Assets/logFindScreenCapture.gif) 32 | 33 | The logged message is: 34 | ``` 35 | [Binding]An error occurred binding 'Command' to 'BadBinding' at 'BadBinding': 'Could not find a matching property accessor for 'BadBinding' on 'Debugging.ViewModels.MainWindowViewModel'.' (Button #36936550 #badButton Visual Tree: Grid -> StackPanel -> Button) 36 | ``` 37 | -------------------------------------------------------------------------------- /Debugging/ViewLocator.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Controls.Templates; 3 | using Debugging.ViewModels; 4 | using System; 5 | 6 | namespace Debugging; 7 | public class ViewLocator : IDataTemplate 8 | { 9 | public Control? Build(object? data) 10 | { 11 | if (data is null) 12 | return null; 13 | 14 | var name = data.GetType().FullName!.Replace("ViewModel", "View", StringComparison.Ordinal); 15 | var type = Type.GetType(name); 16 | 17 | if (type != null) 18 | { 19 | var control = (Control)Activator.CreateInstance(type)!; 20 | control.DataContext = data; 21 | return control; 22 | } 23 | 24 | return new TextBlock { Text = "Not Found: " + name }; 25 | } 26 | 27 | public bool Match(object? data) 28 | { 29 | return data is ViewModelBase; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Debugging/ViewModels/MainWindowViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using CommunityToolkit.Mvvm.Input; 3 | using Monaco.Debugging; 4 | using System.Collections.ObjectModel; 5 | using System; 6 | using System.Linq; 7 | using System.IO; 8 | 9 | namespace Debugging.ViewModels; 10 | 11 | public partial class MainWindowViewModel : ViewModelBase 12 | { 13 | [ObservableProperty] private ObservableCollection _display = new(); 14 | [ObservableProperty] private bool _filterResourceXamlInfo = false; 15 | [ObservableProperty] private string _fontUri = ""; 16 | 17 | [RelayCommand] 18 | public void ListAssemblies() 19 | { 20 | Display.Clear(); 21 | 22 | foreach (var assembly in DebugPlus.GetAllAssemblyInfo()) 23 | { 24 | Display.Add($"Assembly Name: {assembly.Name}, URI: {assembly.Uri?.AbsoluteUri ?? "Unavailable"}"); 25 | } 26 | } 27 | 28 | [RelayCommand] 29 | public void ListAssets() 30 | { 31 | Display.Clear(); 32 | 33 | var infos = DebugPlus.GetAllAssetInfo() 34 | .Where(x => FilterResourceXamlInfo ? !x.Uri.AbsolutePath.Contains("!AvaloniaResourceXamlInfo") : true); 35 | 36 | foreach (var info in infos) 37 | { 38 | Display.Add($"Asset URI: {info.Uri} | Size: {info.Length}"); 39 | } 40 | } 41 | 42 | [RelayCommand] 43 | public void ListFontIdNamesCommand() 44 | { 45 | if (string.IsNullOrWhiteSpace(FontUri)) 46 | return; 47 | 48 | Display.Clear(); 49 | 50 | var uri = new Uri(FontUri); 51 | var infos = DebugPlus.GetNameIdsFromFontUri(uri); 52 | var fontName = infos.FirstOrDefault(x => x.Id == "FontFamily")?.Name; 53 | var typographicName = infos.FirstOrDefault(x => x.Id == "TypographicFamily")?.Name; 54 | 55 | // Poor code for building an avares:// scheme without the font file name 56 | if (Path.GetDirectoryName(FontUri) is not string initialFolder) 57 | return; 58 | 59 | var fontFolder = initialFolder.Replace('\\', '/').Replace("avares:/", "avares://"); 60 | 61 | string? standardFontName = string.IsNullOrEmpty(typographicName) ? fontName : typographicName; 62 | string resourceFontName = $"{fontFolder}#{standardFontName}"; 63 | 64 | foreach (var info in infos) 65 | { 66 | Display.Add($"{info.Id}: {info.Name}"); 67 | } 68 | 69 | Display.Add($@"!Avalonia Resource: {resourceFontName}"); 70 | Display.Add($@"!AXAML Snippet: {resourceFontName}"); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Debugging/ViewModels/ViewModelBase.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | 3 | namespace Debugging.ViewModels; 4 | public class ViewModelBase : ObservableObject 5 | { 6 | } 7 | -------------------------------------------------------------------------------- /Debugging/Views/MainWindow.axaml: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | 24 |