BindableProps 1.2.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package BindableProps --version 1.2.0
NuGet\Install-Package BindableProps -Version 1.2.0
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.2.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add BindableProps --version 1.2.0
#r "nuget: BindableProps, 1.2.0"
#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.2.0

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

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.

BindableProp - Basic Usage

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

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

The BindableProp along is just not enough for covering all use-cases of BindableProperty. Planning features:

Attribute Equivalent/Description Status
BindableAttachedProp BindableProperty.CreateAttached
BindableAttachedReadOnlyProp BindablePropertyKey.CreateAttachedReadOnly
BindableReadOnlyProp BindablePropertyKey.CreateReadOnly
AllBindableProps Put this to your class,<br />Default BindableProp to all field members 👌
IgnoredProp AllBindableProps should ignore this field 👌
Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
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.10 134 2/12/2024
1.3.9 287 8/19/2023
1.3.8 196 6/24/2023
1.3.7 248 4/17/2023
1.3.6 158 4/16/2023
1.3.5 155 4/11/2023
1.3.5-beta 108 4/11/2023
1.3.4 304 1/8/2023
1.3.3 271 1/8/2023
1.3.2 268 1/7/2023
1.3.1 267 1/7/2023
1.3.0 277 1/6/2023
1.2.1 264 1/5/2023
1.2.0 430 7/10/2022
1.2.0-beta 134 7/10/2022
1.1.9 404 7/10/2022
1.1.8 401 7/10/2022
1.1.8-beta 155 7/9/2022
1.1.7-beta 136 7/8/2022
1.1.3-beta 129 7/8/2022
1.1.2-beta 133 7/8/2022
1.1.1-beta 140 7/8/2022
1.1.0-beta 138 7/7/2022
1.0.7-beta 152 7/5/2022
1.0.6-beta 136 7/4/2022
1.0.4-beta 151 7/3/2022
1.0.3-beta 139 7/3/2022
1.0.2-beta 143 7/3/2022
1.0.1-beta 146 7/1/2022
1.0.0-beta 140 7/1/2022