Replies: 38 comments
-
Nice, very similar to a syntax I have been thinking about, based somewhat on QML. Shouldn't be a pre-1.0 priority IMO but would definitely be nice to explore. One suggestion: what about handlebar syntax for bindings: Also: including C# in markup would be a KILLER feature. |
Beta Was this translation helpful? Give feedback.
-
Proposal for C# syntax embedded inside xaml file: Current syntax (C# and XAML file separate): public class IsToolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is ITool tool && parameter is string name)
{
if (tool.Title == name)
{
return true;
}
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
} <MenuItem Header="Scr_ibble" Command="{Binding SetTool}" CommandParameter="Scribble">
<MenuItem.Icon>
<CheckBox BorderThickness="0" IsHitTestVisible="False" IsChecked="{Binding CurrentTool, Converter={StaticResource IsToolConverter}, ConverterParameter=Scribble, Mode=OneWay}"/>
</MenuItem.Icon>
</MenuItem> alternative syntax (single XAML file): csharp {
using Draw2D.ViewModels;
bool IsTool(ITool value, string name) => tool?.Title == name;
}
MenuItem {
Header="Scr_ibble", Command="{Binding SetTool}", CommandParameter="Scribble"
Icon {
CheckBox { BorderThickness="0" IsHitTestVisible="False" IsChecked="{x:Bind IsTool(CurrentTool, "Scribble")}" }
}
} |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
Design-time support would be a bit wonky since compiled code would be in a separate assembly where we can't disable visibility checks. We can inject |
Beta Was this translation helpful? Give feedback.
-
Look at what Google just announced today at I/O 2019: https://developer.android.com/jetpack/compose But that probably only looks good with Kotlin syntax though :( Sadly C# does not have much support for DSLs. |
Beta Was this translation helpful? Give feedback.
-
Playing with the alternative syntax using some of my existing xaml. XAML: <Grid RowDefinitions="Auto,*" ColumnDefinitions="*">
<Border x:Name="button" Background="{DynamicResource GreenBrush}" Width="100" Height="50" Grid.Row="1" Grid.Column="0" Margin="5,0,0,5" HorizontalAlignment="Center" VerticalAlignment="Center">
<i:Interaction.Behaviors>
<ia:EventTriggerBehavior EventName="PointerPressed" SourceObject="{Binding #button}">
<iac:CaptureMouseDeviceAction/>
<ia:ChangePropertyAction TargetObject="{Binding #button}" PropertyName="Background" Value="{DynamicResource RedBrush}"/>
<ia:ChangePropertyAction TargetObject="{Binding #text}" PropertyName="Foreground" Value="{DynamicResource YellowBrush}"/>
<ia:CallMethodAction TargetObject="{Binding}" MethodName="IncrementCount"/>
</ia:EventTriggerBehavior>
<ia:EventTriggerBehavior EventName="PointerReleased" SourceObject="{Binding #button}">
<iac:ReleaseMouseDeviceAction/>
<ia:ChangePropertyAction TargetObject="{Binding #button}" PropertyName="Background" Value="{DynamicResource GreenBrush}"/>
<ia:ChangePropertyAction TargetObject="{Binding #text}" PropertyName="Foreground" Value="{DynamicResource WhiteBrush}"/>
<ia:CallMethodAction TargetObject="{Binding}" MethodName="DecrementCount"/>
</ia:EventTriggerBehavior>
</i:Interaction.Behaviors>
<TextBlock x:Name="text" Text="{Binding Count}" Foreground="{DynamicResource WhiteBrush}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Border>
</Grid> Alternative markup syntax: Grid { RowDefinitions="Auto,*", ColumnDefinitions="*"
Border { x:Name="button", Background=${DynamicResource GreenBrush}, Width="100", Height="50", Grid.Row="1", Grid.Column="0", Margin="5,0,0,5", HorizontalAlignment="Center", VerticalAlignment="Center"
i:Interaction.Behaviors {
ia:EventTriggerBehavior { EventName="PointerPressed", SourceObject=${Binding #button}
Actions {
iac:CaptureMouseDeviceAction
ia:ChangePropertyAction { TargetObject=${Binding #button}, PropertyName="Background", Value=${DynamicResource RedBrush} }
ia:ChangePropertyAction { TargetObject=${Binding #text}, PropertyName="Foreground", Value=${DynamicResource YellowBrush} }
ia:CallMethodAction { TargetObject=${Binding}, MethodName="IncrementCount" }
}
}
ia:EventTriggerBehavior { EventName="PointerReleased", SourceObject=${Binding #button}
Actions {
iac:ReleaseMouseDeviceAction
ia:ChangePropertyAction { TargetObject=${Binding #button}, PropertyName="Background", Value=${DynamicResource GreenBrush} }
ia:ChangePropertyAction { TargetObject=${Binding #text}, PropertyName="Foreground", Value=${DynamicResource WhiteBrush} }
ia:CallMethodAction { TargetObject="{Binding}", MethodName="DecrementCount" }
}
}
TextBlock { x:Name="text", Text=${Binding Count}, Foreground=${DynamicResource WhiteBrush}, VerticalAlignment="Center", HorizontalAlignment="Center" }
}
} |
Beta Was this translation helpful? Give feedback.
-
Another example: XAML: <UserControl x:Class="Draw2D.Views.Style.ShapeStyleView"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Draw2D.Views.Style"
mc:Ignorable="d"
d:DesignWidth="400" d:DesignHeight="300">
<TabControl Classes="default" TabStripPlacement="Top">
<TabItem Classes="default" Header="Style">
<Grid RowDefinitions="Auto,Auto,Auto" ColumnDefinitions="Auto,*">
<TextBlock Classes="default" Grid.Column="0" Grid.Row="0" Text="Thickness"/>
<TextBox Classes="default" Grid.Column="1" Grid.Row="0" Text="{Binding Thickness}"/>
<TextBlock Classes="default" Grid.Column="0" Grid.Row="1" Text="IsStroked"/>
<CheckBox Classes="default" Grid.Column="1" Grid.Row="1" IsChecked="{Binding IsStroked}"/>
<TextBlock Classes="default" Grid.Column="0" Grid.Row="2" Text="IsFilled"/>
<CheckBox Classes="default" Grid.Column="1" Grid.Row="2" IsChecked="{Binding IsFilled}"/>
</Grid>
</TabItem>
<TabItem Classes="default" Header="Stroke">
<ContentControl Content="{Binding Stroke}"/>
</TabItem>
<TabItem Classes="default" Header="Fill">
<ContentControl Content="{Binding Fill}"/>
</TabItem>
<TabItem Classes="default" Header="Text">
<ContentControl Content="{Binding TextStyle}"/>
</TabItem>
</TabControl>
</UserControl> Alternative markup syntax: using local = clr Draw2D.Views.Style
UserControl {
d:DesignWidth="400"
d:DesignHeight="300"
TabControl {
Classes="default", TabStripPlacement="Top"
TabItem {
Classes="default", Header="Style"
Grid {
RowDefinitions="Auto,Auto,Auto", ColumnDefinitions="Auto,*"
TextBlock { Classes="default", Grid.Column="0", Grid.Row="0", Text="Thickness" }
TextBox { Classes="default", Grid.Column="1", Grid.Row="0", Text=${Binding Thickness} }
TextBlock { Classes="default", Grid.Column="0", Grid.Row="1", Text="IsStroked" }
CheckBox { Classes="default", Grid.Column="1", Grid.Row="1", IsChecked=${Binding IsStroked} }
TextBlock { Classes="default", Grid.Column="0", Grid.Row="2", Text="IsFilled"/>
CheckBox { Classes="default", Grid.Column="1", Grid.Row="2", IsChecked=${Binding IsFilled} }
}
}
TabItem {
Classes="default", Header="Stroke"
ContentControl { Content=${Binding Stroke} }
}
TabItem {
Classes="default" Header="Fill">
ContentControl { Content=${Binding Fill} }
}
TabItem {
Classes="default" Header="Text">
ContentControl { Content=${Binding TextStyle} }
}
}
} |
Beta Was this translation helpful? Give feedback.
-
or maybe something like this: using local = clr Draw2D.Views.Style
UserControl
d:DesignWidth="400"
d:DesignHeight="300"
TabControl
Classes="default", TabStripPlacement="Top"
TabItem
Classes="default", Header="Style"
Grid
RowDefinitions="Auto,Auto,Auto", ColumnDefinitions="Auto,*"
TextBlock => Classes="default", Grid.Column="0", Grid.Row="0", Text="Thickness"
TextBox => Classes="default", Grid.Column="1", Grid.Row="0", Text=${Binding Thickness}
TextBlock => Classes="default", Grid.Column="0", Grid.Row="1", Text="IsStroked"
CheckBox => Classes="default", Grid.Column="1", Grid.Row="1", IsChecked=${Binding IsStroked}
TextBlock => Classes="default", Grid.Column="0", Grid.Row="2", Text="IsFilled"
CheckBox => Classes="default", Grid.Column="1", Grid.Row="2", IsChecked=${Binding IsFilled}
TabItem
Classes="default", Header="Stroke"
ContentControl => Content=${Binding Stroke}
TabItem
Classes="default" Header="Fill">
ContentControl => Content=${Binding Fill}
TabItem {
Classes="default" Header="Text">
ContentControl => Content=${Binding TextStyle} |
Beta Was this translation helpful? Give feedback.
-
I've noticed a bit of confusion with
I'm not sure how to unify this syntax. Quoting strings seems more natural, but it might be inconvenient for binding paths. I think we can make quotes optional and only use them when they are actually needed, e. g.
but
That would make object-typed properties a bit more verbose, e. g.
instead of
but I guees it's justified to make everything else to be way less verbose. |
Beta Was this translation helpful? Give feedback.
-
It would be interesting to have an abstract model of the UI for MVU/React/Elm/Flutter like approaches. You can play with and edit some MVU samples online (Elm /F#) // Called each time AppState changes
IView View(AppState state)
{
new StackPanel
{
Orientation = Orientation.Vertical,
// Because this is just C#/F#.. we can just filter inline
Children = state.Data
.Where(data => state.filter data)
.Select(data => ViewData(data))
}
}
IView View(Data state)
{
new StackPanel
{
Orientation = Orientation.Horizontal,
Children = [
new TextBlock
{
Text = Data.Title
},
new Button
{
Click = code // dispatch changes to app state -> rerenders by calling view
}
]
}
} I guess diffing with the current VisualTree would be needed for performance. |
Beta Was this translation helpful? Give feedback.
-
Wow, curious concept, looks great! By the way, folks who don't like XAML syntax usually say it's too verbose, so probably worth using indentation (spaces, tabs, etc.) as logical block delimiters, like tools such as Pug or Elm do. How about something like the following elm-like example? using lib = xml "http://some.lib/xml/namespace"
using x = xml "http://schemas.microsoft.com/winfx/2006/xaml"
using collections = clr System.Collections.Generic
using local = clr Full.Path.To.Namespace
using system = clr System
Window
StackPanel
Button
Orientation = "Horizontal"
Attached.Property = "Value"
Command = {Binding ViewModel.Clicked}
"Hello, Avalonia!"
-- A control with List<int> as its content.
local:MyControl
collections:List<system:Int32> { 1 2 3 }
-- Bound controls example.
TextBox x:Name = "ExampleTextBlock"
TextBlock Text = {Binding Text, Mode=OneWay, Source=ExampleTextBlock}
TextBlock
"Example text"
LineBreak
Run Background = "Red" "Red Text!"
LineBreak
"More plain text in the end"
Styles
StackPanel > is:(Control).someClass:pointerover
Margin = "0 0 1 4"
Button
Template
ContentPresenter
Name = "PART_ContentPresenter"
Background = {TemplateBinding Background}
TextBlock.Foreground = {TemplateBinding Foreground} It'd feel much nicer if an IDE highlighted nesting depth, like IntelliJ IDEA does. |
Beta Was this translation helpful? Give feedback.
-
The cool thing about elm/f# in this regard is that you can build great DSLs with them, all in code. I'm actually playing around with building a DSL for Avalonia called FuncUI (WIP). This is valid F# code let buttonView = button {
background Brushes.Azure
foreground Brushes.Brown
contentView (textblock {
text "some text"
})
} |
Beta Was this translation helpful? Give feedback.
-
@JaggerJo The problem with DSLs is the lack of live code reloading. With XAML and any alternative markup that get parsed into the same AST we can just reload the code at runtime which makes previewer useful. While it would be really nice to have something React-like, it would be a different project, not something we can implement with XamlIl. |
Beta Was this translation helpful? Give feedback.
-
@kekekeks ahh, missed the XAMLIL part in the description. Thought this is a general "alternative markup syntax" discussion. |
Beta Was this translation helpful? Give feedback.
-
I prefer the XAML syntax. |
Beta Was this translation helpful? Give feedback.
-
Implementing QML as-is in Avalonia should be hard enough if i understand correctly |
Beta Was this translation helpful? Give feedback.
-
It would be interesting to implement nice support for object initializing clause https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/how-to-initialize-objects-by-using-an-object-initializer Based on this it should be relatively easy to implement custom AOT DSLs for Avalonia, or to not use them at all. |
Beta Was this translation helpful? Give feedback.
-
We won't depend on 3rd party JS engine, everything has to be done in managed code with as few dependencies as there possibly can be. Not sure how Qt does that, they might be somehow recording getter calls like MobX does. |
Beta Was this translation helpful? Give feedback.
-
What we could do is some custom language suitable for defining expressions. Such expressions could be compiled into a MultiBinding with a customized value converter |
Beta Was this translation helpful? Give feedback.
-
I think it is theoretically feasible to convert some scripting language (such as JS / TS) into XAML or MSIL Qt commercial version has a tool: QML TO CPP, this tool allows QML to run in the MCU(such as stm32) |
Beta Was this translation helpful? Give feedback.
-
The "Blazor" syntax is a really cool concept too. microsoft/microsoft-ui-xaml#2499 "Proposal: Support Blazor Syntax" The link to the video where some ideas were presented today by the Uno Platform is below (time 56:14) Yes, I'm seeding this all over the place because it's awesome! |
Beta Was this translation helpful? Give feedback.
-
Another option here would be to use a syntax based on KDL. It's XML-like but still much cleaner. We could build a flavor on top of it that would give a UX similar to the example in the OP on this issue. |
Beta Was this translation helpful? Give feedback.
-
There is such thing like AmmyUI. It is JSON like language that are compiled to XAML. There was a sick hot reload implementation(current WPF/Xamarin not even close) and bunch of improvements like mixings, aliases, variables. I was experimenting with it ages ago and it was very promising. But looks like it is dead, but syntax was pretty good. Here is a demo: https://player.vimeo.com/video/198873582 |
Beta Was this translation helpful? Give feedback.
-
Hi @robloo, hi @maxkatz6, You can find my fork at https://github.com/warappa/BlazorBindings.Maui/tree/avalonia-11. There is a simple HelloWorld project that you can run and play with. Highlights
Todo
The code is still messy and some controls are missing/not hand-tuned, but it works pretty well already. Sample App PreviewThe sample Hello World app looks like this: Code:@using BlazorBindings.AvaloniaBindings.Elements
@using System.Collections.ObjectModel;
@using BlazorBindings.AvaloniaBindings.Elements.Shapes
@using Microsoft.AspNetCore.Components.Rendering;
@using global::Avalonia.Layout
@using global::Avalonia
@using global::Avalonia.Media;
@using global::Avalonia.Media.Imaging;
<Window Topmost="true" Width="600" Height="500">
<StackPanel HorizontalAlignment="HorizontalAlignment.Center"
VerticalAlignment="VerticalAlignment.Center">
<TextBlock Text="Hello World"></TextBlock>
@if (buttonVisible == true)
{
<Button OnClick="OnCounterClicked">Click me</Button>
}
<TextBlock Text="@ButtonText"></TextBlock>
<CheckBox @bind-IsChecked="buttonVisible">Button visible</CheckBox>
<ListBox ItemsSource="Items">
<ItemTemplate>
<StackPanel>
<TextBlock Text="Content:"></TextBlock>
<TextBlock Text="@context"></TextBlock>
</StackPanel>
</ItemTemplate>
<ItemsPanel>
<StackPanel Orientation="Orientation.Horizontal"></StackPanel>
</ItemsPanel>
</ListBox>
<StackPanel Orientation="Orientation.Horizontal" Margin="new Thickness(0,20,0,0)">
<TextBlock Text="Ellipse size: "></TextBlock>
<TextBlock Text="@slider.ToString()"></TextBlock>
</StackPanel>
<Slider @bind-Value="slider"></Slider>
<Ellipse Width="slider" Height="slider/2" Fill="Brushes.Red"></Ellipse>
</StackPanel>
</Window>
@code {
double slider = 50;
int count = 0;
bool? buttonVisible { get; set; } = true;
ObservableCollection<string> Items { get; set; } = new ObservableCollection<string>(new[]
{
"a",
"b",
"c"
});
void ToggleButton()
{
buttonVisible = !buttonVisible;
}
string ButtonText => count switch
{
0 => "Click me",
1 => $"Clicked 1 time",
_ => $"Clicked {count} times"
};
void OnCounterClicked()
{
count++;
}
protected override void OnInitialized()
{
base.OnInitialized();
}
} Please check it out! |
Beta Was this translation helpful? Give feedback.
-
It looks cool. Is threre any progress on this proposal? |
Beta Was this translation helpful? Give feedback.
-
Check the repo he linked. There's some mild activity |
Beta Was this translation helpful? Give feedback.
-
Who linked the repo? kekekeks? I can't find any links |
Beta Was this translation helpful? Give feedback.
-
@warappa in his comment about blazor bindings. I believe that's the only active proposal at the moment |
Beta Was this translation helpful? Give feedback.
-
Any update about this feature? |
Beta Was this translation helpful? Give feedback.
-
@warappa 's latest version seems to be this: https://github.com/warappa/BlazorBindings.AvaloniaBindings/tree/todos/remove-maui. I tried bumping to Avalonia 11.1.4 but this breaks the razor language server for some reason. |
Beta Was this translation helpful? Give feedback.
-
Since the very first thing XamlIl does is conversion of XML to its own AST, we can now create other markup languages relatively cheaply by simply writing a parser that produces the same AST.
Since some people seem to hate XML, here is a non-XML markup proposal:
XAML for comparison:
Beta Was this translation helpful? Give feedback.
All reactions