-
Notifications
You must be signed in to change notification settings - Fork 674
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Discussion: X# / Xaml Sharp / Xaml Code - inspired by SwiftUI #804
Comments
Adding Ammy UI (http://www.ammyui.com/) as excellent prior art in this area. If this is something that moved forward, it'd also be great if said code or DSL made doing styles & visual state management CSS levels of easy. |
JSON is very popular at the moment, but it is so fickle and easy to break - where as XML is robust but complex and wordy. Whatever format is chosen, there should be a thought on constructing the UI on the screen decoratively, but an ability to easily manage Templates and Styles either inline or as attached files. At the moment XAML does it all, but it is bothersome to edit for designers, and also verbose for those who prefer to work in code. |
It's already possible to make WPF or WinUI UI purely in code (for example, Charles Petzold's WPF book didn't even introduce XAML at all until halfway through the book) so it might be good to use this issue to call out specific problems with doing this today (bindings and templates for example?) It also might be worth looking at Fabulous which is an existing project for F# and WPF or Xamarin.Forms that seems along the lines suggested in this topic. |
The thing that makes SwiftUI stand out is that it remains declarative in nature, similar to HTML and Markup, but is within the language and syntax of Swift. It is possible to create XAML objects, but the concept of nesting elements, and declaring the properties, bindings, templates and styles are obtuse and not very effective. |
I guess it will come down to available resources at Microsoft. For the forseeable future, I want them to first-and-foremost focus on making XAML less verbose - and there have been countless of proposals over the years how to do just that. WinUI 3.0 is currently blocking most of that work so we need to wait some more months until MS and the community can work together and give XAML a much needed update across the board. Depending on how that will go, we can re-visit your idea, Martin, and potentially look at areas outside of XAML markup to make UI design more efficient. |
@mdtauk using System;
using Windows.ApplicationModel.Activation;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
namespace NoXaml
{
static class Program { public static void Main(string[] _) => Application.Start(p => new MyApp()); }
class MyApp : Application
{
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
Window.Current.Content = MainPage;
Window.Current.Activate();
}
UIElement MainPage =>
new Grid
{
Background = new SolidColorBrush(Colors.Blue),
Children = {
new Button { Content = "Hello!" }
}
};
}
} |
Adding @VincentH-Net's CSharpForMarkup to the mix as well for reference. https://github.com/VincentH-Net/CSharpForMarkup FWIW, ATM I think any idea that circumvents a visual designer is a terrible idea but I am keeping an open mind about it. Maybe it will grow on me. 😁
Ideally, you should be able to choose between the two (or more) formats, for optimal adoption. This is the forward-thinking and .NET-like approach that @cwensley utilizes for his ETO project: https://github.com/picoe/Eto |
Avalonia team also discussed about XAML markup alternative: |
XAML markup is a advancement. These approaches are limited. .NET and XAML need to move forward and reach those platforms, not the contrary. Also give a look at #740 (comment) |
If we look carefully we see clearly the simplicity and advantages of XAML. |
@mdtauk |
There are definitely a lot of possible XAML Markup improvements the team should focus on before thinking about alternative ways to build a UI. Both on the control level (like the new grid proposal) and the XAML language itself like introducing a negation syntax for binding, i.e: To sum it up: The focus should be first-and-foremost on improving XAML markup! |
Offering the choice of building XAML UI in C#, C++, F#, VB, JSON, as well as Xaml markup itself - would allow the developer to choose what way is best for them - as long as it all renders the same. There is Xaml Direct Microsoft.UI.Xaml.Core.Direct but it is intended for middle-ware tools, is very low level, and hasn't been formalised as a first class way to build an app's UI. (New Project templates, sample code, etc) I am not averse to Xaml as Markup (but anything that results in less markup and simpler declarations would be welcome) but SwiftUI shows that there are alternatives. No idea if one is better than the other, but it is interesting to discuss the pain points, the aspirations, and to possibly learn lessons from other platform ideas :) |
I do agree with the rest that XAML improvements are of priority first and foremost. For the sake of discussion on possible alternatives how about talking with the Razor Compiler team? They seem to have a decent compiler already that can parse C# and VB (not too sure if this is still supported on newer razor) expressions in Off the top of my head it'd probably be like this? This is assuming we treat
This should be less effort in my opinion that writing a new DSL parser from scratch, but the downside is the razor compiler is biased to C# by default so adding other languages like F# might be a hassle. |
I like your thinking @RyoukoKonpaku. Unfortunately, Razor is a templating engine, and the results it generates is a plain text file (or stream) for use to emit to a browser. What we're attempting to do here is describe an object graph that ultimately gets loaded into memory and rendered from there. For instance, while you do reference a Unless, of course, you're suggesting that we use Razor to create XAML files and then from there we load in the Xaml, which would work. In that case, however, you're doubling the required components to get the view loaded, plus there are other considerations such as debugging. How do you debug a binding that was defined in a template file that created the Xaml file? Not to say that it wouldn't work. It's a neat idea, but it would be an involved undertaking, IMO. |
One point I think most of us would agree on, is that whatever emerges should be Human Readable |
I would add that I feel that if we unite and ask for the XAML Standard or a Universal XAML, it would be much better that any attempt to change or improve the XAML. About the code only approach, the best to do would be invest in F# elmish style and also gjallahorn with a WinUI XAML Type Provider. Another thing that could be done is to allow XAML literals in C#/F# code.
|
@Mike-EEE Indeed it's not that smooth sailing either. I'm actually referring to Blazor in this case which can detect C# classes as @using NameSpace.SomeControls
@inject ViewModel VM
@* SampleComponent.razor *@
<ChildComponent>
<NestedComponent />
</ChildComponent> The generated class is this. // compiled SampleComponent.g.cs
using NameSpace.SomeControls;
# alot of pragmas here since it's generated
public partial class SampleComponent : Microsoft.AspNetCore.Components.ComponentBase
{
[Inject]
protected ViewModel VM { get; set; }
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder) {
builder.OpenComponent<Namespace.ChildComponent>();
builder.OpenComponent<Namespace.NestedComponent>();
builder.CloseComponent<Namespace.NestedComponent>();
builder.CloseComponent<Namespace.ChildComponent>();
// ... removed for brevity but basically constructs the combination of
// strings (e.g. div, span etc...) and Component which are eseentially just classes
}
} So what I'd imagine is that we replace the generated builder into a series of So from the above it would compile to something like this // SampleComponent.g.cs
using NameSpace.SomeControls;
# pragmas here
public partial class SampleComponent: Microsoft.UI.Xaml.UserControl
{
[Inject]
protected ViewModel VM { get; set; }
protected Microsoft.UI.Xaml.UserControl BuildObjectGraph() {;
// Create object graph here for ChildComponent and it's nested Control.
}
} Essentially the effort needed here is the generation from razor generated AST to |
To be sure, @RyoukoKonpaku, are In either case, that does look interesting and worth an exploration, IMO. 👍 |
I think this could be discussed in https://github.com/dotnet/csharplang? |
@Mike-EEE yep You can also refer the full namespace to reference a Component just like how you'd reference a normal class. <NameSpace.SomeControls.ChildComponent>
<NameSpace.SomeControls.NestedComponent />
</NameSpace.SomeControls.ChildComponent> The AST to C# class is the bigger effort if this was to be done from what I can tell but it's definitely plausible with the tools available now, |
You might be onto something @RyoukoKonpaku. I'm taking @reli-msft's hint and moving this onto a perhaps more pertinent forum: dotnet/aspnetcore#10911 See you there. :) |
@Odonno Glad to hear you like it!
Yes, the approach I took with CSharpForMarkup for Xamarin Forms can be applied to any .NET UI framework that uses data binding. Since CSharpForMarkup is just a thin set of helper functions directly on top of the UI framework, you could effectively create a close lookalike of these helpers for UWP. Some helpers for Forms are specific but most will have a close equivalent on UWP. You're welcome to do a PR to add a UWP port on CSharpForMarkup :-) You might want to consider porting your UWP sample to UNO since that virtually is 1 on 1 compatible with UWP. Now that is a PR I'd really love to see :-) |
@VincentH-Net Good to know. I'll give a look at it once the 3.0 of ReduxSimple is finished. |
UI defined in code can achieve the following now:
For example we can already write: // in a ViewModel
let text = Mutable.create ""
let capitalizedText = text |> Signal.map (fun s -> s.ToUpper())
let add() = text.Value <- text.Value + "a"
// in a View
let label = Label()
capitalizedText.bind label.set_Text
StackLayout(
Views = [
label
Button(Text = "+A", Click = add)
]
) Useful future tooling:
type FooLabel(bar:string) =
inherit Label(Text = "foo"+bar)
[<Preview>] // only used for live preview tooling
static member Sample = FooLabel("bar") |
I like QML & Razor synax: @UserControl
@Class ControlCatalog.Pages.ButtonPage
@Using System.Drawing
StackPanel {
Orientation:"Vertical"
Spacing:"4"
TextBlock{Classes:"h1" Text:"Button"}
TextBlock{Classes:"h2" Text:"A button control"}
StackPanel{
Orientation:"Horizontal"
Margin:"0,16,0,0"
HorizontalAlignment:"Center"
Spacing:"16"
StackPanel{
Orientation:"Vertical" Spacing:"8" Width:"150"
Button{Text: "Button"}
Button{Foreground:"White" Text:"Foreground"}
Button{Background:"@ThemeAccentBrush" Text:"Background"}
Button{IsEnabled:"False" Text:"Disabled"}
Button{
Text:"Re-themed"
Style.Resources {
SolidColorBrush {ThemeBorderMidBrush:"Red"}
SolidColorBrush {ThemeControlHighBrush:"DarkRed"}
}
}
}
StackPanel{
Orientation:"Vertical" Spacing:"8" Width:"150"
Button {BorderThickness:"0" Text:"No Border"}
Button {BorderBrush:"@ThemeAccentBrush" Text:"Border Color"}
Button {BorderBrush:"@ThemeAccentBrush" BorderThickness:"4" Text:"Thick Border"}
Button {BorderBrush:"@ThemeAccentBrush" BorderThickness:"4" IsEnabled:"False" Text:"Disabled"}
}
}
} And QML binding syntax:
The grammar of QML is well refined, just liking poem. QML has several advantages:
The first 3 items can be adopted. |
I think the specialty of UI frameworks like SwiftUI and Jetpack Compose is putting logical operations like if conditions and loops while defining UI code. Swift and Kotlin has some language features, that makes this possible. This will be hard to do in C#. |
@gulshan said:
Actually, it's not that hard. #CSharpForMarkup is now built-in in Xamarin Forms as the C# Markup feature, and makes it easy to combine declarative and procedural buildup of markup. @surfsky In contrast to Razor-like approach C# Markup does not add / mix new language elements to C#, and avoids strings for data binding or property values, choosing typesafe, intellisense-supported C# features instead. However the markup structure reads similar to your example. |
@VincentH-Net I followed the PR and I really admire what you did. But what I tried say is, Jetpack compose supports code like this- Column {
people?.forEach { person ->
Padding(16.dp) {
Text(text = "${person.name} (${person.craft})")
}
}
} We cannot put a loop or a conditional statement within a collection initializer in C#. |
@gulshan said
Thanks for the compliment! Actually you can do this because #CSharpForMarkup has built-in helpers to mix-in logic with declarative markup: new StackLayout { Spacing = 16 }
.Invoke(s => people?.ForEach(person => s.Children.Add(
new Label { Text = $"{person.Name} ({person.Craft})" }
))) Or, if you want the label text to update to changes in person, you can add a calculated property and bind it like this: class Person : BaseViewModel
{
public string Name { get; set; }
public string Craft { get; set; }
public string NameAndCraft => $"{Name} ({Craft})";
}
new StackLayout { Spacing = 16 }
.Invoke(s => people?.ForEach(person => s.Children.Add(
new Label { } .Bind (nameof(Person.NameAndCraft))
))) Or, you could factor out the logic to a method that either returns a new Or, if you want to support hot reload optimally, you could Note that in Forms you would actually use a BindableLayout or ListView or Collectionview and bind to the collection, so you would not need the foreach logic anyway - but that would defeat the purpose of this discussion. So there are quite a few options to do this, both inline and in separate methods. All because the versatility of C#. No magic, new syntax or strings needed :-) |
@VincentH-Net Actually SwiftUI and Jetpack Compose both use a little bit of code generation along with built-in language syntax, specially for "Container" view (with children views), like this- I don't know if this is possible in C#. I think it will be something really nice to have in C#. |
I think @VincentH-Net the challenge now is getting C # HotReload into VS. If this is achieved, the number of people using C # projects will be massive. LiveSharp is great, and it's not expensive. But many do not know it, and it is a disadvantage. The simple fact of knowing that you have to install a third-party tool, and more being paid, leaves this option for a specific audience. |
@luismts I shall say hot reload + hot restart. |
With a language that's more suitable for DSLs the UI declaration can be just code. We don't need yet another Razor/xaml thing IMHO.. The code below is a sample of (https://github.com/AvaloniaCommunity/Avalonia.FuncUI) with F#. Might be interesting... here are templates if you want to give it a spin. module Counter =
type CounterState = {
count : int
}
let init = {
count = 0
}
type Msg =
| Increment
| Decrement
let update (msg: Msg) (state: CounterState) : CounterState =
match msg with
| Increment -> { state with count = state.count + 1 }
| Decrement -> { state with count = state.count - 1 }
let view (state: CounterState) (dispatch): IView =
DockPanel.create [
DockPanel.children [
Button.create [
Button.onClick (fun _ -> dispatch Increment)
Button.content "click to increment"
]
Button.create [
Button.onClick (fun _ -> dispatch Decrement)
Button.content "click to decrement"
]
TextBlock.create [
TextBlock.dock Dock.Top
TextBlock.text (sprintf "the count is %i" state.count)
]
]
] |
Shameless plug, check out a reactive extension to C#/XAML that I wrote for last year's hackathon, called CSX. It lets you have a syntax like JSX suck as: <StackPanel
Id="taskList"
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2">
{
return person.Tasks.Select(todo => CreateToDoItem(todo));
}
</StackPanel>
<Grid.PointerReleased Handler={
completionRateTextBlock.Opacity = 1.0;
taskList.Visibility = taskList.Visibility == Visibility.Collapsed ?
Visibility.Visible : Visibility.Collapsed;
}/> |
Xamarin is working on what it calls C# Markup https://docs.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/csharp-markup Example Grid grid = new Grid();
Label label = new Label { Text = "Code: " };
grid.Children.Add(label, 0, 1);
Entry entry = new Entry
{
Placeholder = "Enter number",
Keyboard = Keyboard.Numeric,
BackgroundColor = Color.AliceBlue,
TextColor = Color.Black,
FontSize = 15,
HeightRequest = 44,
Margin = fieldMargin
};
grid.Children.Add(entry, 0, 2);
Grid.SetColumnSpan(entry, 2);
entry.SetBinding(Entry.TextProperty, new Binding("RegistrationCode"));
Content = grid; |
@VincentH-Net That looks really interesting. |
I have created an proof of concept for a MVU pattern in UWP. Maybe this can extend this discussion.
|
What are the chances of getting C# Markup as a first class option for WinUI development? Looking at the latest Google IO and WWDC events, we now see a trend which follows on from Flutter and React Native for Declarative UI Swift and SwiftUI for macOS and iOS Kotlin and Jetpack Compose for Android Will a decision be made for WinUI to prefer C# and C# Markup - so push resources to it, feature it over Xaml in sample apps, templates, conference demos etc. This also brings up some issues. WinUI and Reunion supports C++ and various other language abstractions - so Xaml will need to remain for those other languages right? |
I have the feeling that this is not high on the priority list. I don't think that this is really bad. I love XAML. It is great, consistent and not very difficult. It is verbose though. I would love to have a C# markup solution. But I don't need it. It would be a bonus. I think that other WinUI features like input validation are more essential for its success. |
Sure it's not a priority in terms of functionality, but Microsoft is missing a shift in developers expectations, as every other platform and framework is moving this way. Meeting developers where they are this is Microsoft's current mantra |
As I said, just use C#. C# provides a concise way to specify views already. |
Compared to some of the tools mentioned above, it's not quite nearly as concise. Various libs and tools have made it better but not nearly as pleasant. This discussion item outlines it well: dotnet/maui#119 (comment) |
@mdtauk I have been championing C# Markup for all .NET UI frameworks for a couple of years now, and built a Gen 1 for Xamarin Forms (in XCT now). I pushed for proper C# hot reload which is now coming. I also have a nearly finished next-gen C# Markup version for WinUI 3 and UNO on the shelf. A small snippet (source): However, to deliver C# Markup in a way so it can compete with Flutter and SwiftUI, also some tooling is needed: proposal This requires assigning / hiring at least one high level engineer to implement and support this - for one or more .NET UI frameworks. The past 6 months I am just waiting for any of the UNO / WinUI / MAUI teams - or maybe a team that targets multiple .NET UI frameworks, like the XAML tooling team - to wake up to the outside world and come to the realization that using a single language for declarative markup and logic is not the future but the present - and has been for a while now. All arguments were already clearly discussed with involved teams (most with MAUI and UNO, WinUI 3 team not so much yet - they reacted a bit confused when I mentioned C# markup in their community calls but they indicated they had too much work and I could add it myself after they go open source). So, the waiting is for the moment when MS / UNO think this is worth at least a single engineer's time (note that MAUI MVU is also just a single person, who has other tasks as well). If they want to hire me I might even say yes ;-) It might help if many devs make their wishes known to those teams - Twitter linked to GitHub issues seem to work well in that regard (that is how I got C# Markup into Forms) |
@VincentH-Net I agree completely. It needs someone within Microsoft to decide it needs to be built into the tooling, and given priority. People adopting MAUI will be a big push, as I suspect that team will extol the virtues of a move away from XAML files. But MAUI is C#, and whilst C# Markup makes total sense there, it doesn't cover C++ development. MAUI will also be a subset of WinUI's feature set, and so will be limiting in that way. The removal of the XAML designer is another thing to hamper Xaml dev. |
This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 5 days. |
Apple announced SwiftUI which replaces an older and much more verbose method of building UI for their iOS, iPadOS and macOS platforms.
This is not something that could be done by time of WinUI 3.0 and so is not a proposal per se, but XAML being XML based, is nothing if not verbose. A lot of effort has been made in recent years to make it possible to write less Xaml to do more. VisualStateTriggers being a massive one replacing the VisualStateManager. There is also a proposal to re-work the Grid panel control, to make it more powerful with less Xaml required.
XamlDirect currently exists aimed at middleware tools which can generate XAML from code, but is there some merit into looking at what SwiftUI is doing to perhaps create an alternative to Xaml Markup, with the same power and flexibility of Xaml, but similar to React Native, providing a less verbose version of Xaml.
What it could be called is anyone's guess - for the sake of this suggestion I call it X#, XamlSharp (perhaps inspired by C#'s use of the dot property syntax)
^ Example code is not a suggested syntax, but just an illustrative example
This is a place where anyone who has an interest in simplifying Xaml can share their thoughts, ideas, syntax suggestions
The text was updated successfully, but these errors were encountered: