BlazorBindings.Avalonia.Core
0.0.0-alpha.0.864
See the version list below for details.
dotnet add package BlazorBindings.Avalonia.Core --version 0.0.0-alpha.0.864
NuGet\Install-Package BlazorBindings.Avalonia.Core -Version 0.0.0-alpha.0.864
<PackageReference Include="BlazorBindings.Avalonia.Core" Version="0.0.0-alpha.0.864" />
paket add BlazorBindings.Avalonia.Core --version 0.0.0-alpha.0.864
#r "nuget: BlazorBindings.Avalonia.Core, 0.0.0-alpha.0.864"
// Install BlazorBindings.Avalonia.Core as a Cake Addin #addin nuget:?package=BlazorBindings.Avalonia.Core&version=0.0.0-alpha.0.864&prerelease // Install BlazorBindings.Avalonia.Core as a Cake Tool #tool nuget:?package=BlazorBindings.Avalonia.Core&version=0.0.0-alpha.0.864&prerelease
๐ชข BlazorBindings.Avalonia
โฑ๏ธ TL;DR
- Use <a href="https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor">โก Blazor</a> syntax for <a href="https://avaloniaui.net/">Avalonia</a> apps
- ๐ Simpler syntax than XAML
- ๐ช IntelliSense support
- Get free <a href="https://devblogs.microsoft.com/dotnet/introducing-net-hot-reload/">๐ฅ Hot Reload</a> support on-top
- Still ๐งช experimental
๐ค What Is It?
This library enables developers to build native <a href="https://avaloniaui.net/">Avalonia</a> apps using the .NET's <a href="https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor">Blazor</a> UI model.
This 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.
This library wraps native Avalonia's UI controls and exposes them as Blazor components, so
- ๐ซ no hybrid HTML stuff, but
- ๐คฉ real Avalonia UI controls
As Avalonia is cross-platform, this
- enables you to write beautiful ๐ป desktop, ๐ฑ mobile and ๐ web apps
- for every major platform out there (yes, also ๐ง Linux)
- with the same ๐ pixel-perfect look on every platform
And as this library builds on-top of the same foundation as the regular Blazor implementation, Visual Studio's ๐ช IntelliSense works out-of-the-box!
๐ฌ Example: Counter Component
This is an example on how you use the Blazor UI model to create a component (aka. "Blazor UI control").
This is Counter.razor
, a Counter Blazor UI component that renders native Avalonia UI controls.
This component
- shows a
Label
stating how often theButton
beneath was pressed, - shows a
CheckBox
to toggle the visibility of theButton
, and - the
Button
that increments the value on each button press.
<StackPanel>
<Label FontSize="30">You pressed @count times </Label>
<CheckBox @bind-IsChecked="showButton">Button visible</CheckBox>
@if (showButton)
{
<Button Text="+1" OnClick="@HandleClick" />
}
</StackPanel>
@code {
int count;
bool showButton = true;
void HandleClick()
{
count++;
}
}
The 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].
โ๏ธ Binding
For โก๏ธ 1-way binding, Blazor only requires the @<variable-name>
expression that automatically updates - here the Label
's text on every counter update.
The @bind-
prefix is used only if โ๏ธ 2-way binding is required like on the CheckBox
here.
For more advanced bindings and a more complete picture please have a look at the <a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/components/data-binding?view=aspnetcore-8.0">official Blazor documents</a>.
โคต๏ธ Conditionals
This code also showcases the use of a regualar if
statement that adds or removes the Button from the UI tree.
[!NOTE] Unlike XAML, there is no verbose and complex data-binding syntax but just a straight-forward use of variables and methods. 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.
๐ฌ Example: MainPage View
This is an example on how you use the Blazor UI model to create a page.
This is MainPage.razor
page shows the current time and embedds the previous Counter.razor
component.
@page "/"
<StackPanel>
<Label FontSize="30" Text="@time"></Label>
<Counter />
</StackPanel>
@code {
string time = DateTime.Now.ToString();
}
As 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.
The "/"
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.
[!TIP] For a (somewhat) complete example please look at the
MainPage.razor
andSubPage.razor
pages inBlazorBindings.Avalonia.HelloWorld
sample.
โก Blazor
Blazor 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.
๐ฅ Sweet Extra: Hot Reload
As this library builds on the standard Blazor building blocks, this comes with free support of <a href="https://devblogs.microsoft.com/dotnet/introducing-net-hot-reload/">Hot Reload</a>. This means you can make code or UI changes while your app is running.
To 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:
<a href="https://www.youtube.com/watch?v=H5vVVyrqdH8">๐บ Hot Reload in .NET 6 In 10 Minutes or Less</a>
๐ฆ Using This Repository
๐ ๏ธ Building
- Open
BlazorBindings.Avalonia.sln
in Visual Studio 2022 - Build solution
๐ช (Re-)Generate Blazor Wrappers
Just run BlazorBindings.Avalonia.ComponentGenerator
- all wrapper classes in BlazorBindings.Avalonia
get updated.
๐ Register A New Avalonia Control With The Generator
- Open
src/BlazorBindings.Avalonia/AttributeInfo.cs
- Add new
GenerateComponent
attribute for new UI controls that are not yet supported - Run the generator
// Generate `Button` wrapper without further special customizations
[assembly: GenerateComponent(typeof(Button))]
// Generate `ContentControl` wrapper with 2 properties marked as accepting Blazor templates aka. `RenderFragment`s.
[assembly: GenerateComponent(typeof(ContentControl),
ContentProperties = new[]
{
nameof(ContentControl.Content),
nameof(ContentControl.ContentTemplate)
})]
โ๏ธ Blazorize Your Own Avalonia Controls
If 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.
- Ensure the Avalonia base class of your component is already blazorized - if not, handle that one first following these steps
- Create a class named like your Avalonia control, eg.
Button
- Inherit it from the Blazor component equivalent your Avalonia control inherits from
- Add properties for each Avalonia property named as in Avalonia
- Add the
[Parameter]
attribute to the property - Use the actual property type like
Thickness
but notStyledProperty<Thickness>
- although if it is a template property likeContentControl
'sContent
property then useRenderFragment
as its type - Add a
CreateNativeElement()
method that returns a new Avalonia control that this Blazor component should wrap - 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 - If you have a
RenderFragment
or attached properties, please follow the tips below
[!TIP] If you have a
RenderFragment
property, you also must overrideRenderAdditionalElementContent(RenderTreeBuilder builder, ref int sequence)
.
Please refer to this library's components also usingRenderFragment
s likeContentControl
orItemsControl
to see whatRenderTreeBuilderHelper
method you should call.
[!TIP] If you have attached properties, you can register them by adding them to the static constructor.
Please refer to this library's components also using them likeGrid
orCanvas
, especially theRegisterAdditionalHandlers()
method found in<component-name>.generated.attachments.cs
.
๐ Example: Blazorize Avalonia's Button
This simplified example is taken from this repository's generated Button
Blazor component.
We 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.
using System.Windows.Input;
using AC = Avalonia.Controls;
/// <summary>
/// A standard button control.
/// </summary>
public partial class Button : ContentControl
{
static Button()
{
RegisterAdditionalHandlers();
}
/// <summary>
/// Gets or sets a value indicating how the <see cref="T:Avalonia.Controls.Button" /> should react to clicks.
/// </summary>
[Parameter] public AC.ClickMode? ClickMode { get; set; }
...
[Parameter] public EventCallback<global::Avalonia.Interactivity.RoutedEventArgs> OnClick { get; set; }
public new AC.Button NativeControl => (AC.Button)((AvaloniaObject)this).NativeControl;
protected override AC.Button CreateNativeElement() => new();
protected override void HandleParameter(string name, object value)
{
switch (name)
{
case nameof(ClickMode):
if (!Equals(ClickMode, value))
{
ClickMode = (AC.ClickMode?)value;
NativeControl.ClickMode = ClickMode ?? (AC.ClickMode)AC.Button.ClickModeProperty.GetDefaultValue(AC.Button.ClickModeProperty.OwnerType);
}
break;
...
case nameof(OnClick):
if (!Equals(OnClick, value))
{
void NativeControlClick(object sender, global::Avalonia.Interactivity.RoutedEventArgs e) => InvokeEventCallback(OnClick, e);
OnClick = (EventCallback<global::Avalonia.Interactivity.RoutedEventArgs>)value;
NativeControl.Click -= NativeControlClick;
NativeControl.Click += NativeControlClick;
}
break;
default:
base.HandleParameter(name, value);
break;
}
}
protected override void RenderAdditionalElementContent(RenderTreeBuilder builder, ref int sequence)
{
base.RenderAdditionalElementContent(builder, ref sequence);
// 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.
}
static partial void RegisterAdditionalHandlers()
{
// Used for registering attached properties - see `Grid.generated.attachments.cs` for how that can be done
}
}
โน๏ธ About this repository
This repository is a fork of Deamescapers's Experimental 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.
๐ค Code of Conduct
This project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our community.
For more information, see the .NET Foundation Code of Conduct.
Thank you!
[^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 <a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/components/?view=aspnetcore-8.0#partial-class-support">Blazor documentation</a>.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net8.0
- Microsoft.AspNetCore.Components (>= 8.0.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated | |
---|---|---|---|
0.1.2-alpha.0.8 | 69 | 9/28/2024 | |
0.1.2-alpha.0.7 | 64 | 9/23/2024 | |
0.0.0-alpha.0.864 | 65 | 9/23/2024 |