BindableProps 1.3.0
See the version list below for details.
dotnet add package BindableProps --version 1.3.0
NuGet\Install-Package BindableProps -Version 1.3.0
<PackageReference Include="BindableProps" Version="1.3.0" />
paket add BindableProps --version 1.3.0
#r "nuget: BindableProps, 1.3.0"
// Install BindableProps as a Cake Addin #addin nuget:?package=BindableProps&version=1.3.0 // Install BindableProps as a Cake Tool #tool nuget:?package=BindableProps&version=1.3.0
BindableProps
I spend hours to save your moments.
This library helps you to reduce writing boilerplate code when creating your custom UI components.
[TOC]
BindableProp
- Basic Usage
<a href="#top">Back to Top :arrow_up:</a>
Let say you want to create your own text input. Here's how it looks:
namespace MyMauiApp.Controls;
public class TextInput : ContentView
{
public string Text
{
get => (string)GetValue(TextInput.TextProperty);
set => SetValue(TextInput.TextProperty, value);
}
public static readonly BindableProperty TextProperty = BindableProperty.Create(
nameof(Text), typeof(string), typeof(TextInput), string.Empty
);
public string PlaceHolder
{
get => (string)GetValue(TextInput.PlaceHolderProperty);
set => SetValue(TextInput.PlaceHolderProperty, value);
}
public static readonly BindableProperty PlaceHolderProperty = BindableProperty.Create(
nameof(PlaceHolder), typeof(string), typeof(TextInput), string.Empty
);
public TextInput()
{
// Implement your logic
}
}
With BindableProps
, your code will become like this:
using BindableProps;
namespace MyMauiApp.Controls;
// Notice: Your class must be partial class
public partial class TextInput : ContentView
{
[BindableProp]
string text;
[BindableProp]
string placeHolder;
public TextInput()
{
// This piece is same as above
}
}
The real magic happens at Solution Explorer > Dependencies > Analyzers > BindablePropsSG
What you would see in TextInput.g.cs
is the boilerplate code which you had to write. I'll write them for you!
using BindableProps;
namespace MyMauiApp.Controls
{
public partial class TextInput
{
public static readonly BindableProperty TextProperty = BindableProperty.Create(
nameof(Text),
typeof(string),
typeof(TextInput),
default,
(BindingMode)0,
null,
(bindable, oldValue, newValue) =>
((TextInput)bindable).Text = (string)newValue,
null,
null,
null
);
public string Text
{
get => text;
set
{
OnPropertyChanging(nameof(Text));
text = value;
SetValue(TextInput.TextProperty, text);
OnPropertyChanged(nameof(Text));
}
}
public static readonly BindableProperty PlaceHolderProperty = BindableProperty.Create(
nameof(PlaceHolder),
typeof(string),
typeof(TextInput),
default,
(BindingMode)0,
null,
(bindable, oldValue, newValue) =>
((TextInput)bindable).PlaceHolder = (string)newValue,
null,
null,
null
);
public string PlaceHolder
{
get => placeHolder;
set
{
OnPropertyChanging(nameof(PlaceHolder));
placeHolder = value;
SetValue(TextInput.PlaceHolderProperty, placeHolder);
OnPropertyChanged(nameof(PlaceHolder));
}
}
}
}
The above example is the minimal amount of code to work. Here is the complete features:
public partial class TextInput : ContentView
{
// Create prop with a few settings
[BindableProp(DefaultBindingMode = ((int)BindingMode.TwoWay))]
string text = "From every time";
// Full setting
[BindableProp(
DefaultBindingMode = ((int)BindingMode.OneWay),
ValidateValueDelegate = nameof(ValidateValue),
PropertyChangedDelegate = nameof(PropertyChangedDelegate),
PropertyChangingDelegate = nameof(PropertyChangingDelegate),
CoerceValueDelegate = nameof(CoerceValueDelegate),
CreateDefaultValueDelegate = nameof(CreateDefaultValueDelegate)
)]
string placeHolder = "Always!";
static bool ValidateValue(BindableObject bindable, object value)
{
return true;
}
static void PropertyChangedDelegate(BindableObject bindable, object oldValue, object newValue)
{
// Do something
}
static void PropertyChangingDelegate(BindableObject bindable, object oldValue, object newValue)
{
// Do something
}
static object CoerceValueDelegate(BindableObject bindable, object value)
{
// Do something
return 0;
}
static object CreateDefaultValueDelegate(BindableObject bindable)
{
// Do something
return string.Empty;
}
}
And the corresponding result would like:
public partial class TextInput
{
public static readonly BindableProperty TextProperty = BindableProperty.Create(
nameof(Text),
typeof(string),
typeof(TextInput),
"From every time",
(BindingMode)((int)BindingMode.TwoWay),
null,
(bindable, oldValue, newValue) =>
((TextInput)bindable).Text = (string)newValue,
null,
null,
null
);
public string Text
{
get => text;
set
{
OnPropertyChanging(nameof(Text));
text = value;
SetValue(TextInput.TextProperty, text);
OnPropertyChanged(nameof(Text));
}
}
public static readonly BindableProperty PlaceHolderProperty = BindableProperty.Create(
nameof(PlaceHolder),
typeof(string),
typeof(TextInput),
"Always!",
(BindingMode)((int)BindingMode.OneWay),
ValidateValue,
PropertyChangedDelegate,
PropertyChangingDelegate,
CoerceValueDelegate,
CreateDefaultValueDelegate
);
public string PlaceHolder
{
get => placeHolder;
set
{
OnPropertyChanging(nameof(PlaceHolder));
placeHolder = value;
SetValue(TextInput.PlaceHolderProperty, placeHolder);
OnPropertyChanged(nameof(PlaceHolder));
}
}
}
Finally, you can use your component in other page/view like a normal component. For example, at MainPage.xaml
:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm="clr-namespace:MyMauiApp.ViewModels"
xmlns:controls="clr-namespace:MyMauiApp.Controls"
x:Class="MyMauiApp.MainPage"
x:DataType="vm:MainPageViewModel">
<controls:TextInput PlaceHolder="Say you do"
Text="{Binding MyLoveStory, Mode=TwoWay}" />
</ContentPage>
AllBindableProps
and IgnoredProp
<a href="#top">Back to Top :arrow_up:</a>
If you just need the default setting for all of your props, try this:
[AllBindableProps]
public partial class TextInput : ContentView
{
// Default field
string text;
// Support field with a default value
string placeHolder = "Do you trust me?";
// This field will be handled by BindableProp
[BindableProp(
DefaultBindingMode = (int)BindingMode.TwoWay,
ValidateValueDelegate = nameof(ValidateValue)
)]
string message = "With every cell in my body!";
[IgnoredProp]
bool isBusy; // Don't touch!
// If you have existing props, we don't touch them
public static readonly BindableProperty ErrorProperty = BindableProperty.Create(
nameof(Error),
typeof(string),
typeof(TextInput),
"Things just get out of hand",
(BindingMode)(int)BindingMode.OneWayToSource
);
// Also not touch this prop
public string Error
{
get => (string)GetValue(TextInput.ErrorProperty);
set
{
SetValue(TextInput.ErrorProperty, value);
}
}
static bool ValidateValue(BindableObject bindable, object value)
{
return true;
}
public TextInput()
{
InitializeComponent();
}
}
And the result is:
namespace WibuTube.Controls
{
public partial class TextInput
{
public static readonly BindableProperty TextProperty = BindableProperty.Create(
nameof(Text),
typeof(string),
typeof(TextInput),
default,
propertyChanged: (bindable, oldValue, newValue) =>
((TextInput)bindable).Text = (string)newValue
);
public string Text
{
get => text;
set
{
OnPropertyChanging(nameof(Text));
text = value;
SetValue(TextInput.TextProperty, text);
OnPropertyChanged(nameof(Text));
}
}
public static readonly BindableProperty PlaceHolderProperty = BindableProperty.Create(
nameof(PlaceHolder),
typeof(string),
typeof(TextInput),
"Do you trust me?",
propertyChanged: (bindable, oldValue, newValue) =>
((TextInput)bindable).PlaceHolder = (string)newValue
);
public string PlaceHolder
{
get => placeHolder;
set
{
OnPropertyChanging(nameof(PlaceHolder));
placeHolder = value;
SetValue(TextInput.PlaceHolderProperty, placeHolder);
OnPropertyChanged(nameof(PlaceHolder));
}
}
}
}
Roadmap
<a href="#top">Back to Top :arrow_up:</a>
The BindableProp
along is just not enough for covering all use-cases of BindableProperty
. Planning features:
Attribute | Equivalent/Description | Status |
---|---|---|
BindableAttachedProp |
BindableProperty.CreateAttached |
👌 |
AllBindableProps |
Put this to your class,<br />Default BindableProp to all field members |
👌 |
IgnoredProp |
AllBindableProps should ignore this field |
👌 |
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
1.4.3 | 293 | 5/13/2024 |
1.4.2 | 89 | 5/13/2024 |
1.4.1 | 86 | 5/11/2024 |
1.4.0 | 136 | 5/4/2024 |
1.3.10 | 275 | 2/12/2024 |
1.3.9 | 377 | 8/19/2023 |
1.3.8 | 230 | 6/24/2023 |
1.3.7 | 287 | 4/17/2023 |
1.3.6 | 181 | 4/16/2023 |
1.3.5 | 186 | 4/11/2023 |
1.3.5-beta | 133 | 4/11/2023 |
1.3.4 | 336 | 1/8/2023 |
1.3.3 | 305 | 1/8/2023 |
1.3.2 | 299 | 1/7/2023 |
1.3.1 | 296 | 1/7/2023 |
1.3.0 | 305 | 1/6/2023 |
1.2.1 | 300 | 1/5/2023 |
1.2.0 | 461 | 7/10/2022 |
1.2.0-beta | 159 | 7/10/2022 |
1.1.9 | 434 | 7/10/2022 |
1.1.8 | 431 | 7/10/2022 |
1.1.8-beta | 180 | 7/9/2022 |
1.1.7-beta | 164 | 7/8/2022 |
1.1.3-beta | 156 | 7/8/2022 |
1.1.2-beta | 156 | 7/8/2022 |
1.1.1-beta | 167 | 7/8/2022 |
1.1.0-beta | 164 | 7/7/2022 |
1.0.7-beta | 177 | 7/5/2022 |
1.0.6-beta | 145 | 7/4/2022 |
1.0.4-beta | 160 | 7/3/2022 |
1.0.3-beta | 165 | 7/3/2022 |
1.0.2-beta | 169 | 7/3/2022 |
1.0.1-beta | 173 | 7/1/2022 |
1.0.0-beta | 163 | 7/1/2022 |