Awesome
Overview
Sharp.UI is a library for the .NET Multi-platform App User Interface (MAUI) framework that enables you to build user interfaces declaratively in C# code using fluent methods. With Sharp.UI, you can create interfaces without needing to use XAML. Additionally, the library includes hot reload support to make the development process faster and more efficient. The hot reload feature is supported in Visual Studio Code and Visual Studio 2022 using the HotReloadKit library.
<a href="https://youtu.be/wxQr0p3lEg0" target="_blank"> <img src="https://github.com/idexus/Sharp.UI/raw/main/doc/assets/ytscreen2.jpg" alt="Hot Reload Support" width="640" border="0" /> </a>Hello, World! Example
Here is an example of how you could create a simple "Hello, World!" page in Sharp.UI:
namespace ExampleApp;
using Sharp.UI;
public partial class HelloWorldPage : ContentPage
{
int count = 0;
public HelloWorldPage()
{
Content =
new VStack(e => e
.Spacing(25)
.Padding(30, 0)
.CenterVertically())
{
new Image("dotnet_bot.png", out var image)
.HeightRequest(280)
.CenterHorizontally(),
new Label("Welcome to .NET Multi-platform App UI")
.FontSize(e => e.OnPhone(16).Default(30))
.CenterHorizontally(),
new Button("Click me")
.FontSize(20)
.CenterHorizontally()
.OnClicked(button =>
{
count++;
button.Text = $"Clicked {count} ";
button.Text += count == 1 ? "time" : "times";
})
};
}
}
Using Sharp.UI
Nuget Package
To add Sharp.UI to your project, along with all its functionality, you can use:
Repository
This project uses submodules, which means that it depends on other external projects to function properly. To ensure that these dependencies are properly included, you'll need to initialize the submodules when you first clone the repository.
To do this, use the following command:
git submodule update --init --recursive
If you ever update your clone of the repository, you may need to update the submodules as well to ensure that you have the latest version of all dependencies. To do this, you can use the following command:
git submodule update --recursive
Project Reference
You can also add the library to your project by adding a project reference to the Sharp.UI library. For more information, see the Adding the Library by VS Project Reference document.
In Your Project
To use Sharp.UI in your projects, you need to include the using Sharp.UI
statement inside your app namespace.
namespace ExampleApp;
using Sharp.UI;
Or:
namespace ExampleApp
{
using Sharp.UI;
...
}
Sharp.UI Template Project
A vanilla sample project using nuget package
https://github.com/idexus/Sharp.UI-Template
Hot Reload
The hot reload feature allows you to see changes to your UI in real-time without having to rebuild the entire application. To use hot reload in Sharp.UI, you will need to use the HotReloadKit library and add SharpUIApp<App>(HotReloadSupport.IdeIPs)
extension method in your MauiApp
builder.
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.SharpUIApp<App>(HotReloadSupport.IdeIPs) // to enable Hot Reload
.UseMauiApp<App>()
...
return builder.Build();
}
Visual Studio Code and Visual Studio 2022 extensions for both Windows and Mac are available for download from the HotReloadKit releases page. Please note that this is a proof of concept and there is no official support. Use it at your own risk.
Examples
Here are some examples showing how to use the Sharp.UI library
Properties and Fluent Methods
Sharp.UI provides a convenient way to set properties for UI elements by matching properties with fluent helper methods. This makes it easier and more readable to define the interface of your application.
Here is an example of using fluent methods to set properties on a Label
:
new Label()
.Text("This is a test")
.Padding(20)
.FontSize(30)
Additionally
- Some common properties can be set directly as constructor arguments for even faster definition of the interface. Here is an example using a constructor argument to set the text property on a
Label
:
new Label("This is a test")
- All classes that implement the
ITextAlignment
interface get additional methods, so you can write e.g.:
new Label().TextCenter()
new Entry().TextBottomStart()
- You can layout every view in their container using the helper extension methods
new VStack
{
new Label("Hello, World!").CenterHorizontally()
}
Inline bindable property configuration
In C# user interfaces, it's often useful to configure the properties of UI elements inline, instead of setting them directly in XAML or code-behind.
Property binding
One way to configure a bindable property inline is to use the Path
method and additional extension methods to bind the property to a data source. For example:
new Label().FontSize(e => e.Path("MyFontSize"))
new Label().Text(e => e.Path("Value").Source(slider).StringFormat("Value : {0:F1}"))
Idiom, Platform, and Theme
Another way to configure bindable properties inline is to set different values for different device idiom (phone, tablet, etc.), platform (WinUI, iOS, etc.), or theme (light or dark). For example:
new Label().FontSize(e => e.OnPhone(30).OnTablet(50).Default(40))
new Label().FontSize(e => e.OnWinUI(30).OniOS(50).Default(40))
new Label().TextColor(e => e.OnLight(Colors.Black).OnDark(Colors.White))
Dynamic resources
Another way to configure bindable properties inline is to use dynamic resources. Dynamic resources are resources whose values can change at runtime. For example:
Resources = new ResourceDictionary
{
{ "myColor", Colors.Yellow },
...
}
Label().TextColor(e => e.DynamicResource("myColor"))
Mixing
Finally, it's also possible to mix these various configuration options to achieve more complex property configurations. For example:
new Label()
.TextColor(e => e
.OnLight(e => e.OnWinUI(Colors.Aqua).Default(Colors.LightCoral))
.OnDark(Colors.Black)
)
How to assign object references
There are two main ways to assign objects in Sharp.UI:
new Label(out label)
Or:
new Label().Assign(out label)
Using fluent methods for styling
Sharp.UI provides a way to define the styles of elements using the Style<T>
class and extension methods. Here's an example of how you can define the styles for a Label
and a Button
:
Resources = new ResourceDictionary
{
new Style<Label>(e => e
.FontSize(35)
.TextColor(AppColors.Gray200)
.CenterInContainer()),
new Style<Button>(e => e
.BackgroundColor(AppColors.Gray950)
.Padding(20)
.CornerRadius(10))
{
new VisualState<Button>(VisualStates.Button.Normal, e => e
.FontSize(33)
.TextColor(AppColors.Gray200)
.SizeRequest(270,110)),
new VisualState<Button>(VisualStates.Button.Disabled, e => e
.FontSize(20)
.TextColor(AppColors.Gray600)
.SizeRequest(180,80))
}
};
Animations
In Sharp.UI, you can use async methods with the naming convention Animate{PropertyName}To
to animate any double
or Color
bindable property.
For example, to animate the BackgroundColor
property, you can use the AnimateBackgroundColorTo
async method.
await border.AnimateBackgroundColorTo(Colors.Red, 500); // 500ms
Example Usage
You can use animations inside event handlers. For example, to animate a Button
when it's clicked:
new Button()
.Text("Click me")
.OnClicked(async (Button button) =>
{
count++;
button.Text = $"Clicked {count} ";
button.Text += count == 1 ? "time" : "times";
_ = button.AnimateBackgroundColorTo(count % 1 == 0 ? Colors.Red : Colors.Blue, 500);
await button.AnimateFontSizeTo(count % 1 == 0 ? Colors.Red : Colors.Blue);
await button.RotateTo(360 * (count % 2), 300);
})
You can also use visual states inside Style<T> to define animations. See the documentation on Style<T> for more information.
Auto-generated code
Sharp.UI library has a feature of automatically generating bindable properties and their fluent helper methods. To use this feature, you need to define the view-model as follows:
[BindableProperties]
public interface IViewModelProperties
{
string Title { get; set; }
string Author { get; set; }
}
[SharpObject]
public partial class ViewModel : BindableObject, IViewModelProperties
{
public void SetAuthor(Button button)
{
this.Title = "Tosca";
this.Author = "Puccini";
}
}
And, in the view, the code will be:
public class ViewPage : ContentPage
{
ViewModel viewModel => BindingContext as ViewModel;
public ViewPage(ViewModel viewModel)
{
BindingContext = viewModel;
Content = new VStack
{
new Label().Text(e => e.Path("Author"))
new Label().Text(e => e.Path("Title"))
new Button("Click Me")
.FontSize(100)
.OnClicked(viewModel.SetAuthor)
};
}
}
Other Examples
- Properties and fluent methods
- Property Bindings
- Object references assignment
- Object containers
- Layout options extension methods
- ITextAlignment interface extension methods
- Binding converters
- Attached properties
- Event handlers
- Grid definition
- Absolute layout
- Menu definition
- Gradient example
- Gesture recognizers
- Application styling
- Triggers
- Behaviors
- Application shell
- Callbacks and default values
Advanced
Disclaimer
Sharp.UI is a proof of concept. There is no official support. Use at your own risk.
License
The MIT License, Copyright (c) 2022 Pawel Krzywdzinski