{"id":14965161,"url":"https://github.com/warappa/blazorbindings.avaloniabindings","last_synced_at":"2025-09-30T23:30:47.555Z","repository":{"id":195269554,"uuid":"692422767","full_name":"warappa/BlazorBindings.AvaloniaBindings","owner":"warappa","description":"AvaloniaUI Blazor Bindings - Build native Avalonia apps with Blazor","archived":false,"fork":true,"pushed_at":"2024-05-17T21:50:49.000Z","size":8469,"stargazers_count":41,"open_issues_count":1,"forks_count":3,"subscribers_count":6,"default_branch":"main","last_synced_at":"2024-09-30T11:09:45.476Z","etag":null,"topics":["avalonia","avalonia-ui","avaloniaui","blazor"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"Dreamescaper/BlazorBindings.Maui","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/warappa.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2023-09-16T12:35:02.000Z","updated_at":"2024-09-27T19:05:19.000Z","dependencies_parsed_at":"2023-09-17T08:35:52.449Z","dependency_job_id":"f3a74943-c66c-4fa4-b83e-f4ee8c07b983","html_url":"https://github.com/warappa/BlazorBindings.AvaloniaBindings","commit_stats":null,"previous_names":["warappa/blazorbindings.maui","warappa/blazorbindings.avaloniabindings"],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/warappa%2FBlazorBindings.AvaloniaBindings","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/warappa%2FBlazorBindings.AvaloniaBindings/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/warappa%2FBlazorBindings.AvaloniaBindings/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/warappa%2FBlazorBindings.AvaloniaBindings/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/warappa","download_url":"https://codeload.github.com/warappa/BlazorBindings.AvaloniaBindings/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234790147,"owners_count":18887106,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["avalonia","avalonia-ui","avaloniaui","blazor"],"created_at":"2024-09-24T13:34:18.451Z","updated_at":"2025-09-30T23:30:41.961Z","avatar_url":"https://github.com/warappa.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🪢 BlazorBindings.AvaloniaBindings\n\n[![Nuget](https://img.shields.io/nuget/v/BlazorBindings.AvaloniaBindings)](https://www.nuget.org/packages/BlazorBindings.AvaloniaBindings/)\n\n## ⏱️ TL;DR\n- Use \u003ca href=\"https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor\"\u003e⚡ Blazor\u003c/a\u003e syntax for \u003ca href=\"https://avaloniaui.net/\"\u003eAvalonia\u003c/a\u003e apps\n- 😎 Simpler syntax than XAML\n- 🪄 IntelliSense support\n- Get free \u003ca href=\"https://devblogs.microsoft.com/dotnet/introducing-net-hot-reload/\"\u003e🔥 Hot Reload\u003c/a\u003e support on-top\n- Still  🧪 experimental\n\n## 🤔 What Is It?\n\nThis library enables developers to build **native \u003ca href=\"https://avaloniaui.net/\"\u003eAvalonia\u003c/a\u003e apps** using the .NET's **\u003ca href=\"https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor\"\u003eBlazor\u003c/a\u003e UI model**.  \nThis means you can use the **Blazor syntax** to write and use Avalonia UI components and pages. If you used Blazor or Razor in the past, this will look very familiar.\n\nThis library **wraps native Avalonia's UI controls** and exposes them as **Blazor components**, so\n- 🚫 ***no* hybrid HTML stuff**, but \n- 🤩 **real Avalonia UI controls**\n\nAs Avalonia is **cross-platform**, this \n- enables you to write beautiful 💻 **desktop, 📱 mobile and 🌐 web apps**\n- **for every major platform** out there (yes, also 🐧 Linux)\n- with the **same 🏁 pixel-perfect look on every platform**\n\nAnd as this library builds on-top of the same foundation as the regular Blazor implementation, **Visual Studio's  🪄 IntelliSense works out-of-the-box**!\n\n## 🔬 Example: Counter Component\nThis is an example on how you use the Blazor UI model to create a **component** (aka. \"Blazor UI control\").\n\nThis is `Counter.razor`, a **Counter** Blazor UI component that **renders native Avalonia UI controls**.  \nThis component\n- shows a `Label` stating how often the `Button` beneath was pressed,\n- shows a `CheckBox` to toggle the visibility of the `Button`, and\n- the `Button` that increments the value on each button press.\n\n```razor\n\u003cStackPanel\u003e\n    \u003cLabel FontSize=\"30\"\u003eYou pressed @count times \u003c/Label\u003e\n    \u003cCheckBox @bind-IsChecked=\"showButton\"\u003eButton visible\u003c/CheckBox\u003e\n    @if (showButton)\n    {\n        \u003cButton Text=\"+1\" OnClick=\"@HandleClick\" /\u003e\n    }\n\u003c/StackPanel\u003e\n\n@code {\n    int count;\n    bool showButton = true;\n\n    void HandleClick()\n    {\n        count++;\n    }\n}\n```\n\nThe UI markup uses the **Blazor/Razor syntax** with **Avalonia specific wrapper components** `StackPanel`, `Label`, `CheckBox` and `Button`. This is followed by **C# code** in the **`@code` section** which defines the variables and the click-handler method that increments the counter [^1].  \n\n### ↔️ Binding\n For ➡️ **1-way binding**, Blazor only requires the `@\u003cvariable-name\u003e` expression that automatically updates - here the `Label`'s text on every counter update.  \n The **`@bind-` prefix** is used only if ↔️ **2-way binding** is required like on the `CheckBox` here.\n\n For more advanced bindings and a more complete picture please have a look at the \u003ca href=\"https://learn.microsoft.com/en-us/aspnet/core/blazor/components/data-binding?view=aspnetcore-8.0\"\u003eofficial Blazor documents\u003c/a\u003e.\n\n### ⤵️ Conditionals\nThis code also showcases the use of a regualar `if` statement that adds or removes the Button from the UI tree.\n \n\n\u003e [!NOTE]\n\u003e Unlike **XAML**, there is ***no* verbose and complex data-binding syntax** but just a **straight-forward use of variables and methods**.\n\u003e Also, Blazor supports real conditionals that allows you to actually add and remove parts of the UI from the UI tree, while XAML only supports hiding.\n\n## 🔬 Example: MainPage View\nThis is an example on how you use the Blazor UI model to create a **page**.\n\nThis is `MainPage.razor` page shows the current time and embedds the previous `Counter.razor` component.\n\n```razor\n@page \"/\"\n\u003cStackPanel\u003e\n    \u003cLabel FontSize=\"30\" Text=\"@time\"\u003e\u003c/Label\u003e\n    \u003cCounter /\u003e\n\u003c/StackPanel\u003e\n\n@code {\n    string time = DateTime.Now.ToString();\n}\n```\n\nAs you might already noted, this looks very familiar like a standard component - and this is by design. Only the name and the `@page \"/\"` declaration give hints that this should be used as a page.  \nThe `\"/\"` part is a route. It is useful if you want use routing in your application and paths like this can be used for navigating from one page to another.\n\n\u003e [!TIP]\n\u003e For a (somewhat) complete example please look at the `MainPage.razor` and `SubPage.razor` pages in `BlazorBindings.AvaloniaBindings.HelloWorld` sample.\n\n## ⚡ Blazor\nBlazor was originally a technology for interactive web apps. But the authors imagined from the start that it could also be used on-top of any UI framework. This architecture allows us to use Blazor to drive Avalonia controls.\n\n### 🔥 Sweet Extra: Hot Reload\nAs this library builds on the standard Blazor building blocks, this comes with free support of **\u003ca href=\"https://devblogs.microsoft.com/dotnet/introducing-net-hot-reload/\"\u003eHot Reload\u003c/a\u003e**. This means you can make code or UI changes while your app is running.\n\nTo see how Hot Reload in action, here's a video of how well it integrates in .NET applications which also in general applies to the support in this library:  \n\n\u003ca href=\"https://www.youtube.com/watch?v=H5vVVyrqdH8\"\u003e📺 Hot Reload in .NET 6 In 10 Minutes or Less\u003c/a\u003e\n\n## 📦 Using This Repository\n### 🛠️ Building\n- Open `BlazorBindings.AvaloniaBindings.sln` in Visual Studio 2022\n- Build solution\n\n### 🪛 (Re-)Generate Blazor Wrappers\nJust run `BlazorBindings.AvaloniaBindings.ComponentGenerator` - all wrapper classes in `BlazorBindings.AvaloniaBindings` get updated.\n\n#### 🌟 Register A New Avalonia Control With The Generator\n- Open `src/BlazorBindings.AvaloniaBindings/AttributeInfo.cs`\n- Add new `GenerateComponent` attribute for new UI controls that are not yet supported\n- Run the generator\n\n```csharp\n// Generate `Button` wrapper without further special customizations\n[assembly: GenerateComponent(typeof(Button))]\n\n// Generate `ContentControl` wrapper with 2 properties marked as accepting Blazor templates aka. `RenderFragment`s.\n[assembly: GenerateComponent(typeof(ContentControl),\n    ContentProperties = new[]\n    {\n        nameof(ContentControl.Content),\n        nameof(ContentControl.ContentTemplate)\n    })]\n```\n\n### ✍️ Blazorize Your Own Avalonia Controls\nIf you use **3rd party Avalonia controls** or have **self-made Avalonia controls**, you can write a Blazor wrapper class yourself **by hand** - you don't need the generator for this.\n\n1) Ensure the **Avalonia base class** of your component is **already blazorized** - if not, handle that one first following these steps\n2) Create a class **named like your Avalonia control**, eg. `Button`\n3) **Inherit** it from the ***Blazor* component equivalent** your Avalonia control inherits from\n4) **Add properties** for each Avalonia property named as in Avalonia\n5) Add the **`[Parameter]` attribute** to the property\n6) Use the **actual property type** like `Thickness` but not `StyledProperty\u003cThickness\u003e` - although if it is a **template property** like `ContentControl`'s `Content` property then use **`RenderFragment`** as its type\n7) Add a `CreateNativeElement()` method that returns a new Avalonia control that this Blazor component should wrap\n8) Override `HandleParameter(string name, object value)` to map the native value of a property to its Blazor counterpart and also set it on the native control\n9) If you have a `RenderFragment` or attached properties, please follow the tips below \n\n\n\u003e [!TIP]\n\u003e If you have a `RenderFragment` property, you also must override `RenderAdditionalElementContent(RenderTreeBuilder builder, ref int sequence)`.  \n\u003e Please refer to this library's components also using `RenderFragment`s like `ContentControl` or `ItemsControl` to see what `RenderTreeBuilderHelper` method you should call.\n\n\u003e [!TIP]\n\u003e If you have attached properties, you can register them by adding them to the static constructor.  \n\u003e Please refer to this library's components also using them like `Grid` or `Canvas`, especially the `RegisterAdditionalHandlers()` method found in `\u003ccomponent-name\u003e.generated.attachments.cs`.\n\n#### 🔌 Example: Blazorize Avalonia's Button\nThis simplified example is taken from this repository's generated `Button` Blazor component.\n\nWe use the `AC` namespace alias for `Avalonia.Controls` to make it easier to differenciate between `Avalonia.Controls.Button` and the current ***Blazor*** `Button` class we create. So all types prefixed with `AC` are the native Avalonia types.\n\n```csharp\nusing System.Windows.Input;\nusing AC = Avalonia.Controls;\n\n/// \u003csummary\u003e\n/// A standard button control.\n/// \u003c/summary\u003e\npublic partial class Button : ContentControl\n{\n    static Button()\n    {\n        RegisterAdditionalHandlers();\n    }\n\n    /// \u003csummary\u003e\n    /// Gets or sets a value indicating how the \u003csee cref=\"T:Avalonia.Controls.Button\" /\u003e should react to clicks.\n    /// \u003c/summary\u003e\n    [Parameter] public AC.ClickMode? ClickMode { get; set; }\n\n    ...\n\n    [Parameter] public EventCallback\u003cglobal::Avalonia.Interactivity.RoutedEventArgs\u003e OnClick { get; set; }\n\n    public new AC.Button NativeControl =\u003e (AC.Button)((AvaloniaObject)this).NativeControl;\n\n    protected override AC.Button CreateNativeElement() =\u003e new();\n\n    protected override void HandleParameter(string name, object value)\n    {\n        switch (name)\n        {\n            case nameof(ClickMode):\n                if (!Equals(ClickMode, value))\n                {\n                    ClickMode = (AC.ClickMode?)value;\n                    NativeControl.ClickMode = ClickMode ?? (AC.ClickMode)AC.Button.ClickModeProperty.GetDefaultValue(AC.Button.ClickModeProperty.OwnerType);\n                }\n                break;\n            \n            ...\n\n            case nameof(OnClick):\n                if (!Equals(OnClick, value))\n                {\n                    void NativeControlClick(object sender, global::Avalonia.Interactivity.RoutedEventArgs e) =\u003e InvokeEventCallback(OnClick, e);\n\n                    OnClick = (EventCallback\u003cglobal::Avalonia.Interactivity.RoutedEventArgs\u003e)value;\n                    NativeControl.Click -= NativeControlClick;\n                    NativeControl.Click += NativeControlClick;\n                }\n                break;\n\n            default:\n                base.HandleParameter(name, value);\n                break;\n        }\n    }\n\n    protected override void RenderAdditionalElementContent(RenderTreeBuilder builder, ref int sequence)\n    {\n        base.RenderAdditionalElementContent(builder, ref sequence);\n\n        // If the control has a `RenderFragment`, here is the place to hook this up to the rendering tree - see `ContentControl.generated.cs` for how this can be done.\n    }\n\n    static partial void RegisterAdditionalHandlers()\n    {\n        // Used for registering attached properties - see `Grid.generated.attachments.cs` for how that can be done\n    }\n}\n```\n\n## ℹ️ About this repository\n\nThis repository is a fork of Deamescapers's [Experimental MobileBlazorBindings](https://github.com/DreamEscaper/MobileBlazorBindings), which I decided to fork and maintain separately. If at any point of time Avalonia developers decide to push that repository moving forward, I'll gladly contribute all of my changes to the original repository. \n\n# 🤝 Code of Conduct\n\nThis project has adopted the code of conduct defined by the Contributor Covenant\nto clarify expected behavior in our community.\n\nFor more information, see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct).\n\nThank you!\n\n[^1]: You can also use a code-behind file, eg. for Blazor component `Foo.razor` you can add a `Foo.razor.cs` file. More details can be found in \u003ca href=\"https://learn.microsoft.com/en-us/aspnet/core/blazor/components/?view=aspnetcore-8.0#partial-class-support\"\u003eBlazor documentation\u003c/a\u003e.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwarappa%2Fblazorbindings.avaloniabindings","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwarappa%2Fblazorbindings.avaloniabindings","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwarappa%2Fblazorbindings.avaloniabindings/lists"}