BindableProps 1.3.4

.NET Standard 2.0
dotnet add package BindableProps --version 1.3.4
NuGet\Install-Package BindableProps -Version 1.3.4
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="BindableProps" Version="1.3.4" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add BindableProps --version 1.3.4
#r "nuget: BindableProps, 1.3.4"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install BindableProps as a Cake Addin
#addin nuget:?package=BindableProps&version=1.3.4

// Install BindableProps as a Cake Tool
#tool nuget:?package=BindableProps&version=1.3.4

BindableProps

NuGet Build Status

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

image-20220704231041505

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
.NET net5.0 net5.0-windows net6.0 net6.0-android net6.0-ios net6.0-maccatalyst net6.0-macos net6.0-tvos net6.0-windows net7.0 net7.0-android net7.0-ios net7.0-maccatalyst net7.0-macos net7.0-tvos net7.0-windows
.NET Core netcoreapp2.0 netcoreapp2.1 netcoreapp2.2 netcoreapp3.0 netcoreapp3.1
.NET Standard netstandard2.0 netstandard2.1
.NET Framework net461 net462 net463 net47 net471 net472 net48 net481
MonoAndroid monoandroid
MonoMac monomac
MonoTouch monotouch
Tizen tizen40 tizen60
Xamarin.iOS xamarinios
Xamarin.Mac xamarinmac
Xamarin.TVOS xamarintvos
Xamarin.WatchOS xamarinwatchos
Compatible target framework(s)
Additional computed target framework(s)
Learn more about Target Frameworks and .NET Standard.
  • .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.3.4 172 1/8/2023
1.3.3 144 1/8/2023
1.3.2 143 1/7/2023
1.3.1 139 1/7/2023
1.3.0 143 1/6/2023
1.2.1 140 1/5/2023
1.2.0 318 7/10/2022
1.2.0-beta 74 7/10/2022
1.1.9 293 7/10/2022
1.1.8 288 7/10/2022
1.1.8-beta 94 7/9/2022
1.1.7-beta 75 7/8/2022
1.1.3-beta 66 7/8/2022
1.1.2-beta 69 7/8/2022
1.1.1-beta 74 7/8/2022
1.1.0-beta 73 7/7/2022
1.0.7-beta 90 7/5/2022
1.0.6-beta 72 7/4/2022
1.0.4-beta 87 7/3/2022
1.0.3-beta 89 7/3/2022
1.0.2-beta 91 7/3/2022
1.0.1-beta 81 7/1/2022
1.0.0-beta 76 7/1/2022