├── .devcontainer ├── Dockerfile ├── devcontainer.json └── xvfb_init.sh ├── .gitattributes ├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── DotNetBrowser.AvaloniaUi.Demo ├── App.axaml ├── App.axaml.cs ├── DataTemplates │ └── BrowserTabDataTemplate.cs ├── DotNetBrowser.AvaloniaUi.Demo.csproj ├── DotNetBrowser.AvaloniaUi.Demo.csproj.DotSettings ├── Icon.ico ├── MainWindow.axaml ├── MainWindow.axaml.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── TabModels │ ├── BrowserTabModel.cs │ ├── BrowserTabsModel.cs │ └── MessageEventArgs.cs ├── Views │ ├── BrowserTabView.axaml │ ├── BrowserTabView.axaml.cs │ ├── FullScreenWindow.axaml │ ├── FullScreenWindow.axaml.cs │ ├── NoLicenseDialog.axaml │ └── NoLicenseDialog.axaml.cs └── app.manifest ├── DotNetBrowser.Demo.sln ├── DotNetBrowser.Demo.sln.DotSettings ├── README.md ├── nuget.config └── screenshot.png /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/devcontainers/dotnet:0-6.0 AS dev 2 | 3 | # XVFB and other dependencies 4 | RUN apt-get update && \ 5 | apt-get -y install --no-install-recommends \ 6 | libgtk2.0-0 \ 7 | libgtk-3-0 \ 8 | libgbm1 \ 9 | libnotify4 \ 10 | libgconf-2-4 \ 11 | libnss3 \ 12 | libxss1 \ 13 | libasound2 \ 14 | libxtst6 \ 15 | xauth \ 16 | xvfb \ 17 | x11-xserver-utils 18 | 19 | ENV DISPLAY :0 20 | 21 | ADD xvfb_init.sh /etc/init.d/xvfb 22 | RUN chmod a+x /etc/init.d/xvfb 23 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "DotNetBrowser Development(.NET 6)", 3 | "build": { 4 | "dockerfile": "Dockerfile" 5 | }, 6 | "hostRequirements": { 7 | "cpus": 4, 8 | "memory": "4gb", 9 | "storage": "32gb" 10 | }, 11 | // Features to add to the dev container. More info: https://containers.dev/features. 12 | // Here, desktop-lite is configured, and its VNC ports are forwarded. 13 | "features": { 14 | "desktop-lite": { 15 | "password": "dotnetbrowser", 16 | "webPort": "6082", 17 | "vncPort": "5902" 18 | } 19 | }, 20 | "forwardPorts": [6082, 5902], 21 | "portsAttributes": { 22 | "5902": { 23 | "label": "VNC" 24 | }, 25 | "6082": { 26 | "label": "WEB VNC" 27 | } 28 | }, 29 | 30 | // Set DOTNETBROWSER_LICENSE for the whole container according to its value in the local environment. 31 | // This can be used to pass the license key to DotNetBrowser. 32 | // This also works if the license is stored in GitHub secrets. 33 | "containerEnv": { 34 | "DOTNETBROWSER_LICENSE": "${localEnv:DOTNETBROWSER_LICENSE}" 35 | }, 36 | 37 | // Start Xvfb whenever the container is started. 38 | "postStartCommand": "sudo /etc/init.d/xvfb start && dotnet tool restore" 39 | } 40 | -------------------------------------------------------------------------------- /.devcontainer/xvfb_init.sh: -------------------------------------------------------------------------------- 1 | XVFB=/usr/bin/Xvfb 2 | XVFBARGS=":0 -ac -screen 0 1024x768x16" 3 | PIDFILE=/var/xvfb_0.pid 4 | case "$1" in 5 | start) 6 | echo -n "Starting virtual X frame buffer: Xvfb" 7 | /sbin/start-stop-daemon --start --quiet --pidfile $PIDFILE --make-pidfile --background --exec $XVFB -- $XVFBARGS 8 | echo "." 9 | ;; 10 | stop) 11 | echo -n "Stopping virtual X frame buffer: Xvfb" 12 | /sbin/start-stop-daemon --stop --quiet --pidfile $PIDFILE 13 | echo "." 14 | ;; 15 | restart) 16 | $0 stop 17 | $0 start 18 | ;; 19 | *) 20 | echo "Usage: /etc/init.d/xvfb {start|stop|restart}" 21 | exit 1 22 | esac 23 | exit 0 24 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior, in case core.autocrlf is not set. 2 | * text=auto 3 | 4 | # Explicitly declare text files you want to always be normalized and converted 5 | # to native line endings on checkout. 6 | *.c text diff=cpp 7 | *.cc text diff=cpp 8 | *.cxx text diff=cpp 9 | *.cpp text diff=cpp 10 | *.c++ text diff=cpp 11 | *.hpp text diff=cpp 12 | *.h text diff=cpp 13 | *.h++ text diff=cpp 14 | *.hh text diff=cpp 15 | 16 | *.cs text diff=csharp 17 | 18 | *.csproj text merge=union eol=crlf 19 | *.sln text merge=union eol=crlf 20 | 21 | *.vbproj text eol=crlf 22 | 23 | *.vcxproj text eol=crlf 24 | *.vcxitems text eol=crlf 25 | *.props text eol=crlf 26 | *.filters text eol=crlf 27 | 28 | *.patch eol=lf 29 | 30 | *.doc diff=word 31 | *.xsl diff=excel 32 | *.xlsx diff=zip 33 | *.docx diff=zip -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # Build results 11 | [Dd]ebug/ 12 | [Dd]ebugPublic/ 13 | [Rr]elease/ 14 | [Rr]eleases/ 15 | x64/ 16 | x86/ 17 | build/ 18 | bld/ 19 | [Bb]in/ 20 | [Oo]bj/ 21 | [Tt]ools/ 22 | 23 | # Roslyn cache directories 24 | *.ide/ 25 | 26 | # Visual Studio profiler 27 | *.psess 28 | *.vsp 29 | *.vspx 30 | 31 | # TFS 2012 Local Workspace 32 | $tf/ 33 | 34 | # ReSharper is a .NET coding add-in 35 | _ReSharper*/ 36 | *.[Rr]e[Ss]harper 37 | *.DotSettings.user 38 | 39 | # JustCode is a .NET coding addin-in 40 | .JustCode 41 | 42 | # TeamCity is a build add-in 43 | _TeamCity* 44 | 45 | # DotCover is a Code Coverage Tool 46 | *.dotCover 47 | 48 | # NCrunch 49 | _NCrunch_* 50 | .*crunch*.local.xml 51 | 52 | # Web workbench (sass) 53 | .sass-cache/ 54 | 55 | # Installshield output folder 56 | [Ee]xpress/ 57 | 58 | # Click-Once directory 59 | publish/ 60 | 61 | # Publish Web Output 62 | *.[Pp]ublish.xml 63 | *.azurePubxml 64 | # TODO: Comment the next line if you want to checkin your web deploy settings 65 | # but database connection strings (with potential passwords) will be unencrypted 66 | *.pubxml 67 | *.publishproj 68 | 69 | # NuGet Packages 70 | *.nupkg 71 | # The packages folder can be ignored because of Package Restore 72 | **/packages/* 73 | # except build/, which is used as an MSBuild target. 74 | !**/packages/build/ 75 | # If using the old MSBuild-Integrated Package Restore, uncomment this: 76 | #!**/packages/repositories.config 77 | 78 | # Windows Store app package directory 79 | AppPackages/ 80 | 81 | # Others 82 | sql/ 83 | *.Cache 84 | ClientBin/ 85 | [Ss]tyle[Cc]op.* 86 | ~$* 87 | *~ 88 | *.dbmdl 89 | *.dbproj.schemaview 90 | *.pfx 91 | *.publishsettings 92 | node_modules/ 93 | 94 | # Backup & report files from converting an old project file 95 | # to a newer Visual Studio version. Backup files are not needed, 96 | # because we have git ;-) 97 | _UpgradeReport_Files/ 98 | Backup*/ 99 | UpgradeLog*.XML 100 | UpgradeLog*.htm 101 | 102 | #License files 103 | *.license 104 | 105 | #Ignore Visual Studio and Rider cache 106 | .idea 107 | *.vs 108 | 109 | #Ignore DotNetBrowser logs 110 | dotnetbrowser.log 111 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Launch Avalonia Demo", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | "program": "${workspaceFolder}/DotNetBrowser.AvaloniaUi.Demo/bin/Debug/net6.0/DotNetBrowser.AvaloniaUi.Demo.dll", 13 | "args": [], 14 | "cwd": "${workspaceFolder}/DotNetBrowser.AvaloniaUi.Demo", 15 | "console": "internalConsole", 16 | "stopAtEntry": false 17 | }, 18 | { 19 | "name": ".NET Core Attach", 20 | "type": "coreclr", 21 | "request": "attach" 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/DotNetBrowser.Demo.sln", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "publish", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "publish", 22 | "${workspaceFolder}/DotNetBrowser.Demo.sln", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "watch", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "watch", 34 | "run", 35 | "--project", 36 | "${workspaceFolder}/DotNetBrowser.Demo.sln" 37 | ], 38 | "problemMatcher": "$msCompile" 39 | } 40 | ] 41 | } -------------------------------------------------------------------------------- /DotNetBrowser.AvaloniaUi.Demo/App.axaml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /DotNetBrowser.AvaloniaUi.Demo/App.axaml.cs: -------------------------------------------------------------------------------- 1 | #region Copyright 2 | 3 | // Copyright © 2025, TeamDev. All rights reserved. 4 | // 5 | // Redistribution and use in source and/or binary forms, with or without 6 | // modification, must retain the above copyright notice and the following 7 | // disclaimer. 8 | // 9 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 10 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 11 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 12 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 13 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 14 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 15 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 16 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 17 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 18 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 19 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 20 | 21 | #endregion 22 | 23 | using Avalonia; 24 | using Avalonia.Controls.ApplicationLifetimes; 25 | using Avalonia.Markup.Xaml; 26 | 27 | namespace DotNetBrowser.AvaloniaUi.Demo 28 | { 29 | public class App : Application 30 | { 31 | public override void Initialize() 32 | { 33 | AvaloniaXamlLoader.Load(this); 34 | } 35 | 36 | public override void OnFrameworkInitializationCompleted() 37 | { 38 | if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) 39 | { 40 | desktop.MainWindow = new MainWindow(); 41 | } 42 | 43 | base.OnFrameworkInitializationCompleted(); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /DotNetBrowser.AvaloniaUi.Demo/DataTemplates/BrowserTabDataTemplate.cs: -------------------------------------------------------------------------------- 1 | #region Copyright 2 | 3 | // Copyright © 2025, TeamDev. All rights reserved. 4 | // 5 | // Redistribution and use in source and/or binary forms, with or without 6 | // modification, must retain the above copyright notice and the following 7 | // disclaimer. 8 | // 9 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 10 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 11 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 12 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 13 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 14 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 15 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 16 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 17 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 18 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 19 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 20 | 21 | #endregion 22 | 23 | using System.Collections.Concurrent; 24 | using Avalonia.Controls; 25 | using Avalonia.Controls.Templates; 26 | using DotNetBrowser.AvaloniaUi.Demo.TabModels; 27 | using DotNetBrowser.AvaloniaUi.Demo.Views; 28 | 29 | namespace DotNetBrowser.AvaloniaUi.Demo.DataTemplates 30 | { 31 | public class BrowserTabDataTemplate : IDataTemplate 32 | { 33 | private readonly ConcurrentDictionary views = new(); 34 | 35 | public Control Build(object data) 36 | { 37 | return views.GetOrAdd(data as BrowserTabModel, model => 38 | { 39 | if (model.Browser != null) 40 | { 41 | model.Browser.Disposed += delegate { views.TryRemove(model, out _); }; 42 | } 43 | 44 | return new BrowserTabView(); 45 | }); 46 | } 47 | 48 | public bool Match(object data) => data is BrowserTabModel; 49 | } 50 | } -------------------------------------------------------------------------------- /DotNetBrowser.AvaloniaUi.Demo/DotNetBrowser.AvaloniaUi.Demo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | WinExe 4 | net6.0 5 | 6 | copyused 7 | true 8 | false 9 | netstandard20 10 | app.manifest 11 | latest 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | BrowserTabView.axaml 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /DotNetBrowser.AvaloniaUi.Demo/DotNetBrowser.AvaloniaUi.Demo.csproj.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | Default -------------------------------------------------------------------------------- /DotNetBrowser.AvaloniaUi.Demo/Icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TeamDev-IP/Avalonia-WebView/18ecdf1d95d6e111a930edd41ac069d0f1d0266c/DotNetBrowser.AvaloniaUi.Demo/Icon.ico -------------------------------------------------------------------------------- /DotNetBrowser.AvaloniaUi.Demo/MainWindow.axaml: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 22 | 29 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 73 | 77 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /DotNetBrowser.AvaloniaUi.Demo/MainWindow.axaml.cs: -------------------------------------------------------------------------------- 1 | #region Copyright 2 | 3 | // Copyright © 2025, TeamDev. All rights reserved. 4 | // 5 | // Redistribution and use in source and/or binary forms, with or without 6 | // modification, must retain the above copyright notice and the following 7 | // disclaimer. 8 | // 9 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 10 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 11 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 12 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 13 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 14 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 15 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 16 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 17 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 18 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 19 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 20 | 21 | #endregion 22 | 23 | using System; 24 | using System.Diagnostics; 25 | using System.IO; 26 | using System.Threading.Tasks; 27 | using Avalonia.Controls; 28 | using Avalonia.Threading; 29 | using DotNetBrowser.AvaloniaUi.Demo.TabModels; 30 | using DotNetBrowser.AvaloniaUi.Demo.Views; 31 | using DotNetBrowser.Logging; 32 | using MsBox.Avalonia; 33 | using MsBox.Avalonia.Base; 34 | using MsBox.Avalonia.Dto; 35 | using MsBox.Avalonia.Models; 36 | 37 | namespace DotNetBrowser.AvaloniaUi.Demo 38 | { 39 | public partial class MainWindow : Window 40 | { 41 | public BrowserTabsModel Model { get; set; } 42 | 43 | public MainWindow() 44 | { 45 | LoggerProvider.Instance.Level = SourceLevels.Verbose; 46 | LoggerProvider.Instance.FileLoggingEnabled = true; 47 | LoggerProvider.Instance.OutputFile = "dotnetbrowser.log"; 48 | InitializeComponent(); 49 | Model = new BrowserTabsModel(); 50 | Model.AllTabsClosed += (_, _) => Close(); 51 | Model.EngineCrashed += (_, e) => ShowError(e.Message, e.Title); 52 | Model.EngineInitFailed += (_, e) => ShowError(e.Message, e.Title); 53 | Model.NoLicenseFound += ShowNoLicenseMessage; 54 | Model.TabCreated += (_, model) => MainTabControl.SelectedItem = model; 55 | DataContext = Model; 56 | Closed += MainWindow_Closed; 57 | } 58 | 59 | private void MainWindow_Closed(object sender, EventArgs e) 60 | { 61 | Model.DisposeEngine(); 62 | } 63 | 64 | private void ShowError(string message, string title) 65 | { 66 | MessageBoxCustomParams parameters = new() 67 | { 68 | Height = 150, 69 | ContentTitle = title, 70 | ContentMessage = message, 71 | WindowStartupLocation = WindowStartupLocation.CenterOwner, 72 | ButtonDefinitions = new[] 73 | { new ButtonDefinition { Name = "OK", IsDefault = true } }, 74 | }; 75 | IMsBox messageBoxStandardWindow = MessageBoxManager 76 | .GetMessageBoxCustom(parameters); 77 | 78 | messageBoxStandardWindow.ShowWindowDialogAsync(this); 79 | } 80 | 81 | private void ShowNoLicenseMessage(object sender, EventArgs args) 82 | { 83 | NoLicenseDialog dialog = new(); 84 | dialog.ShowDialog(this) 85 | .ContinueWith(t => 86 | { 87 | string license = t.Result; 88 | if (!string.IsNullOrWhiteSpace(license)) 89 | { 90 | string directory = 91 | Path.GetDirectoryName(typeof(MainWindow).Assembly.Location); 92 | string path = Path.Combine(directory, "dotnetbrowser.license"); 93 | File.WriteAllText(Path.GetFullPath(path), 94 | license); 95 | Dispatcher.UIThread.InvokeAsync(Initialize); 96 | } 97 | else 98 | { 99 | Close(); 100 | } 101 | }, TaskScheduler.FromCurrentSynchronizationContext()); 102 | } 103 | 104 | // ReSharper disable UnusedParameter.Local 105 | private void MainWindow_Opened(object sender, EventArgs e) 106 | { 107 | if (Design.IsDesignMode) 108 | { 109 | return; 110 | } 111 | 112 | Initialize(); 113 | } 114 | 115 | private void Initialize() 116 | { 117 | Model.CreateEngine(this); 118 | Model.CreateTab(); 119 | } 120 | // ReSharper restore UnusedParameter.Local 121 | } 122 | } -------------------------------------------------------------------------------- /DotNetBrowser.AvaloniaUi.Demo/Program.cs: -------------------------------------------------------------------------------- 1 | #region Copyright 2 | 3 | // Copyright © 2025, TeamDev. All rights reserved. 4 | // 5 | // Redistribution and use in source and/or binary forms, with or without 6 | // modification, must retain the above copyright notice and the following 7 | // disclaimer. 8 | // 9 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 10 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 11 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 12 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 13 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 14 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 15 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 16 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 17 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 18 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 19 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 20 | 21 | #endregion 22 | 23 | using System; 24 | using Avalonia; 25 | 26 | namespace DotNetBrowser.AvaloniaUi.Demo 27 | { 28 | internal class Program 29 | { 30 | // Avalonia configuration, don't remove; also used by visual designer. 31 | public static AppBuilder BuildAvaloniaApp() 32 | => AppBuilder.Configure() 33 | .UsePlatformDetect() 34 | .LogToTrace(); 35 | 36 | // Initialization code. Don't use any Avalonia, third-party APIs or any 37 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized 38 | // yet and stuff might break. 39 | [STAThread] 40 | public static void Main(string[] args) => BuildAvaloniaApp() 41 | .StartWithClassicDesktopLifetime(args); 42 | } 43 | } -------------------------------------------------------------------------------- /DotNetBrowser.AvaloniaUi.Demo/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "DotNetBrowser.AvaloniaUi.Demo": { 4 | "commandName": "Project" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /DotNetBrowser.AvaloniaUi.Demo/TabModels/BrowserTabModel.cs: -------------------------------------------------------------------------------- 1 | #region Copyright 2 | 3 | // Copyright © 2025, TeamDev. All rights reserved. 4 | // 5 | // Redistribution and use in source and/or binary forms, with or without 6 | // modification, must retain the above copyright notice and the following 7 | // disclaimer. 8 | // 9 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 10 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 11 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 12 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 13 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 14 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 15 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 16 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 17 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 18 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 19 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 20 | 21 | #endregion 22 | 23 | using System; 24 | using System.Collections.Generic; 25 | using System.ComponentModel; 26 | using System.IO; 27 | using System.Runtime.CompilerServices; 28 | using System.Threading.Tasks; 29 | using Avalonia.Media; 30 | using DotNetBrowser.Browser; 31 | using DotNetBrowser.Browser.Handlers; 32 | using DotNetBrowser.Engine; 33 | using DotNetBrowser.Handlers; 34 | using DotNetBrowser.Print.Handlers; 35 | using SkiaSharp; 36 | 37 | namespace DotNetBrowser.AvaloniaUi.Demo.TabModels 38 | { 39 | public class BrowserTabModel : INotifyPropertyChanged 40 | { 41 | private readonly Action action; 42 | private readonly string url; 43 | private IBrowser browser; 44 | private IImage favicon; 45 | private string header; 46 | private bool scrollbarsHidden; 47 | private string status; 48 | 49 | public IBrowser Browser 50 | { 51 | get => browser; 52 | set 53 | { 54 | browser = value; 55 | if (browser != null) 56 | { 57 | browser.TitleChanged += (_, _) => Header = Browser.Title; 58 | browser.StatusChanged += (_, e) => Status = e.Text; 59 | browser.FaviconChanged += 60 | (_, e) => Favicon = e.NewFavicon.ToUiBitmap(); 61 | browser.Navigation.FrameLoadFinished += (_, _) => UpdateStates(); 62 | LoadUrl(url); 63 | } 64 | } 65 | } 66 | 67 | public bool CanGoBack => Browser?.Navigation.CanGoBack() ?? false; 68 | public bool CanGoForward => Browser?.Navigation.CanGoForward() ?? false; 69 | 70 | public IImage Favicon 71 | { 72 | get => favicon; 73 | set => SetField(ref favicon, value); 74 | } 75 | 76 | public string Header 77 | { 78 | get => header; 79 | private set => SetField(ref header, value); 80 | } 81 | 82 | public RenderingMode RenderingMode { get; set; } 83 | 84 | public bool ScrollbarsHidden 85 | { 86 | get => scrollbarsHidden; 87 | set => SetField(ref scrollbarsHidden, value); 88 | } 89 | 90 | public string Status 91 | { 92 | get => status; 93 | set => SetField(ref status, value); 94 | } 95 | 96 | public event EventHandler StatesUpdated; 97 | 98 | public event PropertyChangedEventHandler PropertyChanged; 99 | 100 | public BrowserTabModel(string header, string url, Action action) 101 | { 102 | this.url = url; 103 | this.action = action; 104 | Header = header; 105 | } 106 | 107 | public void LoadUrl(string newUrl) 108 | { 109 | Browser?.Navigation.LoadUrl(newUrl) 110 | .ContinueWith(_ => { UpdateStates(); }, 111 | TaskScheduler.FromCurrentSynchronizationContext()); 112 | } 113 | 114 | public void OnClose() 115 | { 116 | action(this); 117 | Browser?.Dispose(); 118 | } 119 | 120 | public void OpenDevTools() 121 | { 122 | Browser?.DevTools.Show(); 123 | } 124 | 125 | public void Print() 126 | { 127 | Browser?.MainFrame.Print(); 128 | } 129 | 130 | public Task PrintToPdf(string file) 131 | { 132 | IHandler handler = 133 | Browser.RequestPrintHandler; 134 | Browser.RequestPrintHandler = 135 | new Handler( 136 | p => RequestPrintResponse.Print() 137 | ); 138 | TaskCompletionSource whenCompleted = new(); 139 | // Configure how the browser prints an HTML page. 140 | browser.PrintHtmlContentHandler = 141 | new Handler( 142 | p => 143 | { 144 | // Use the PDF printer. 145 | var printer = p.Printers.Pdf; 146 | var job = printer.PrintJob; 147 | 148 | // Set PDF file path. 149 | job.Settings.PdfFilePath = file; 150 | 151 | job.PrintCompleted += (_, _) => 152 | { 153 | whenCompleted.SetResult(file); 154 | }; 155 | 156 | Browser.RequestPrintHandler = handler; 157 | // Proceed with printing using the PDF printer. 158 | return PrintHtmlContentResponse.Print(printer); 159 | }); 160 | Print(); 161 | return whenCompleted.Task; 162 | } 163 | 164 | public void TakeScreenshot(string fileName) 165 | { 166 | SKImage img = Browser?.TakeImage().ToSkImage(); 167 | using FileStream stream = File.OpenWrite(Path.GetFullPath(fileName)); 168 | SKData d = img?.Encode(SKEncodedImageFormat.Png, 100); 169 | d?.SaveTo(stream); 170 | } 171 | 172 | protected virtual void OnPropertyChanged( 173 | [CallerMemberName] string propertyName = null) 174 | { 175 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 176 | } 177 | 178 | protected bool SetField(ref T field, T value, 179 | [CallerMemberName] string propertyName = null) 180 | { 181 | if (EqualityComparer.Default.Equals(field, value)) 182 | { 183 | return false; 184 | } 185 | 186 | field = value; 187 | OnPropertyChanged(propertyName); 188 | return true; 189 | } 190 | 191 | private void UpdateStates() 192 | { 193 | if (!Browser.IsDisposed) 194 | { 195 | Header = Browser.Title; 196 | Favicon = Browser.Favicon.ToUiBitmap(); 197 | OnPropertyChanged(nameof(CanGoBack)); 198 | OnPropertyChanged(nameof(CanGoForward)); 199 | StatesUpdated?.Invoke(this, EventArgs.Empty); 200 | } 201 | } 202 | } 203 | } -------------------------------------------------------------------------------- /DotNetBrowser.AvaloniaUi.Demo/TabModels/BrowserTabsModel.cs: -------------------------------------------------------------------------------- 1 | #region Copyright 2 | 3 | // Copyright © 2025, TeamDev. All rights reserved. 4 | // 5 | // Redistribution and use in source and/or binary forms, with or without 6 | // modification, must retain the above copyright notice and the following 7 | // disclaimer. 8 | // 9 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 10 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 11 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 12 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 13 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 14 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 15 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 16 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 17 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 18 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 19 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 20 | 21 | #endregion 22 | 23 | using System; 24 | using System.Collections.ObjectModel; 25 | using System.Diagnostics; 26 | using System.IO; 27 | using System.Linq; 28 | using Avalonia; 29 | using DotNetBrowser.AvaloniaUi.Dialogs; 30 | using DotNetBrowser.Engine; 31 | using DotNetBrowser.Handlers; 32 | using DotNetBrowser.Logging; 33 | using DotNetBrowser.Permissions.Handlers; 34 | 35 | namespace DotNetBrowser.AvaloniaUi.Demo.TabModels 36 | { 37 | public class BrowserTabsModel 38 | { 39 | private const string DefaultUrl = 40 | "https://links.teamdev.com/avalonia-case-study-app"; 41 | 42 | private IEngine engine; 43 | private RenderingMode renderingMode; 44 | 45 | public ObservableCollection Tabs { get; } = new(); 46 | 47 | public event EventHandler AllTabsClosed; 48 | public event EventHandler TabCreated; 49 | public event EventHandler EngineCrashed; 50 | public event EventHandler EngineInitFailed; 51 | public event EventHandler NoLicenseFound; 52 | 53 | public void CreateEngine(Visual parent) 54 | { 55 | string[] arguments = Environment.GetCommandLineArgs(); 56 | renderingMode = RenderingMode.HardwareAccelerated; 57 | ProprietaryFeatures proprietaryFeatures = ProprietaryFeatures.None; 58 | if (arguments.FirstOrDefault(arg => arg.ToLower().Contains("lightweight")) 59 | != null) 60 | { 61 | renderingMode = RenderingMode.OffScreen; 62 | } 63 | 64 | if (arguments.FirstOrDefault(arg => arg.ToLower().Contains("enable-file-log")) 65 | != null) 66 | { 67 | LoggerProvider.Instance.Level = SourceLevels.Verbose; 68 | LoggerProvider.Instance.FileLoggingEnabled = true; 69 | string logFile = $"DotNetBrowser-AvaloniaUi-{Guid.NewGuid()}.log"; 70 | LoggerProvider.Instance.OutputFile = Path.GetFullPath(logFile); 71 | } 72 | 73 | if (arguments.FirstOrDefault(arg => arg.ToLower().Contains("proprietary")) 74 | != null) 75 | { 76 | proprietaryFeatures = ProprietaryFeatures.Aac 77 | | ProprietaryFeatures.H264 78 | | ProprietaryFeatures.Widevine; 79 | } 80 | 81 | try 82 | { 83 | engine = EngineFactory.Create(new EngineOptions.Builder 84 | { 85 | RenderingMode = renderingMode, 86 | ProprietaryFeatures = proprietaryFeatures, 87 | ChromiumSwitches = { "--force-renderer-accessibility" } 88 | }.Build()); 89 | 90 | engine.Profiles.Default.Network.AuthenticateHandler = 91 | new DefaultAuthenticationHandler(parent); 92 | engine.Profiles.Default.Permissions.RequestPermissionHandler = 93 | new Handler(_ => RequestPermissionResponse 95 | .Grant()); 96 | engine.Disposed += (_, args) => 97 | { 98 | if (args.ExitCode != 0) 99 | { 100 | string message = 101 | $"The Chromium engine exit code was {args.ExitCode:x8}"; 102 | Trace.WriteLine(message); 103 | EngineCrashed?.Invoke(this, 104 | new MessageEventArgs(message, 105 | "DotNetBrowser Warning")); 106 | } 107 | }; 108 | } 109 | catch (NoLicenseException) 110 | { 111 | NoLicenseFound?.Invoke(this, EventArgs.Empty); 112 | } 113 | catch (Exception e) 114 | { 115 | Trace.WriteLine(e); 116 | EngineInitFailed?.Invoke(this, 117 | new MessageEventArgs(e.Message, 118 | "DotNetBrowser Initialization Error")); 119 | } 120 | } 121 | 122 | public void CreateTab(string url = DefaultUrl) 123 | { 124 | if (engine == null) 125 | { 126 | return; 127 | } 128 | BrowserTabModel browserTabModel = new("New Tab", url, CloseTab) 129 | { 130 | Browser = engine?.CreateBrowser(), 131 | RenderingMode = renderingMode 132 | }; 133 | Tabs.Insert(Tabs.Count, browserTabModel); 134 | TabCreated?.Invoke(this, browserTabModel); 135 | } 136 | 137 | public void DisposeEngine() 138 | { 139 | engine?.Dispose(); 140 | } 141 | 142 | public void OnNewTab() 143 | { 144 | CreateTab(); 145 | } 146 | 147 | private void CloseTab(BrowserTabModel browserTabModel) 148 | { 149 | Tabs.Remove(browserTabModel); 150 | if (Tabs.Count == 0) 151 | { 152 | AllTabsClosed?.Invoke(this, EventArgs.Empty); 153 | } 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /DotNetBrowser.AvaloniaUi.Demo/TabModels/MessageEventArgs.cs: -------------------------------------------------------------------------------- 1 | #region Copyright 2 | 3 | // Copyright © 2025, TeamDev. All rights reserved. 4 | // 5 | // Redistribution and use in source and/or binary forms, with or without 6 | // modification, must retain the above copyright notice and the following 7 | // disclaimer. 8 | // 9 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 10 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 11 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 12 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 13 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 14 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 15 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 16 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 17 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 18 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 19 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 20 | 21 | #endregion 22 | 23 | namespace DotNetBrowser.AvaloniaUi.Demo.TabModels 24 | { 25 | public class MessageEventArgs 26 | { 27 | public string Message { get; } 28 | public string Title { get; } 29 | 30 | public MessageEventArgs(string message, string title) 31 | { 32 | Message = message; 33 | Title = title; 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /DotNetBrowser.AvaloniaUi.Demo/Views/BrowserTabView.axaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 37 | 42 | 43 | 48 | 49 | 50 | 54 | 55 | 56 | 58 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /DotNetBrowser.AvaloniaUi.Demo/Views/BrowserTabView.axaml.cs: -------------------------------------------------------------------------------- 1 | #region Copyright 2 | 3 | // Copyright © 2025, TeamDev. All rights reserved. 4 | // 5 | // Redistribution and use in source and/or binary forms, with or without 6 | // modification, must retain the above copyright notice and the following 7 | // disclaimer. 8 | // 9 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 10 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 11 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 12 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 13 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 14 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 15 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 16 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 17 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 18 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 19 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 20 | 21 | #endregion 22 | 23 | using System; 24 | using System.Threading.Tasks; 25 | using Avalonia.Controls; 26 | using Avalonia.Input; 27 | using Avalonia.Interactivity; 28 | using Avalonia.Platform.Storage; 29 | using Avalonia.Threading; 30 | using Avalonia.VisualTree; 31 | using DotNetBrowser.AvaloniaUi.Demo.TabModels; 32 | using DotNetBrowser.Browser; 33 | using DotNetBrowser.Handlers; 34 | using DotNetBrowser.Input; 35 | using DotNetBrowser.Input.Keyboard.Events; 36 | using KeyEventArgs = Avalonia.Input.KeyEventArgs; 37 | 38 | // ReSharper disable UnusedParameter.Local 39 | 40 | namespace DotNetBrowser.AvaloniaUi.Demo.Views 41 | { 42 | public partial class BrowserTabView : UserControl 43 | { 44 | public BrowserTabModel Model => DataContext as BrowserTabModel; 45 | 46 | public BrowserTabView() 47 | { 48 | InitializeComponent(); 49 | DataContextChanged += OnDataContextChanged; 50 | } 51 | 52 | private void AddressBarKeyDown(object sender, KeyEventArgs e) 53 | { 54 | if (e.Key == Key.Enter) 55 | { 56 | Model?.LoadUrl(AddressBar.Text); 57 | e.Handled = true; 58 | } 59 | } 60 | 61 | private void FullScreen(object sender, RoutedEventArgs e) 62 | { 63 | FullScreenWindow fullScreenWindow = new FullScreenWindow(); 64 | fullScreenWindow.Opened += (_, __) => 65 | { 66 | Model.Browser.Focus(); 67 | }; 68 | fullScreenWindow.Closed += (s, e1) => 69 | { 70 | Model.Browser.Keyboard.KeyPressed.Handler = null; 71 | View.InitializeFrom(Model.Browser); 72 | View.IsVisible = true; 73 | }; 74 | 75 | Model.Browser.Keyboard.KeyPressed.Handler = 76 | new Handler(p => 77 | { 78 | if (p.VirtualKey is KeyCode.F11 or KeyCode.Escape) 79 | { 80 | Dispatcher.UIThread.InvokeAsync(() => fullScreenWindow.Close()); 81 | } 82 | 83 | return InputEventResponse.Proceed; 84 | }); 85 | View.IsVisible = false; 86 | fullScreenWindow.View.InitializeFrom(Model.Browser); 87 | 88 | fullScreenWindow.Show(); 89 | } 90 | 91 | private void OnDataContextChanged(object sender, EventArgs e) 92 | { 93 | if (Model != null) 94 | { 95 | View.InitializeFrom(Model.Browser); 96 | Model.StatesUpdated += OnStatesUpdated; 97 | DataContextChanged -= OnDataContextChanged; 98 | } 99 | } 100 | 101 | private void OnMenuButtonClick(object sender, RoutedEventArgs e) 102 | { 103 | Menu.ContextMenu?.Open(); 104 | } 105 | 106 | private void OnStatesUpdated(object sender, EventArgs e) 107 | { 108 | Dispatcher.UIThread.InvokeAsync(() => AddressBar.Text = Model?.Browser.Url); 109 | } 110 | 111 | private void PrintToPdf(object sender, RoutedEventArgs e) 112 | { 113 | IStorageProvider provider = TopLevel.GetTopLevel(this)?.StorageProvider; 114 | 115 | provider?.SaveFilePickerAsync(new FilePickerSaveOptions 116 | { 117 | Title = "Select where to save the PDF file", 118 | DefaultExtension = "pdf", 119 | FileTypeChoices = new[] { FilePickerFileTypes.Pdf }, 120 | }) 121 | .ContinueWith(async t => 122 | { 123 | string file = t.Result?.Path.LocalPath; 124 | if (!string.IsNullOrWhiteSpace(file) && Model != null) 125 | { 126 | return await Model.PrintToPdf(file); 127 | } 128 | 129 | return string.Empty; 130 | }, TaskScheduler.FromCurrentSynchronizationContext()) 131 | .ContinueWith(async t1 => 132 | { 133 | string pdf = await t1.Result; 134 | if (!string.IsNullOrWhiteSpace(pdf)) 135 | { 136 | Model?.LoadUrl(pdf); 137 | } 138 | }, default, TaskContinuationOptions.OnlyOnRanToCompletion, 139 | TaskScheduler.FromCurrentSynchronizationContext()); 140 | } 141 | 142 | private void TakeScreenshot(object sender, RoutedEventArgs e) 143 | { 144 | IStorageProvider provider = TopLevel.GetTopLevel(this)?.StorageProvider; 145 | 146 | provider?.SaveFilePickerAsync(new FilePickerSaveOptions 147 | { 148 | Title = "Select where to save the screenshot", 149 | DefaultExtension = "png", 150 | FileTypeChoices = new[] { FilePickerFileTypes.ImagePng }, 151 | }) 152 | .ContinueWith(t => 153 | { 154 | string file = t.Result?.Path.LocalPath; 155 | if (!string.IsNullOrWhiteSpace(file)) 156 | { 157 | Model?.TakeScreenshot(file); 158 | } 159 | }, TaskScheduler.FromCurrentSynchronizationContext()); 160 | } 161 | } 162 | } -------------------------------------------------------------------------------- /DotNetBrowser.AvaloniaUi.Demo/Views/FullScreenWindow.axaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /DotNetBrowser.AvaloniaUi.Demo/Views/FullScreenWindow.axaml.cs: -------------------------------------------------------------------------------- 1 | #region Copyright 2 | 3 | // Copyright © 2025, TeamDev. All rights reserved. 4 | // 5 | // Redistribution and use in source and/or binary forms, with or without 6 | // modification, must retain the above copyright notice and the following 7 | // disclaimer. 8 | // 9 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 10 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 11 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 12 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 13 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 14 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 15 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 16 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 17 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 18 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 19 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 20 | 21 | #endregion 22 | 23 | using Avalonia.Controls; 24 | using Avalonia.Input; 25 | 26 | namespace DotNetBrowser.AvaloniaUi.Demo.Views; 27 | 28 | public partial class FullScreenWindow : Window 29 | { 30 | public FullScreenWindow() 31 | { 32 | InitializeComponent(); 33 | } 34 | 35 | protected override void OnKeyDown(KeyEventArgs e) 36 | { 37 | base.OnKeyDown(e); 38 | if (e.Key is Key.F11 or Key.Escape) 39 | { 40 | Close(); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /DotNetBrowser.AvaloniaUi.Demo/Views/NoLicenseDialog.axaml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | Thank you for trying DotNetBrowser in AvaloniaUI. 28 | 29 | 30 | To run the demo, get the free trial license and insert it below: 31 | 32 | 33 | 34 | 35 | 36 |