Sharp.UI
[删除(380066935@qq.com或微信通知)]
idexus/Sharp.UI: Allows to build a .NET Multi-platform App User Interface (.NET MAUI) declaratively in code using fluent methods (github.com)
Overview
Sharp.UI allows you to build a .NET Multi-platform App User Interface (.NET MAUI) declaratively in code using fluent methods. It is a wrapper library, mostly auto-generated.
Before you start
If you want to use this library, you must copy the GlobalUsings.cs
file, which replaces the standard MAUI classes, into your project.
...
global using Label = Sharp.UI.Label;
global using ListView = Sharp.UI.ListView;
....
....
global using ScrollView = Sharp.UI.ScrollView;
global using SearchBar = Sharp.UI.SearchBar;
global using Slider = Sharp.UI.Slider;
...
Examples
Here are some simple examples showing how to use the Sharp.UI library
Hello, World!
using Sharp.UI;
public class HelloWorldPage : ContentPage
{
int count = 0;
public HelloWorldPage()
{
Content = new VStack
{
new Image("dotnet_bot.png")
.HeightRequest(200)
.HorizontalOptions(LayoutOptions.Center),
new Label("Hello, World!")
.FontSize(32)
.HorizontalOptions(LayoutOptions.Center),
new Label("Welcome to .NET Multi-platform App UI")
.FontSize(18)
.HorizontalOptions(LayoutOptions.Center),
new Button("Click me")
.HorizontalOptions(LayoutOptions.Center)
.OnClicked(OnCounterClicked)
}
.Spacing(25)
.Padding(new Thickness(30, 0))
.VerticalOptions(LayoutOptions.Center);
}
private void OnCounterClicked(Button sender)
{
count++;
if (count == 1)
sender.Text = $"Clicked {count} time";
else
sender.Text = $"Clicked {count} times";
}
}
using Sharp.UI;
public class HelloWorldPage : ContentPage
{
int count = 0;
public HelloWorldPage()
{
Content = new VStack
{
new Image("dotnet_bot.png")
.HeightRequest(200)
.HorizontalOptions(LayoutOptions.Center),
new Label("Hello, World!")
.FontSize(32)
.HorizontalOptions(LayoutOptions.Center),
new Label("Welcome to .NET Multi-platform App UI")
.FontSize(18)
.HorizontalOptions(LayoutOptions.Center),
new Button("Click me")
.HorizontalOptions(LayoutOptions.Center)
.OnClicked(OnCounterClicked)
}
.Spacing(25)
.Padding(new Thickness(30, 0))
.VerticalOptions(LayoutOptions.Center);
}
private void OnCounterClicked(Button sender)
{
count++;
if (count == 1)
sender.Text = $"Clicked {count} time";
else
sender.Text = $"Clicked {count} times";
}
}
Properties
The properties of the MAUI classes are matched with their fluent helper methods
new Label()
.Text("This is a test")
.Padding(20)
.FontSize(30)
.HorizontalOptions(LayoutOptions.Center)
.VerticalOptions(LayoutOptions.Center)
To speed up defining the interface, some common properties are placed directly as constructor arguments
new Label("This is a test")
Property value
You can set the property value depending on the device idiom, platform, or app theme
new Label("Hello")
.FontSize(e => e.Default(50).OnDesktop(80).OnPhone(30))
.TextColor(e => e.Light(Colors.Black).Dark(Colors.Teal))
Assign
There are two ways to assign objects. Using the Assign
method
new Label()
.Assign(out label1)
or using a constructor parameter
new Label(out label1)
Containers
Sharp.UI allows you to create single and multi-element containers using braces.
Single item containers
Objects such as ScrollView
, Border
, ContentView
etc. can contain only one element.
new Scrollview
{
new VStack
{
...
}
}
Multi item containers
Objects such as VStack
, HStack
, Grid
etc. can contain many elements.
new VStack
{
new Label("Hello,")
.FontSize(40),
new Label("World!"),
new Ellipse()
.WidthRequest(100)
.HeightRequest(40)
},
Bindings
Sharp.UI allows you to create bindings using a property method parameter and fluent methods.
new Label()
.Text(e => e.Path("Author"))
.TextColor(e => e.Path("TextColor").Source(myColors))
Example of bindings between objects
using Sharp.UI;
public class SimpleBindings : ContentPage
{
public SimpleBindings()
{
this.Content = new VStack
{
new Slider(out var slider)
.Minimum(1)
.Maximum(20),
new Label()
.Text(e => e
.Path("Value")
.Source(slider)
.StringFormat("Slider value: {0}")
)
.FontSize(28)
.TextColor(Colors.Blue)
}
.VerticalOptions(LayoutOptions.Center);
}
}
Event handlers
To handle events like OnClicked
, you can write a method handler
using Sharp.UI;
public class HelloWorldPage : ContentPage
{
int count = 0;
public HelloWorldPage()
{
Content = new VStack
{
...
new Button("Click me")
.OnClicked(OnCounterClicked)
};
}
private void OnCounterClicked(Button sender)
{
count++;
...
}
}
or do it inline
new Button("Click me")
.OnClicked(button =>
{
count++;
if (count == 1)
button.Text = $"Clicked {count} time";
else
button.Text = $"Clicked {count} times";
})
Grid
Rows, columns
Using the Row()
, Column()
and Span()
methods, you can set the row, column and span within the grid definition.
new Label("Column 0, Row 2, Span 2 columns")
.Column(0)
.Row(2)
.Span(column: 2)
Row and column definition
Using folowing fluent methods you can define the number and sizes of rows and columns.
new Grid(out grid)
{
...
}
.RowDefinitions(e => e.Star(2).Star())
.ColumnDefinitions(e => e.Absolute(100).Star());
Example grid definition
new Grid(out grid)
{
new BoxView().Color(Colors.Green),
new Label("Column 0, Row 0"),
new BoxView().Color(Colors.Blue).Column(1).Row(0),
new Label("Column 1, Row 0").Column(1).Row(0),
new BoxView().Color(Colors.Teal).Column(0).Row(1),
new Label("Column 0, Row 1").Column(0).Row(1),
new BoxView().Color(Colors.Purple).Column(1).Row(1),
new Label("Column 1, Row 1").Column(1).Row(1),
}
.RowDefinitions(e => e.Star(2).Star())
.ColumnDefinitions(e => e.Absolute(200).Star());
new Grid(out grid)
{
new BoxView().Color(Colors.Green),
new Label("Column 0, Row 0"),
new BoxView().Color(Colors.Blue).Column(1).Row(0),
new Label("Column 1, Row 0").Column(1).Row(0),
new BoxView().Color(Colors.Teal).Column(0).Row(1),
new Label("Column 0, Row 1").Column(0).Row(1),
new BoxView().Color(Colors.Purple).Column(1).Row(1),
new Label("Column 1, Row 1").Column(1).Row(1),
}
.RowDefinitions(e => e.Star(2).Star())
.ColumnDefinitions(e => e.Absolute(200).Star());
Style
Using the Style<T>
class and the Set()
extension method of the BindableProperty
you can define the style of the elements
new Style<Button>
{
Button.FontSizeProperty.Set(14),
Button.CornerRadiusProperty.Set(8)
...
}
If you want to use different values depending on your app theme, device idiom, or platform, you can combine following methods.
new Style<Button>
{
Button.TextColorProperty.Set().Light(Colors.White).Dark(AppColors.Primary),
Button.BackgroundColorProperty.Set().Light(AppColors.Primary).Dark(Colors.White),
Button.FontSizeProperty.Set(14).OnDesktop(20),
Button.CornerRadiusProperty.Set(8).OniOS(15),
...
}
you can also define visual states of objects
new Style<Button>
{
...
new VisualState(VisualState.VisualElement.Normal)
{
Button.TextColorProperty.Set().Light(Colors.White).Dark(AppColors.Primary),
Button.BackgroundColorProperty.Set().Light(AppColors.Primary).Dark(Colors.White),
},
new VisualState(VisualState.VisualElement.Disabled)
{
Button.TextColorProperty.Set().Light(AppColors.Gray950).Dark(AppColors.Gray200),
Button.BackgroundColorProperty.Set().Light(AppColors.Gray200).Dark(AppColors.Gray600),
},
...
},
All defined styles can be placed in ResourceDictionary
new ResourceDictionary
{
new Style<VStack>
{
VStack.VerticalOptionsProperty.Set(LayoutOptions.Center),
VStack.HorizontalOptionsProperty.Set(LayoutOptions.Center)
...
},
new Style<Label>
{
Label.TextColorProperty.Set(Colors.Red),
Label.FontSizeProperty.Set(30.0),
Label.MarginProperty.Set(new Thickness(10,10)),
Label.HorizontalOptionsProperty.Set(LayoutOptions.Center),
...
},
...
};
Triggers
Property triggers
using Sharp.UI;
public class PropertyTriggerPage : ContentPage
{
public PropertyTriggerPage()
{
Resources = new ResourceDictionary
{
new Style<Entry>
{
Entry.BackgroundColorProperty.Set(Colors.Black),
Entry.TextColorProperty.Set(Colors.White),
new Trigger(Entry.IsFocusedProperty, true)
{
Entry.BackgroundColorProperty.Set(Colors.Yellow),
Entry.TextColorProperty.Set(Colors.Black)
},
}
};
Content = new VStack
{
new Entry("Enter name"),
new Entry("Enter password"),
new Entry("Enter address")
}
.WidthRequest(300)
.VerticalOptions(LayoutOptions.Center);
}
}
using Sharp.UI;
public class PropertyTriggerPage : ContentPage
{
public PropertyTriggerPage()
{
Resources = new ResourceDictionary
{
new Style<Entry>
{
Entry.BackgroundColorProperty.Set(Colors.Black),
Entry.TextColorProperty.Set(Colors.White),
new Trigger(Entry.IsFocusedProperty, true)
{
Entry.BackgroundColorProperty.Set(Colors.Yellow),
Entry.TextColorProperty.Set(Colors.Black)
},
}
};
Content = new VStack
{
new Entry("Enter name"),
new Entry("Enter password"),
new Entry("Enter address")
}
.WidthRequest(300)
.VerticalOptions(LayoutOptions.Center);
}
}
Data triggers
new VStack
{
new Entry("Enter text...", out var entry).Text(""),
new Button("Save")
.Triggers(new List<TriggerBase>
{
new DataTrigger<Button>(e => e.Path("Text.Length").Source(entry), 0)
{
Entry.IsEnabledProperty.Set(false)
}
})
}
new VStack
{
new Entry("Enter text...", out var entry).Text(""),
new Button("Save")
.Triggers(new List<TriggerBase>
{
new DataTrigger<Button>(e => e.Path("Text.Length").Source(entry), 0)
{
Entry.IsEnabledProperty.Set(false)
}
})
}
Event triggers
new Entry("Enter text...", out var entry).Text("")
.Triggers(new List<TriggerBase>
{
new EventTrigger("TextChanged")
{
new NumericValidationTriggerAction()
}
})
new Entry("Enter text...", out var entry).Text("")
.Triggers(new List<TriggerBase>
{
new EventTrigger("TextChanged")
{
new NumericValidationTriggerAction()
}
})
Action
public class NumericValidationTriggerAction : TriggerAction<Entry>
{
protected override void Invoke(Entry entry)
{
double result;
bool isValid = Double.TryParse(entry.Text, out result);
entry.TextColor = isValid ? Colors.Black : Colors.Red;
}
}
Definition templates
With Def<T>
you can create templates depending on device idiom, platform, or app theme
new Def<VStack>(e => e
.BackgroundColor(Colors.Red)
.Padding(20)
)
.OnDesktop(() =>
new VStack
{
new Label("Desktop version"),
new Image("dotnet_bot.png"),
})
.OnPhone(() =>
new VStack
{
new Label("This is a phone version"),
new Label("No images...")
}),
Shell
Defining your application's shell
using Sharp.UI;
public partial class App : Application
{
public App()
{
MainPage = new Shell
{
new FlyoutItem(FlyoutDisplayOptions.AsMultipleItems)
{
new Tab("Main")
{
new ShellContent<HelloWorldPage>("Hello Page"),
new ShellContent<ExamplePage>("ExamplePage"),
},
new ShellContent<GridPage>("Grid"),
...
}
}
.Resources(AppResources.Default);
}
}
License
MIT License, Copyright 2022 Pawel Krzywdzinski