microsoft / microsoft-ui-xaml

Windows UI Library: the latest Windows 10 native controls and Fluent styles for your applications
MIT License
6.16k stars 667 forks source link

Discussion: X# / Xaml Sharp / Xaml Code - inspired by SwiftUI #804

Closed mdtauk closed 11 months ago

mdtauk commented 5 years ago

Apple announced SwiftUI which replaces an older and much more verbose method of building UI for their iOS, iPadOS and macOS platforms.

image

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)

using xaml;  

xaml page  
{  
      this.backgroundColor = themeResource(ApplicationPageBackgroundColor);  
      this.foregroundColor = themeResource(ApplicationTextForegroundColor);  

      xaml grid[name "rootGrid", rows "*, *, *, Auto, 12epx"]  
      {  
            xaml stackPanel[name "toolbar", orientation Horizontal]  
            {}  
            xaml button[grid.row "4" content xaml bind(_appIcon)];  
      }  
}  

^ 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

mdtauk commented 4 years ago

@mdtauk good point, I get hyper-focused sometimes on the .NET side of things from the Toolkit.

Though I think that also shows where XAML itself can shine as it's a well understood and powerful abstraction that doesn't need to tie-itself to the language driving it. 😉

Its mainly the verbosity of XAML, as well as the rigid syntax, and no separation of Styling from UI Layout - that makes it awkward to work with.

VincentH-Net commented 4 years ago

@michael-hawker

Having worked with various code based UI frameworks in the past in various languages, the power of XAML is really the ease in which one can copy and paste things around in the XML structure without having to re-work anything. With code, there's a lot of other syntax that gets in the way when trying to move things around, especially when code is needed to direct the relations of UI elements to one another as well.

Could you give/point to some example XAML and indicate a part that would be harder to move around if it were coded UI? I'm wondering if your experience is due to imperative coded UI versus declarative (like CSharpForMarkup). I'd like to try out an example and improve CSharpForMarkup if possible.

Odonno commented 4 years ago

@VincentH-Net I really like the approach you take with CSharpForMarkup.

I am currently working on a huge new version of ReduxSimple, with simplified reducers, sub-reducers, rewritten selectors, effects and routing.

I would like to use your library in the samples of ReduxSimple because it can be a perfect match to combine a Redux architecture and a Functional UI framework. The thing is that I started my first sample app with UWP (before WPF and Xamarin) because I needed to start somewhere obviously. :) Do you think CSharpForMarkup can work with UWP?

VincentH-Net commented 4 years ago

@Odonno Glad to hear you like it!

Do you think CSharpForMarkup can work with UWP?

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 :-) Simply copy XamarinFormsMarkupExtensions.cs to UwpMarkupExtensions.cs and refactor it to UWP.

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 :-)

Odonno commented 4 years ago

@VincentH-Net Good to know. I'll give a look at it once the 3.0 of ReduxSimple is finished.

charlesroddie commented 4 years ago

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:

surfsky commented 4 years ago

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:

SliderBar { Id=“sld” Min=0 Max=100}
TextBlock { Text: sld.Value }

The grammar of QML is well refined, just liking poem. QML has several advantages:

  1. Syntax like JSON
  2. Concise binding syntax
  3. Concise property syntax
  4. Signal and slot
  5. ...

The first 3 items can be adopted. By the way, the file extension may be ".csml", It means "csharp markup language". :)

gulshan commented 4 years ago

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#.

VincentH-Net commented 4 years ago

@gulshan said:

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#.

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. It is currently in the 4.6 prerelease NuGet. The documentation will go live when 4.6 goes stable; for now see the merged Xamarin Forms PR for full documentation.

@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.

gulshan commented 4 years ago

@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#.

VincentH-Net commented 4 years ago

@gulshan said

I followed the PR and I really admire what you did. ... We cannot put a loop or a conditional statement within a collection initializer in C#

Thanks for the compliment!

Actually you can do this because #CSharpForMarkup has built-in helpers to mix-in logic with declarative markup: .Invoke() and .Assign(). E.g. above could be:

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 StackLayout or takes the StackLayout.Children as parameter.

Or, if you want to support hot reload optimally, you could .Assign (out peopleStack) and at the end of the Build() method call a void BuildPeopleStack() method that sets peopleStack.Children

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 :-)

gulshan commented 4 years ago

@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-

freeze_header

I don't know if this is possible in C#. I think it will be something really nice to have in C#.

luismts commented 4 years ago

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.

weitzhandler commented 4 years ago

@luismts I shall say hot reload + hot restart.

JaggerJo commented 4 years ago

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)
                ]
            ]
        ]    

NewApp_screenshot

asklar commented 4 years ago

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;
    }/>

https://github.com/asklar/CSX

mdtauk commented 4 years ago

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 commented 4 years ago

See this spec for the next version of C# Markup in .NET MAUI (top: MVU, bottom: MVVM): image

Odonno commented 4 years ago

@VincentH-Net That looks really interesting.

sonnemaf commented 3 years ago

I have created an proof of concept for a MVU pattern in UWP. Maybe this can extend this discussion.

using MvuTest.Controls;
using System;
using Windows.UI;
using Windows.UI.Popups;
using Windows.UI.Xaml;

namespace MvuTest {

    public class MainPage : MvuPage {

        private TextBlock2 _tb;

        private int _count;

        protected override IUIElement2 Build() =>
            this.StackPanel(
                this.ToggleButton("Toggle it")
                    .Width(200)
                    .FontSize(30),
                this.Button("Increment", (sender, e) => {
                    _tb.Text($"Count {++_count}");
                }).HorizontalAlignment(HorizontalAlignment.Center),
                (_tb = this.TextBlock("Count 0"))
                    .FontSize(20)
                    .Foreground(Colors.Red),
                this.TextBox("Fons Sonnemans")
                    .Header("Name")
            ).Spacing(12)
             .HorizontalAlignment(HorizontalAlignment.Center)
             .VerticalAlignment(VerticalAlignment.Center);
    }

}
mdtauk commented 3 years ago

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?

sonnemaf commented 3 years ago

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.

mdtauk commented 3 years ago

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

Happypig375 commented 3 years ago

As I said, just use C#. C# provides a concise way to specify views already.

RichiCoder1 commented 3 years ago

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: https://github.com/dotnet/maui/discussions/119#discussioncomment-59504

VincentH-Net commented 3 years ago

@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): image

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)

mdtauk commented 3 years ago

@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.

github-actions[bot] commented 11 months ago

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.