{"id":13677222,"url":"https://github.com/LibraStack/UnityMvvmToolkit","last_synced_at":"2025-04-29T10:32:19.758Z","repository":{"id":60501599,"uuid":"513764769","full_name":"LibraStack/UnityMvvmToolkit","owner":"LibraStack","description":"Brings data-binding to your Unity project","archived":false,"fork":false,"pushed_at":"2024-03-27T16:54:08.000Z","size":2702,"stargazers_count":480,"open_issues_count":6,"forks_count":27,"subscribers_count":10,"default_branch":"main","last_synced_at":"2025-01-13T14:02:41.919Z","etag":null,"topics":["data-binding","mvvm-architecture","ui","unity"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LibraStack.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":null,"patreon":"DimaChebanov","open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":["https://boosty.to/dimachebanov/about"]}},"created_at":"2022-07-14T04:58:39.000Z","updated_at":"2025-01-08T06:30:01.000Z","dependencies_parsed_at":"2023-11-11T09:21:42.790Z","dependency_job_id":"16a9566b-eff8-45cc-9753-84fe2360b842","html_url":"https://github.com/LibraStack/UnityMvvmToolkit","commit_stats":null,"previous_names":["chebanovdd/unitymvvmtoolkit"],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LibraStack%2FUnityMvvmToolkit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LibraStack%2FUnityMvvmToolkit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LibraStack%2FUnityMvvmToolkit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LibraStack%2FUnityMvvmToolkit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LibraStack","download_url":"https://codeload.github.com/LibraStack/UnityMvvmToolkit/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251484066,"owners_count":21596653,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["data-binding","mvvm-architecture","ui","unity"],"created_at":"2024-08-02T13:00:39.055Z","updated_at":"2025-04-29T10:32:14.746Z","avatar_url":"https://github.com/LibraStack.png","language":"C#","funding_links":["https://patreon.com/DimaChebanov","https://boosty.to/dimachebanov/about","https://www.buymeacoffee.com/chebanovdd"],"categories":["Open Source Repositories"],"sub_categories":["Framework"],"readme":"# UnityMvvmToolkit\n\nA package that brings data-binding to your Unity project.\n\n![git-main](https://user-images.githubusercontent.com/28132516/187478087-909fc50b-778b-4827-8090-c5a66d7b6b11.png)\n\n## :open_book: Table of Contents\n\n- [About](#pencil-about)\n  - [Samples](#samples)\n- [Folder Structure](#cactus-folder-structure)\n- [Installation](#gear-installation)\n  - [IL2CPP restriction](#il2cpp-restriction)\n- [Introduction](#ledger-introduction)\n  - [IBindingContext](#ibindingcontext)\n  - [CanvasView](#canvasviewtbindingcontext)\n  - [DocumentView](#documentviewtbindingcontext)\n  - [Property](#propertyt--readonlypropertyt)\n  - [Command](#command--commandt)\n  - [AsyncCommand](#asynccommand--asynccommandt)\n  - [PropertyValueConverter](#propertyvalueconvertertsourcetype-ttargettype)\n  - [ParameterValueConverter](#parametervalueconverterttargettype)\n- [Quick start](#watch-quick-start)\n- [How To Use](#joystick-how-to-use)\n  - [Data-binding](#data-binding)\n  - [Create custom control](#create-custom-control)\n  - [Source code generator](#source-code-generator)\n- [External Assets](#link-external-assets)\n  - [UniTask](#unitask)\n- [Performance](#rocket-performance)\n  - [Memory allocation](#memory-allocation)\n- [Contributing](#bookmark_tabs-contributing)\n  - [Discussions](#discussions)\n  - [Report a bug](#report-a-bug)\n  - [Request a feature](#request-a-feature)\n  - [Show your support](#show-your-support)\n- [License](#balance_scale-license)\n\n## :pencil: About\n\nThe **UnityMvvmToolkit** allows you to use data binding to establish a connection between the app UI and the data it displays. This is a simple and consistent way to achieve clean separation of business logic from UI. Use the samples as a starting point for understanding how to utilize the package.\n\nKey features:\n- Runtime data-binding\n- UI Toolkit \u0026 uGUI integration\n- Multiple-properties binding\n- Custom UI Elements support\n- Compatible with [UniTask](https://github.com/Cysharp/UniTask)\n- Mono \u0026 IL2CPP support[*](#il2cpp-restriction)\n\n### Samples\n\nThe following example shows the **UnityMvvmToolkit** in action using the **Counter** app.\n\n\u003cdetails open\u003e\u003csummary\u003e\u003cb\u003eCounterView\u003c/b\u003e\u003c/summary\u003e\n\u003cbr /\u003e\n\n```xml\n\u003cUXML\u003e\n    \u003cBindableContentPage binding-theme-mode-path=\"ThemeMode\" class=\"counter-screen\"\u003e\n        \u003cVisualElement class=\"number-container\"\u003e\n            \u003cBindableCountLabel binding-text-path=\"Count\" class=\"count-label count-label--animation\" /\u003e\n        \u003c/VisualElement\u003e\n        \u003cBindableThemeSwitcher binding-value-path=\"ThemeMode, Converter={ThemeModeToBoolConverter}\" /\u003e\n        \u003cBindableCounterSlider increment-command=\"IncrementCommand\" decrement-command=\"DecrementCommand\" /\u003e\n    \u003c/BindableContentPage\u003e\n\u003c/UXML\u003e\n```\n\n\u003e **Note:** The namespaces are omitted to make the example more readable.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cb\u003eCounterViewModel\u003c/b\u003e\u003c/summary\u003e\n\u003cbr /\u003e\n\n```csharp\npublic class CounterViewModel : IBindingContext\n{\n    public CounterViewModel()\n    {\n        Count = new Property\u003cint\u003e();\n        ThemeMode = new Property\u003cThemeMode\u003e();\n\n        IncrementCommand = new Command(IncrementCount);\n        DecrementCommand = new Command(DecrementCount);\n    }\n\n    public IProperty\u003cint\u003e Count { get; }\n    public IProperty\u003cThemeMode\u003e ThemeMode { get; }\n\n    public ICommand IncrementCommand { get; }\n    public ICommand DecrementCommand { get; }\n\n    private void IncrementCount() =\u003e Count.Value++;\n    private void DecrementCount() =\u003e Count.Value--;\n}\n```\n\n\u003c/details\u003e\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003eCounter\u003c/td\u003e\n    \u003ctd align=\"center\"\u003eCalculator\u003c/td\u003e\n    \u003ctd align=\"center\"\u003eToDoList\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\" width=25%\u003e\n      \u003cvideo src=\"https://user-images.githubusercontent.com/28132516/187030099-a440bc89-4c28-44e3-9898-9894eac5bff4.mp4\" alt=\"CounterSample\" /\u003e\n    \u003c/td\u003e\n    \u003ctd align=\"center\" width=25%\u003e\n      \u003cvideo src=\"https://user-images.githubusercontent.com/28132516/187471982-4acdeddb-ec4d-45b6-a2a3-4198a03760de.mp4\" alt=\"CalculatorSample\" /\u003e\n    \u003c/td\u003e\n    \u003ctd align=\"center\" width=25%\u003e\n      \u003cvideo src=\"https://user-images.githubusercontent.com/28132516/187030101-ad1f2123-59d5-4d1e-a9ca-ab983589e52f.mp4\" alt=\"ToDoListSample\" /\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003e You will find all the samples in the `samples` folder.\n\n## :cactus: Folder Structure\n\n    .\n    ├── samples\n    │   ├── Unity.Mvvm.Calc\n    │   ├── Unity.Mvvm.Counter\n    │   ├── Unity.Mvvm.ToDoList\n    │   └── Unity.Mvvm.CounterLegacy\n    │\n    ├── src\n    │   ├── UnityMvvmToolkit.Core\n    │   └── UnityMvvmToolkit.UnityPackage\n    │       ...\n    │       ├── Core      # Auto-generated\n    │       ├── Common\n    │       ├── External\n    │       ├── UGUI\n    │       └── UITK\n    │\n    ├── UnityMvvmToolkit.sln\n\n## :gear: Installation\n\nYou can install UnityMvvmToolkit in one of the following ways:\n\n\u003cdetails\u003e\u003csummary\u003e1. Install via Package Manager\u003c/summary\u003e\n\u003cbr /\u003e\n  \n  The package is available on the [OpenUPM](https://openupm.com/packages/com.chebanovdd.unitymvvmtoolkit/).\n\n  - Open `Edit/Project Settings/Package Manager`\n  - Add a new `Scoped Registry` (or edit the existing OpenUPM entry)\n\n    ```\n    Name      package.openupm.com\n    URL       https://package.openupm.com\n    Scope(s)  com.cysharp.unitask\n              com.chebanovdd.unitymvvmtoolkit\n    ```\n  - Open `Window/Package Manager`\n  - Select `My Registries`\n  - Install `UniTask` and `UnityMvvmToolkit` packages\n  \n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e2. Install via Git URL\u003c/summary\u003e\n\u003cbr /\u003e\n  \n  You can add `https://github.com/ChebanovDD/UnityMvvmToolkit.git?path=src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit` to the Package Manager.\n\n  If you want to set a target version, UnityMvvmToolkit uses the `v*.*.*` release tag, so you can specify a version like `#v1.0.0`. For example `https://github.com/ChebanovDD/UnityMvvmToolkit.git?path=src/UnityMvvmToolkit.UnityPackage/Assets/Plugins/UnityMvvmToolkit#v1.0.0`.\n  \n\u003c/details\u003e\n\n### IL2CPP restriction\n\nThe **UnityMvvmToolkit** uses generic virtual methods under the hood to create bindable properties, but `IL2CPP` in `Unity 2021` does not support [Full Generic Sharing](https://blog.unity.com/technology/feature-preview-il2cpp-full-generic-sharing-in-unity-20221-beta) this restriction will be removed in `Unity 2022`.\n\nTo work around this issue in `Unity 2021` you need to change the `IL2CPP Code Generation` setting in the `Build Settings` window to `Faster (smaller) builds`.\n\n\u003cdetails\u003e\u003csummary\u003eInstruction\u003c/summary\u003e\n\u003cbr /\u003e\n\n![build-settings](https://user-images.githubusercontent.com/28132516/187468236-4b455b62-48ef-4e9c-9a3f-83391833c3c0.png)\n\n\u003c/details\u003e\n\n## :ledger: Introduction\n\nThe package contains a collection of standard, self-contained, lightweight types that provide a starting implementation for building apps using the MVVM pattern.\n\nThe included types are:\n- [IBindingContext](#ibindingcontext)\n- [CanvasView\\\u003cTBindingContext\\\u003e](#canvasviewtbindingcontext)\n- [DocumentView\\\u003cTBindingContext\\\u003e](#documentviewtbindingcontext)\n- [Property\\\u003cT\\\u003e \u0026 ReadOnlyProperty\\\u003cT\\\u003e](#propertyt--readonlypropertyt)\n- [Command \u0026 Command\\\u003cT\\\u003e](#command--commandt)\n- [AsyncCommand \u0026 AsyncCommand\\\u003cT\\\u003e](#asynccommand--asynccommandt)\n- [PropertyValueConverter\\\u003cTSourceType, TTargetType\\\u003e](#propertyvalueconvertertsourcetype-ttargettype)\n- [ParameterValueConverter\\\u003cTTargetType\\\u003e](#parametervalueconverterttargettype)\n- [IProperty\\\u003cT\\\u003e \u0026 IReadOnlyProperty\\\u003cT\\\u003e](#propertyt--readonlypropertyt)\n- [ICommand \u0026 ICommand\\\u003cT\\\u003e](#command--commandt)\n- [IAsyncCommand \u0026 IAsyncCommand\\\u003cT\\\u003e](#asynccommand--asynccommandt)\n- [IPropertyValueConverter\\\u003cTSourceType, TTargetType\\\u003e](#propertyvalueconvertertsourcetype-ttargettype)\n- [IParameterValueConverter\\\u003cTTargetType\\\u003e](#parametervalueconverterttargettype)\n\n### IBindingContext\n\nThe `IBindingContext` is a base interface for ViewModels. It is a marker for Views that the class contains observable properties to bind to.\n\nHere's an example of a simple ViewModel.\n\n```csharp\npublic class CounterViewModel : IBindingContext\n{\n    public CounterViewModel()\n    {\n        Count = new Property\u003cint\u003e();\n    }\n\n    public IProperty\u003cint\u003e Count { get; }\n}\n```\n\n\u003e **Note:** In case your ViewModel doesn't have a parameterless constructor, you need to override the `GetBindingContext` method in the View.\n\n### CanvasView\\\u003cTBindingContext\\\u003e\n\nThe `CanvasView\u003cTBindingContext\u003e` is a base class for `uGUI` views.\n\nKey functionality:\n- Provides a base implementation for `Canvas` based view\n- Automatically searches for bindable UI elements on the `Canvas`\n- Allows to override the base viewmodel instance creation\n- Allows to define [property](#propertyvalueconvertertsourcetype-ttargettype) \u0026 [parameter](#parametervalueconverterttargettype) value converters\n\n```csharp\npublic class CounterView : CanvasView\u003cCounterViewModel\u003e\n{\n    // Override the base viewmodel instance creation.\n    // Required in case the viewmodel doesn't have a parameterless constructor.\n    protected override CounterViewModel GetBindingContext()\n    {\n        return _appContext.Resolve\u003cCounterViewModel\u003e();\n    }\n\n    // Define 'property' \u0026 'parameter' value converters.\n    protected override IValueConverter[] GetValueConverters()\n    {\n        return _appContext.Resolve\u003cIValueConverter[]\u003e();\n    }\n  \n    // Define a collection item templates.\n    protected override IReadOnlyDictionary\u003cType, object\u003e GetCollectionItemTemplates()\n    {\n        return _appContext.Resolve\u003cIReadOnlyDictionary\u003cType, object\u003e\u003e();\n    }\n}\n```\n\n### DocumentView\\\u003cTBindingContext\\\u003e\n\nThe `DocumentView\u003cTBindingContext\u003e` is a base class for `UI Toolkit` views.\n\nKey functionality:\n- Provides a base implementation for `UI Document` based view\n- Automatically searches for bindable UI elements on the `UI Document`\n- Allows to override the base viewmodel instance creation\n- Allows to define [property](#propertyvalueconvertertsourcetype-ttargettype) \u0026 [parameter](#parametervalueconverterttargettype) value converters\n\n```csharp\npublic class CounterView : DocumentView\u003cCounterViewModel\u003e\n{\n    // Override the base viewmodel instance creation.\n    // Required in case the viewmodel doesn't have a parameterless constructor.\n    protected override CounterViewModel GetBindingContext()\n    {\n        return _appContext.Resolve\u003cCounterViewModel\u003e();\n    }\n\n    // Define 'property' \u0026 'parameter' value converters.\n    protected override IValueConverter[] GetValueConverters()\n    {\n        return _appContext.Resolve\u003cIValueConverter[]\u003e();\n    }\n  \n    // Define a collection item templates.\n    protected override IReadOnlyDictionary\u003cType, object\u003e GetCollectionItemTemplates()\n    {\n        return _appContext.Resolve\u003cIReadOnlyDictionary\u003cType, object\u003e\u003e();\n    }\n}\n```\n\n### Property\\\u003cT\\\u003e \u0026 ReadOnlyProperty\\\u003cT\\\u003e\n\nThe `Property\u003cT\u003e` and `ReadOnlyProperty\u003cT\u003e` provide a way to bind properties between a ViewModel and UI elements.\n\nKey functionality:\n- Provide a base implementation of the `IBaseProperty` interface\n- Implement the `IProperty\u003cT\u003e` \u0026 `IReadOnlyProperty\u003cT\u003e` interface, which exposes a `ValueChanged` event\n\n#### Simple property\n\nHere's an example of how to implement a simple property.\n\n```csharp\npublic class CounterViewModel : IBindingContext\n{\n    public CounterViewModel()\n    {\n        Count = new Property\u003cint\u003e();\n    }\n\n    public IProperty\u003cint\u003e Count { get; }\n}\n```\n\n```xml\n\u003cui:UXML xmlns:uitk=\"UnityMvvmToolkit.UITK.BindableUIElements\" ...\u003e\n    \u003cuitk:BindableLabel binding-text-path=\"Count\" /\u003e\n\u003c/ui:UXML\u003e\n```\n\n\u003e **Note:** You need to define `IntToStrConverter` to convert int to string. See the [PropertyValueConverter](#propertyvalueconvertertsourcetype-ttargettype) section for more information.\n\n#### Observable property\n\n```csharp\npublic class MyViewModel : IBindingContext\n{\n    [Observable(\"Count\")]\n    private readonly IProperty\u003cint\u003e _amount = new Property\u003cint\u003e();\n  \n    // The field name will be used if you don't provide a property name.\n    // Names '_title' and 'm_title' will be auto-converted to 'Title'.\n    [Observable]\n    private readonly IProperty\u003cstring\u003e _title = new Property\u003cstring\u003e();\n}\n```\n\n```xml\n\u003cui:UXML xmlns:uitk=\"UnityMvvmToolkit.UITK.BindableUIElements\" ...\u003e\n    \u003cuitk:BindableLabel binding-text-path=\"Count\" /\u003e\n    \u003cuitk:BindableLabel binding-text-path=\"Title\" /\u003e\n\u003c/ui:UXML\u003e\n```\n\n\u003e **Note:** You need to define `IntToStrConverter` to convert int to string. See the [PropertyValueConverter](#propertyvalueconvertertsourcetype-ttargettype) section for more information.\n\nYou can use the `Observable` attribute even on `public` properties to override the binding path.\n\n```csharp\npublic class MyViewModel : IBindingContext\n{\n    [Observable(\"PreviousPropertyName\")]\n    public IReadOnlyProperty\u003cstring\u003e NewPropertyName { get; }\n}\n```\n\n#### Wrapping a non-observable model\n\nA common scenario, for instance, when working with database items, is to create a wrapping \"bindable\" model that relays properties of the database model, and raises the property changed notifications when needed.\n\n```csharp\npublic class UserViewModel : IBindingContext\n{\n    private readonly User _user;\n\n    [Observable(nameof(Name))]\n    private readonly IProperty\u003cstring\u003e _name = new Property\u003cstring\u003e();\n\n    public UserViewModel(User user)\n    {\n        _user = user;\n        _name.Value = user.Name;\n    }\n\n    public string Name\n    {\n        get =\u003e _user.Name;\n        set\n        {\n            if (_name.TrySetValue(value))\n            {\n                _user.Name = value;\n            }\n        }\n    }\n}\n```\n\n```xml\n\u003cui:UXML xmlns:uitk=\"UnityMvvmToolkit.UITK.BindableUIElements\" ...\u003e\n    \u003cuitk:BindableLabel binding-text-path=\"Name\" /\u003e\n\u003c/ui:UXML\u003e\n```\n\nTo achieve the same result, but with minimal boilerplate code, you can automatically create an observable backing field using the `[WithObservableBackingField]` attribute from [UnityMvvmToolkit.Generator](https://github.com/LibraStack/UnityMvvmToolkit.Generator).\n\n```csharp\npublic partial class UserViewModel : IBindingContext\n{\n    private readonly User _user;\n\n    public UserViewModel(User user)\n    {\n        _user = user;\n        _name.Value = user.Name;\n    }\n\n    [WithObservableBackingField]\n    public string Name\n    {\n        get =\u003e _user.Name;\n        set\n        {\n            if (_name.TrySetValue(value))\n            {\n                _user.Name = value;\n            }\n        }\n    }\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003e\u003cb\u003eGenerated code\u003c/b\u003e\u003c/summary\u003e\n\u003cbr /\u003e\n\n`UserViewModel.BackingFields.g.cs`\n\n```csharp\npartial class UserViewModel\n{\n    [global::System.CodeDom.Compiler.GeneratedCode(\"UnityMvvmToolkit.Generator\", \"1.0.0.0\")]\n    [global::UnityMvvmToolkit.Core.Attributes.Observable(nameof(Name))]\n    private readonly global::UnityMvvmToolkit.Core.Interfaces.IProperty\u003cstring\u003e _name = new global::UnityMvvmToolkit.Core.Property\u003cstring\u003e();\n}\n```\n\n\u003c/details\u003e\n\nWaiting for the [partial properties](https://github.com/dotnet/csharplang/issues/6420) support to make it even shorter.\n\n```csharp\npublic partial class UserViewModel : IBindingContext\n{\n    private readonly User _user;\n\n    public UserViewModel(User user)\n    {\n        _user = user;\n        _name.Value = user.Name;\n    }\n\n    [WithObservableBackingField]\n    public partial string Name { get; set; }\n}\n```\n\n\u003e **Note:** The [UnityMvvmToolkit.Generator](https://github.com/LibraStack/UnityMvvmToolkit.Generator) is available exclusively for my [patrons](https://patreon.com/DimaChebanov).\n\n#### Serializable ViewModel\n\nA common scenario, for instance, when working with collection items, is to create a \"bindable\" item that can be serialized.\n\n```csharp\npublic class ItemViewModel : ICollectionItem\n{\n    [Observable(nameof(Name))]\n    private readonly IProperty\u003cstring\u003e _name = new Property\u003cstring\u003e();\n\n    public int Id { get; set; }\n\n    public string Name\n    {\n        get =\u003e _name.Value;\n        set =\u003e _name.Value = value;\n    }\n}\n```\n\n```xml\n\u003cui:UXML xmlns:uitk=\"UnityMvvmToolkit.UITK.BindableUIElements\" ...\u003e\n    \u003cuitk:BindableLabel binding-text-path=\"Name\" /\u003e\n\u003c/ui:UXML\u003e\n```\n\nThe `ItemViewModel` can be serialized and deserialized without any issues.\n\nThe same result, but using the `[WithObservableBackingField]` attribute from [UnityMvvmToolkit.Generator](https://github.com/LibraStack/UnityMvvmToolkit.Generator).\n\n```csharp\npublic partial class ItemViewModel : ICollectionItem\n{\n    public int Id { get; set; }\n\n    [WithObservableBackingField]\n    public string Name\n    {\n        get =\u003e _name.Value;\n        set =\u003e _name.Value = value;\n    }\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003e\u003cb\u003eGenerated code\u003c/b\u003e\u003c/summary\u003e\n\u003cbr /\u003e\n\n`ItemViewModel.BackingFields.g.cs`\n\n```csharp\npartial class ItemViewModel\n{\n    [global::System.CodeDom.Compiler.GeneratedCode(\"UnityMvvmToolkit.Generator\", \"1.0.0.0\")]\n    [global::UnityMvvmToolkit.Core.Attributes.Observable(nameof(Name))]\n    private readonly global::UnityMvvmToolkit.Core.Interfaces.IProperty\u003cstring\u003e _name = new global::UnityMvvmToolkit.Core.Property\u003cstring\u003e();\n}\n```\n\n\u003c/details\u003e\n\n\u003e **Note:** The [UnityMvvmToolkit.Generator](https://github.com/LibraStack/UnityMvvmToolkit.Generator) is available exclusively for my [patrons](https://patreon.com/DimaChebanov).\n\n### Command \u0026 Command\\\u003cT\\\u003e\n\nThe `Command` and `Command\u003cT\u003e` are `ICommand` implementations that can expose a method or delegate to the view. These types act as a way to bind commands between the viewmodel and UI elements.\n\nKey functionality:\n- Provide a base implementation of the `ICommand` interface\n- Implement the `ICommand` \u0026 `ICommand\u003cT\u003e` interface, which exposes a `RaiseCanExecuteChanged` method to raise the `CanExecuteChanged` event\n- Expose constructor taking delegates like `Action` and `Action\u003cT\u003e`, which allow the wrapping of standard methods and lambda expressions\n\nThe following shows how to set up a simple command.\n\n```csharp\nusing UnityMvvmToolkit.Core;\nusing UnityMvvmToolkit.Core.Interfaces;\n\npublic class CounterViewModel : IBindingContext\n{\n    public CounterViewModel()\n    {\n        Count = new Property\u003cint\u003e();\n        \n        IncrementCommand = new Command(IncrementCount);\n    }\n\n    public IProperty\u003cint\u003e Count { get; }\n\n    public ICommand IncrementCommand { get; }\n\n    private void IncrementCount() =\u003e Count.Value++;\n}\n```\n\nAnd the relative UI could then be.\n\n```xml\n\u003cui:UXML xmlns:uitk=\"UnityMvvmToolkit.UITK.BindableUIElements\" ...\u003e\n    \u003cuitk:BindableLabel binding-text-path=\"Count\" /\u003e\n    \u003cuitk:BindableButton command=\"IncrementCommand\" /\u003e\n\u003c/ui:UXML\u003e\n```\n\nThe `BindableButton` binds to the `ICommand` in the viewmodel, which wraps the private `IncrementCount` method. The `BindableLabel` displays the value of the `Count` property and is updated every time the property value changes.\n\n\u003e **Note:** You need to define `IntToStrConverter` to convert int to string. See the [PropertyValueConverter](#propertyvalueconvertertsourcetype-ttargettype) section for more information.\n\n### AsyncCommand \u0026 AsyncCommand\\\u003cT\\\u003e\n\nThe `AsyncCommand` and `AsyncCommand\u003cT\u003e` are `ICommand` implementations that extend the functionalities offered by `Command`, with support for asynchronous operations.\n\nKey functionality:\n- Extend the functionalities of the synchronous commands included in the package, with support for UniTask-returning delegates\n- Can wrap asynchronous functions with a `CancellationToken` parameter to support cancelation, and they expose a `DisableOnExecution` property, as well as a `Cancel` method\n- Implement the `IAsyncCommand` \u0026 `IAsyncCommand\u003cT\u003e` interfaces, which allows to replace a command with a custom implementation, if needed\n\nLet's say we want to download an image from the web and display it as soon as it downloads.\n\n```csharp\npublic class ImageViewerViewModel : IBindingContext\n{\n    [Observable(nameof(Image))]\n    private readonly IProperty\u003cTexture2D\u003e _image;\n    private readonly IImageDownloader _imageDownloader;\n\n    public ImageViewerViewModel(IImageDownloader imageDownloader)\n    {\n        _image = new Property\u003cTexture2D\u003e();\n        _imageDownloader = imageDownloader;\n        \n        DownloadImageCommand = new AsyncCommand(DownloadImageAsync);\n    }\n\n    public Texture2D Image =\u003e _image.Value;\n\n    public IAsyncCommand DownloadImageCommand { get; }\n\n    private async UniTask DownloadImageAsync(CancellationToken cancellationToken)\n    {\n        _image.Value = await _imageDownloader.DownloadRandomImageAsync(cancellationToken);\n    }\n}\n```\n\nWith the related UI code.\n\n```xml\n\u003cui:UXML xmlns:uitk=\"UnityMvvmToolkit.UITK.BindableUIElements\" ...\u003e\n    \u003cBindableImage binding-image-path=\"Image\" /\u003e\n    \u003cuitk:BindableButton command=\"DownloadImageCommand\"\u003e\n        \u003cui:Label text=\"Download Image\" /\u003e\n    \u003c/uitk:BindableButton\u003e\n\u003c/ui:UXML\u003e\n```\n\n\u003e **Note:** The `BindableImage` is a custom control from the [create custom control](#create-custom-control) section.\n\nTo disable the `BindableButton` while an async operation is running, simply set the `DisableOnExecution` property of the `AsyncCommand` to `true`.\n\n```csharp\npublic class ImageViewerViewModel : IBindingContext\n{\n    public ImageViewerViewModel(IImageDownloader imageDownloader)\n    {\n        ...\n        DownloadImageCommand = new AsyncCommand(DownloadImageAsync) { DisableOnExecution = true };\n    }\n}\n```\n\nTo allow the same async command to be invoked concurrently multiple times, set the `AllowConcurrency` property of the `AsyncCommand` to `true`.\n\n```csharp\npublic class MainViewModel : IBindingContext\n{\n    public MainViewModel()\n    {\n        RunConcurrentlyCommand = new AsyncCommand(RunConcurrentlyAsync) { AllowConcurrency = true };\n    }\n}\n```\n\nIf you want to create an async command that supports cancellation, use the `WithCancellation` extension method.\n\n```csharp\npublic class MyViewModel : IBindingContext\n{\n    public MyViewModel()\n    {\n        MyAsyncCommand = new AsyncCommand(DoSomethingAsync).WithCancellation();\n        CancelCommand = new Command(Cancel);\n    }\n\n    public IAsyncCommand MyAsyncCommand { get; }\n    public ICommand CancelCommand { get; }\n    \n    private async UniTask DoSomethingAsync(CancellationToken cancellationToken)\n    {\n        ...\n    }\n    \n    private void Cancel()\n    {\n        // If the underlying command is not running, this method will perform no action.\n        MyAsyncCommand.Cancel();\n    }\n}\n```\n\nIf a command supports cancellation and the `AllowConcurrency` property is set to `true`, all running commands will be canceled.\n\n\u003e **Note:** You need to import the [UniTask](https://github.com/Cysharp/UniTask) package in order to use async commands.\n\n### PropertyValueConverter\\\u003cTSourceType, TTargetType\\\u003e\n\nProperty value converter provides a way to apply custom logic to a property binding.\n\nBuilt-in property value converters:\n- IntToStrConverter\n- FloatToStrConverter\n\nIf you want to create your own property value converter, create a class that inherits the `PropertyValueConverter\u003cTSourceType, TTargetType\u003e` abstract class and then implement the `Convert` and `ConvertBack` methods.\n\n```csharp\npublic enum ThemeMode\n{\n    Light = 0,\n    Dark = 1\n}\n\npublic class ThemeModeToBoolConverter : PropertyValueConverter\u003cThemeMode, bool\u003e\n{\n    // From source to target. \n    public override bool Convert(ThemeMode value)\n    {\n        return (int) value == 1;\n    }\n\n    // From target to source.\n    public override ThemeMode ConvertBack(bool value)\n    {\n        return (ThemeMode) (value ? 1 : 0);\n    }\n}\n```\nDon't forget to register the `ThemeModeToBoolConverter` in the view.\n\n```csharp\npublic class MyView : DocumentView\u003cMyViewModel\u003e\n{\n    protected override IValueConverter[] GetValueConverters()\n    {\n        return new IValueConverter[] { new ThemeModeToBoolConverter() };\n    }\n}\n```\n\nThen you can use the `ThemeModeToBoolConverter` as in the following example.\n\n```xml\n\u003cUXML\u003e\n    \u003c!--Full expression--\u003e\n    \u003cMyBindableElement binding-value-path=\"ThemeMode, Converter={ThemeModeToBoolConverter}\" /\u003e\n    \u003c!--Short expression--\u003e\n    \u003cMyBindableElement binding-value-path=\"ThemeMode, ThemeModeToBoolConverter\" /\u003e\n    \u003c!--Minimal expression - the first appropriate converter will be used--\u003e\n    \u003cMyBindableElement binding-value-path=\"ThemeMode\" /\u003e\n\u003c/UXML\u003e\n```\n\n### ParameterValueConverter\\\u003cTTargetType\\\u003e\n\nParameter value converter allows to convert a command parameter.\n\nBuilt-in parameter value converters:\n- ParameterToIntConverter\n- ParameterToFloatConverter\n\nBy default, the converter is not needed if your command has a `string` parameter type.\n\n```csharp\npublic class MyViewModel : IBindingContext\n{\n    public MyViewModel()\n    {\n        PrintParameterCommand = new Command\u003cstring\u003e(PrintParameter);\n    }\n\n    public ICommand\u003cstring\u003e PrintParameterCommand { get; }\n\n    private void PrintParameter(string parameter)\n    {\n        Debug.Log(parameter);\n    }\n}\n```\n\n```xml\n\u003cUXML\u003e\n    \u003cBindableButton command=\"PrintParameterCommand, Parameter={MyParameter}\" /\u003e\n    \u003c!--or--\u003e\n    \u003cBindableButton command=\"PrintParameterCommand, MyParameter\" /\u003e\n\u003c/UXML\u003e\n```\n\nIf you want to create your own parameter value converter, create a class that inherits the `ParameterValueConverter\u003cTTargetType\u003e` abstract class and then implement the `Convert` method.\n\n```csharp\npublic class ParameterToIntConverter : ParameterValueConverter\u003cint\u003e\n{\n    public override int Convert(string parameter)\n    {\n        return int.Parse(parameter);\n    }\n}\n```\n\nDon't forget to register the `ParameterToIntConverter` in the view.\n\n```csharp\npublic class MyView : DocumentView\u003cMyViewModel\u003e\n{\n    protected override IValueConverter[] GetValueConverters()\n    {\n        return new IValueConverter[] { new ParameterToIntConverter() };\n    }\n}\n```\n\nThen you can use the `ParameterToIntConverter` as in the following example.\n\n```csharp\npublic class MyViewModel : IBindingContext\n{\n    public MyViewModel()\n    {\n        PrintParameterCommand = new Command\u003cint\u003e(PrintParameter);\n    }\n\n    public ICommand\u003cint\u003e PrintParameterCommand { get; }\n\n    private void PrintParameter(int parameter)\n    {\n        Debug.Log(parameter);\n    }\n}\n```\n\n```xml\n\u003cUXML\u003e\n    \u003c!--Full expression--\u003e\n    \u003cBindableButton command=\"PrintIntParameterCommand, Parameter={5}, Converter={ParameterToIntConverter}\" /\u003e\n    \u003c!--Short expression--\u003e\n    \u003cBindableButton command=\"PrintIntParameterCommand, 5, ParameterToIntConverter\" /\u003e\n    \u003c!--Minimal expression - the first appropriate converter will be used--\u003e\n    \u003cBindableButton command=\"PrintIntParameterCommand, 5\" /\u003e\n\u003c/UXML\u003e\n```\n\n## :watch: Quick start\n\nOnce the `UnityMVVMToolkit` is installed, create a class `MyFirstViewModel` that implements the `IBindingContext` interface.\n\n```csharp\nusing UnityMvvmToolkit.Core;\nusing UnityMvvmToolkit.Core.Interfaces;\n\npublic class MyFirstViewModel : IBindingContext\n{\n    public MyFirstViewModel()\n    {\n        Text = new ReadOnlyProperty\u003cstring\u003e(\"Hello World\");\n    }\n\n    public IReadOnlyProperty\u003cstring\u003e Text { get; }\n}\n```\n\n#### UI Toolkit\n\nThe next step is to create a class `MyFirstDocumentView` that inherits the `DocumentView\u003cTBindingContext\u003e` class.\n\n```csharp\nusing UnityMvvmToolkit.UITK;\n\npublic class MyFirstDocumentView : DocumentView\u003cMyFirstViewModel\u003e\n{\n}\n```\n\nThen create a file `MyFirstView.uxml`, add a `BindableLabel` control and set the `binding-text-path` to `Text`.\n\n```xml\n\u003cui:UXML xmlns:uitk=\"UnityMvvmToolkit.UITK.BindableUIElements\" ...\u003e\n    \u003cuitk:BindableLabel binding-text-path=\"Text\" /\u003e\n\u003c/ui:UXML\u003e\n```\n\nFinally, add `UI Document` to the scene, set the `MyFirstView.uxml` as a `Source Asset` and add the `MyFirstDocumentView` component to it.\n\n\u003cdetails\u003e\u003csummary\u003eUI Document Inspector\u003c/summary\u003e\n\u003cbr /\u003e\n\n![ui-document-inspector](https://user-images.githubusercontent.com/28132516/187613060-e20a139d-72fc-4088-b8d5-f9a01f5afa5b.png)\n\n\u003c/details\u003e\n\n#### Unity UI (uGUI)\n\nFor the `uGUI` do the following. Create a class `MyFirstCanvasView` that inherits the `CanvasView\u003cTBindingContext\u003e` class.\n\n```csharp\nusing UnityMvvmToolkit.UGUI;\n\npublic class MyFirstCanvasView : CanvasView\u003cMyFirstViewModel\u003e\n{\n}\n```\n\nThen add a `Canvas` to the scene, and add the `MyFirstCanvasView` component to it.\n\n\u003cdetails\u003e\u003csummary\u003eCanvas Inspector\u003c/summary\u003e\n\u003cbr /\u003e\n\n![canvas-inspector](https://user-images.githubusercontent.com/28132516/187613633-2c61c82e-ac25-4319-8e8d-1954eb4be197.png)\n\n\u003c/details\u003e\n\nFinally, add a `Text - TextMeshPro` UI element to the canvas, add the `BindableLabel` component to it and set the `BindingTextPath` to `Text`.\n\n\u003cdetails\u003e\u003csummary\u003eCanvas Text Inspector\u003c/summary\u003e\n\u003cbr /\u003e\n\n![canvas-text-inspector](https://user-images.githubusercontent.com/28132516/187614103-ad42d000-b3b7-4265-96a6-f6d4db6e8978.png)\n\n\u003c/details\u003e\n\n## :joystick: How To Use\n\n### Data-binding\n\nThe package contains a set of standard bindable UI elements out of the box.\n\nThe included UI elements are:\n- [BindableLabel](#bindablelabel)\n- [BindableTextField](#bindabletextfield)\n- [BindableButton](#bindablebutton)\n- [BindableDropdownField](#bindabledropdownfield)\n- [BindableListView](#bindablelistview)\n- [BindableScrollView](#bindablescrollview)\n- [BindingContextProvider](#bindingcontextprovider)\n\n\u003e **Note:** The `BindableListView` \u0026 `BindableScrollView` are provided for `UI Toolkit` only.\n\n#### BindableLabel\n\nThe `BindableLabel` element uses the `OneWay` binding by default.\n\n```csharp\npublic class LabelViewModel : IBindingContext\n{\n    public LabelViewModel()\n    {\n        IntValue = new Property\u003cint\u003e(55);\n        StrValue = new Property\u003cstring\u003e(\"69\");\n    }\n\n    public IReadOnlyProperty\u003cint\u003e IntValue { get; }\n    public IReadOnlyProperty\u003cstring\u003e StrValue { get; }\n}\n\npublic class LabelView : DocumentView\u003cLabelViewModel\u003e\n{\n    protected override IValueConverter[] GetValueConverters()\n    {\n        return new IValueConverter[] { new IntToStrConverter() };\n    }\n}\n```\n\n```xml\n\u003cui:UXML xmlns:uitk=\"UnityMvvmToolkit.UITK.BindableUIElements\" ...\u003e\n    \u003cuitk:BindableLabel binding-text-path=\"StrValue\" /\u003e\n    \u003cuitk:BindableLabel binding-text-path=\"IntValue\" /\u003e\n\u003c/ui:UXML\u003e\n```\n\n#### BindableTextField\n\nThe `BindableTextField` element uses the `TwoWay` binding by default.\n\n```csharp\npublic class TextFieldViewModel : IBindingContext\n{\n    public TextFieldViewModel()\n    {\n        TextValue = new Property\u003cstring\u003e();\n    }\n\n    public IProperty\u003cstring\u003e TextValue { get; }\n}\n```\n\n```xml\n\u003cui:UXML xmlns:uitk=\"UnityMvvmToolkit.UITK.BindableUIElements\" ...\u003e\n    \u003cuitk:BindableTextField binding-value-path=\"TextValue\" /\u003e\n\u003c/ui:UXML\u003e\n```\n\n#### BindableButton\n\nThe `BindableButton` can be bound to the following commands:\n- [Command \u0026 Command\\\u003cT\\\u003e](#command--commandt)\n- [AsyncCommand \u0026 AsyncCommand\\\u003cT\\\u003e](#asynccommand--asynccommandt)\n- [AsyncLazyCommand \u0026 AsyncLazyCommand\\\u003cT\\\u003e](#asynclazycommand--asynclazycommandt)\n\nTo pass a parameter to the viewmodel, see the [ParameterValueConverter](#parametervalueconverterttargettype) section.\n\n#### BindableDropdownField\n\nThe `BindableDropdownField` allows the user to pick a choice from a list of options. The `BindingSelectedItemPath` attribute is optional.\n\n```csharp\npublic class DropdownFieldViewModel : IBindingContext\n{\n    public DropdownFieldViewModel()\n    {\n        var items = new ObservableCollection\u003cstring\u003e\n        {\n            \"Value 1\",\n            \"Value 2\",\n            \"Value 3\"\n        };\n\n        Items = new ReadOnlyProperty\u003cObservableCollection\u003cstring\u003e\u003e(items);\n        SelectedItem = new Property\u003cstring\u003e(items[0]);\n    }\n\n    public IReadOnlyProperty\u003cObservableCollection\u003cstring\u003e\u003e Items { get; }\n    public IProperty\u003cstring\u003e SelectedItem { get; }\n}\n```\n\n```xml\n\u003cui:UXML xmlns:uitk=\"UnityMvvmToolkit.UITK.BindableUIElements\" ...\u003e\n    \u003cuitk:BindableDropdownField binding-items-source-path=\"Items\" binding-selected-item-path=\"SelectedItem\" /\u003e\n\u003c/ui:UXML\u003e\n```\n\n#### BindableListView\n\nThe `BindableListView` control is the most efficient way to create lists. It uses virtualization and creates VisualElements only for visible items. Use the `binding-items-source-path` of the `BindableListView` to bind to an `ObservableCollection`.\n\nThe following example demonstrates how to bind to a collection of users with `BindableListView`.\n\nCreate a `UI Document` named `UserItemView.uxml` for the individual items in the list.\n\n```xml\n\u003cui:UXML xmlns:uitk=\"UnityMvvmToolkit.UITK.BindableUIElements\" ...\u003e\n    \u003cuitk:BindableLabel binding-text-path=\"Name\" /\u003e\n\u003c/ui:UXML\u003e\n```\n\nCreate a `UserItemViewModel` class that implements `ICollectionItem` to store user data.\n\n```csharp\npublic class UserItemViewModel : ICollectionItem\n{\n    [Observable(nameof(Name))] \n    private readonly IProperty\u003cstring\u003e _name = new Property\u003cstring\u003e();\n\n    public UserItemViewModel()\n    {\n        Id = Guid.NewGuid().GetHashCode();\n    }\n\n    public int Id { get; }\n\n    public string Name\n    {\n        get =\u003e _name.Value;\n        set =\u003e _name.Value = value;\n    }\n}\n```\n\nCreate a `UserListView` that inherits the `BindableListView\u003cTItemBindingContext\u003e` abstract class.\n\n```csharp\npublic class UserListView : BindableListView\u003cUserItemViewModel\u003e\n{\n    public new class UxmlFactory : UxmlFactory\u003cUserListView, UxmlTraits\u003e {}\n}\n```\n\nCreate a `UsersViewModel`.\n\n```csharp\npublic class UsersViewModel : IBindableContext\n{\n    public UsersViewModel()\n    {\n        var users = new ObservableCollection\u003cUserItemViewModel\u003e\n        {\n            new() { Name = \"User 1\" },\n            new() { Name = \"User 2\" },\n            new() { Name = \"User 3\" },\n        };\n\n        Users = new ReadOnlyProperty\u003cObservableCollection\u003cUserItemViewModel\u003e\u003e(users);\n    }\n\n    public IReadOnlyProperty\u003cObservableCollection\u003cUserItemViewModel\u003e\u003e Users { get; }\n}\n```\n\nNow we need to provide an item template for the `UserItemViewModel`. Create a `UsersView` as follows.\n\n```csharp\npublic class UsersView : DocumentView\u003cUsersViewModel\u003e\n{\n    [SerializeField] private VisualTreeAsset _userItemViewAsset;\n\n    protected override IReadOnlyDictionary\u003cType, object\u003e GetCollectionItemTemplates()\n    {\n        return new Dictionary\u003cType, object\u003e\n        {\n            { typeof(UserItemViewModel), _userItemViewAsset }\n        };\n    }\n}\n```\n\nStarting with Unity 2023, you can select an ItemTemplate directly in the UI Builder.\n\n\u003cdetails\u003e\u003csummary\u003eUI Builder Inspector\u003c/summary\u003e\n\u003cbr /\u003e\n\n![collection-item-template](https://github.com/LibraStack/UnityMvvmToolkit/assets/28132516/2dba3a31-7ca9-45c3-a704-5f847262449c)\n\n\u003c/details\u003e\n\nFinally, create a main `UI Document` named `UsersView.uxml` with the following content.\n\n```xml\n\u003cui:UXML ...\u003e\n    \u003cUserListView binding-items-source-path=\"Users\" /\u003e\n\u003c/ui:UXML\u003e\n```\n\n#### BindableScrollView\n\nThe `BindableScrollView` has the same binding logic as the `BindableListView`. It does not use virtualization and creates VisualElements for all items regardless of visibility.\n\n#### BindingContextProvider\n\nThe `BindingContextProvider` allows you to provide a custom `IBindingContext` for all child elements.\n\nLet's say we have the following binding contexts.\n\n```csharp\npublic class MainViewModel : IBindingContext\n{\n    [Observable] \n    private readonly IReadOnlyProperty\u003cstring\u003e _title;\n\n    [Observable]\n    private readonly IReadOnlyProperty\u003cCustomViewModel\u003e _customViewModel;\n\n    public MainViewModel()\n    {\n        _title = new ReadOnlyProperty\u003cstring\u003e(\"Main Context\");\n        _customViewModel = new ReadOnlyProperty\u003cCustomViewModel\u003e(new CustomViewModel());\n    }\n}\n```\n\n```csharp\npublic class CustomViewModel : IBindingContext\n{\n    [Observable] \n    private readonly IReadOnlyProperty\u003cstring\u003e _title;\n\n    public CustomViewModel()\n    {\n        _title = new ReadOnlyProperty\u003cstring\u003e(\"Custom Context\");\n    }\n}\n```\n\nTo provide the `CustomViewModel` as a binding context for certain elements, we have to use the `BindingContextProvider` as the parent for those elements.\n\n```xml\n\u003cui:UXML xmlns:uitk=\"UnityMvvmToolkit.UITK.BindableUIElements\" ...\u003e\n    \u003cuitk:BindableLabel name=\"Label1\" binding-text-path=\"Title\" /\u003e\n\u003c!-- Binding context not specified. Will be used MainViewModel for all childs. --\u003e\n    \u003cuitk:BindingContextProvider\u003e\n        \u003cuitk:BindableLabel name=\"Label2\" binding-text-path=\"Title\" /\u003e\n    \u003c/uitk:BindingContextProvider\u003e\n\u003c!-- Binding context is specified. Will be used CustomViewModel for all childs. --\u003e\n    \u003cuitk:BindingContextProvider binding-context-path=\"CustomViewModel\"\u003e\n        \u003cuitk:BindableLabel name=\"Label3\" binding-text-path=\"Title\" /\u003e\n    \u003c/uitk:BindingContextProvider\u003e\n\u003c/ui:UXML\u003e\n```\n\nIn this example, `Label1` and `Label2` will display the text \"Main Context\", while `Label3` will display the text \"Custom Context\".\n\nWe can create a `BindingContextProvider` for a specific `IBindingContext` to avoid allocating memory for a new `PropertyCastWrapper` class. Let's create a `CustomViewModelProvider` element.\n\n```csharp\n[UxmlElement]\npublic partial class CustomViewModelProvider : BindingContextProvider\u003cCustomViewModel\u003e\n{\n}\n```\n\n\u003e **Note:** We use a [UxmlElement](#source-code-generator) attribute to create a custom control.\n\nNow we can use the `CustomViewModelProvider` just like the default `BindingContextProvider`.\n\n```xml\n\u003cui:UXML xmlns:uitk=\"UnityMvvmToolkit.UITK.BindableUIElements\" ...\u003e\n    \u003cuitk:BindableLabel name=\"Label1\" binding-text-path=\"Title\" /\u003e\n\u003c!-- Binding context not specified. Will be used MainViewModel for all childs. --\u003e\n    \u003cCustomViewModelProvider\u003e\n        \u003cuitk:BindableLabel name=\"Label2\" binding-text-path=\"Title\" /\u003e\n    \u003c/CustomViewModelProvider\u003e\n\u003c!-- Binding context is specified. Will be used CustomViewModel for all childs. --\u003e\n    \u003cCustomViewModelProvider binding-context-path=\"CustomViewModel\"\u003e\n        \u003cuitk:BindableLabel name=\"Label3\" binding-text-path=\"Title\" /\u003e\n    \u003c/CustomViewModelProvider\u003e\n\u003c/ui:UXML\u003e\n```\n\n### Create custom control\n\nLet's create a `BindableImage` UI element.\n\nFirst of all, create a base `Image` class.\n\n```csharp\npublic class Image : VisualElement\n{\n    public void SetImage(Texture2D image)\n    {\n        style.backgroundImage = new StyleBackground(image);\n    }\n\n    public new class UxmlFactory : UxmlFactory\u003cImage, UxmlTraits\u003e {}\n}\n```\n\nThen create a `BindableImage` class and implement the data binding logic.\n\n```csharp\npublic class BindableImage : Image, IBindableElement\n{\n    private PropertyBindingData _imagePathBindingData;\n    private IReadOnlyProperty\u003cTexture2D\u003e _imageProperty;\n\n    public string BindingImagePath { get; private set; }\n\n    public void SetBindingContext(IBindingContext context, IObjectProvider objectProvider)\n    {\n        _imagePathBindingData ??= BindingImagePath.ToPropertyBindingData();\n\n        _imageProperty = objectProvider.RentReadOnlyProperty\u003cTexture2D\u003e(context, _imagePathBindingData);\n        _imageProperty.ValueChanged += OnImagePropertyValueChanged;\n\n        SetImage(_imageProperty.Value);\n    }\n\n    public void ResetBindingContext(IObjectProvider objectProvider)\n    {\n        if (_imageProperty == null)\n        {\n            return;\n        }\n\n        _imageProperty.ValueChanged -= OnImagePropertyValueChanged;\n\n        objectProvider.ReturnReadOnlyProperty(_imageProperty);\n\n        _imageProperty = null;\n\n        SetImage(null);\n    }\n\n    private void OnImagePropertyValueChanged(object sender, Texture2D newImage)\n    {\n        SetImage(newImage);\n    }\n\n    public new class UxmlFactory : UxmlFactory\u003cBindableImage, UxmlTraits\u003e { }\n\n    public new class UxmlTraits : Image.UxmlTraits\n    {\n        private readonly UxmlStringAttributeDescription _bindingImageAttribute = new()\n            { name = \"binding-image-path\", defaultValue = \"\" };\n\n        public override void Init(VisualElement visualElement, IUxmlAttributes bag, CreationContext context)\n        {\n            base.Init(visualElement, bag, context);\n            ((BindableImage) visualElement).BindingImagePath = _bindingImageAttribute.GetValueFromBag(bag, context);\n        }\n    }\n}\n```\n\nNow you can use the new UI element as following.\n\n```csharp\npublic class ImageViewerViewModel : IBindingContext\n{\n    public ImageItemViewModel(Texture2D image)\n    {\n        Image = new ReadOnlyProperty\u003cTexture2D\u003e(image);\n    }\n\n    public IReadOnlyProperty\u003cTexture2D\u003e Image { get; }\n}\n```\n\n```xml\n\u003cUXML\u003e\n    \u003cBindableImage binding-image-path=\"Image\" /\u003e\n\u003c/UXML\u003e\n```\n\n### Source code generator\n\nThe best way to speed up the creation of custom `VisualElement` is to use source code generators. With this powerful tool, you can achieve the same great results with minimal boilerplate code and focus on what really matters: programming!\n\nLet's create the `BindableImage` control, but this time using source code generators.\n\nFor a visual element without bindings, we will use a [UnityUxmlGenerator](https://github.com/LibraStack/UnityUxmlGenerator).\n\n```csharp\n[UxmlElement]\npublic partial class Image : VisualElement\n{\n    public void SetImage(Texture2D image)\n    {\n        style.backgroundImage = new StyleBackground(image);\n    }\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003e\u003cb\u003eGenerated code\u003c/b\u003e\u003c/summary\u003e\n\u003cbr /\u003e\n\n`Image.UxmlFactory.g.cs`\n\n```csharp\npartial class Image\n{\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"UnityUxmlGenerator\", \"1.0.0.0\")]\n    public new class UxmlFactory : global::UnityEngine.UIElements.UxmlFactory\u003cImage, UxmlTraits\u003e \n    {\n    }\n}\n```\n\n\u003c/details\u003e\n\nFor a bindable visual element, we will use a [UnityMvvmToolkit.Generator](https://github.com/LibraStack/UnityMvvmToolkit.Generator).\n\n```csharp\n[BindableElement]\npublic partial class BindableImage : Image\n{\n    [BindableProperty]\n    private IReadOnlyProperty\u003cTexture2D\u003e _imageProperty;\n\n    partial void AfterSetBindingContext(IBindingContext context, IObjectProvider objectProvider)\n    {\n        SetImage(_imageProperty?.Value);\n    }\n\n    partial void AfterResetBindingContext(IObjectProvider objectProvider)\n    {\n        SetImage(null);\n    }\n\n    partial void OnImagePropertyValueChanged([CanBeNull] Texture2D value)\n    {\n        SetImage(value);\n    }\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003e\u003cb\u003eGenerated code\u003c/b\u003e\u003c/summary\u003e\n\u003cbr /\u003e\n\n`BindableImage.Bindings.g.cs`\n\n```csharp\npartial class BindableImage : global::UnityMvvmToolkit.Core.Interfaces.IBindableElement\n{\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"UnityMvvmToolkit.Generator\", \"1.0.0.0\")]\n    private global::UnityMvvmToolkit.Core.PropertyBindingData? _imageBindingData;\n\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"UnityMvvmToolkit.Generator\", \"1.0.0.0\")]\n    [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]\n    public void SetBindingContext(global::UnityMvvmToolkit.Core.Interfaces.IBindingContext context,\n        global::UnityMvvmToolkit.Core.Interfaces.IObjectProvider objectProvider)\n    {\n        BeforeSetBindingContext(context, objectProvider);\n\n        if (string.IsNullOrWhiteSpace(BindingImagePath) == false)\n        {\n            _imageBindingData ??=\n                global::UnityMvvmToolkit.Core.Extensions.StringExtensions.ToPropertyBindingData(BindingImagePath!);\n            _imageProperty = objectProvider.RentReadOnlyProperty\u003cglobal::UnityEngine.Texture2D\u003e(context, _imageBindingData!);\n            _imageProperty!.ValueChanged += OnImagePropertyValueChanged;\n        }\n\n        AfterSetBindingContext(context, objectProvider);\n    }\n\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"UnityMvvmToolkit.Generator\", \"1.0.0.0\")]\n    [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]\n    public void ResetBindingContext(global::UnityMvvmToolkit.Core.Interfaces.IObjectProvider objectProvider)\n    {\n        BeforeResetBindingContext(objectProvider);\n\n        if (_imageProperty != null)\n        {\n            _imageProperty!.ValueChanged -= OnImagePropertyValueChanged;\n            objectProvider.ReturnReadOnlyProperty(_imageProperty);\n            _imageProperty = null;\n        }\n\n        AfterResetBindingContext(objectProvider);\n    }\n\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"UnityMvvmToolkit.Generator\", \"1.0.0.0\")]\n    [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]\n    private void OnImagePropertyValueChanged(object sender, global::UnityEngine.Texture2D value)\n    {\n        OnImagePropertyValueChanged(value);\n    }\n\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"UnityMvvmToolkit.Generator\", \"1.0.0.0\")]\n    partial void BeforeSetBindingContext(global::UnityMvvmToolkit.Core.Interfaces.IBindingContext context,\n        global::UnityMvvmToolkit.Core.Interfaces.IObjectProvider objectProvider);\n\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"UnityMvvmToolkit.Generator\", \"1.0.0.0\")]\n    partial void AfterSetBindingContext(global::UnityMvvmToolkit.Core.Interfaces.IBindingContext context,\n        global::UnityMvvmToolkit.Core.Interfaces.IObjectProvider objectProvider);\n\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"UnityMvvmToolkit.Generator\", \"1.0.0.0\")]\n    partial void BeforeResetBindingContext(global::UnityMvvmToolkit.Core.Interfaces.IObjectProvider objectProvider);\n\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"UnityMvvmToolkit.Generator\", \"1.0.0.0\")]\n    partial void AfterResetBindingContext(global::UnityMvvmToolkit.Core.Interfaces.IObjectProvider objectProvider);\n\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"UnityMvvmToolkit.Generator\", \"1.0.0.0\")]\n    partial void OnImagePropertyValueChanged(global::UnityEngine.Texture2D value);\n}\n```\n\n`BindableImage.Uxml.g.cs`\n\n```csharp\npartial class BindableImage\n{\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"UnityMvvmToolkit.Generator\", \"1.0.0.0\")]\n    [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]\n    private string BindingImagePath { get; set; }\n\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"UnityMvvmToolkit.Generator\", \"1.0.0.0\")]\n    public new class UxmlFactory : global::UnityEngine.UIElements.UxmlFactory\u003cBindableImage, UxmlTraits\u003e\n    {\n    }\n\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"UnityMvvmToolkit.Generator\", \"1.0.0.0\")]\n    public new class UxmlTraits : global::BindableUIElements.Image.UxmlTraits\n    {\n        [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"UnityMvvmToolkit.Generator\", \"1.0.0.0\")]\n        private readonly global::UnityEngine.UIElements.UxmlStringAttributeDescription _bindingImagePath = new() \n            { name = \"binding-image-path\", defaultValue = \"\" };\n\n        [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"UnityMvvmToolkit.Generator\", \"1.0.0.0\")]\n        [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]\n        public override void Init(global::UnityEngine.UIElements.VisualElement visualElement, \n            global::UnityEngine.UIElements.IUxmlAttributes bag, \n            global::UnityEngine.UIElements.CreationContext context)\n        {\n            base.Init(visualElement, bag, context);\n\n            var control = (BindableImage) visualElement;\n            control.BindingImagePath = _bindingImagePath.GetValueFromBag(bag, context);\n        }\n    }\n}\n```\n\n\u003c/details\u003e\n\nAs you can see, using [UnityUxmlGenerator](https://github.com/LibraStack/UnityUxmlGenerator) and [UnityMvvmToolkit.Generator](https://github.com/LibraStack/UnityMvvmToolkit.Generator) we can achieve the same results but with just a few lines of code.\n\n\u003e **Note:** The [UnityMvvmToolkit.Generator](https://github.com/LibraStack/UnityMvvmToolkit.Generator) is available exclusively for my [patrons](https://patreon.com/DimaChebanov).\n  \n## :link: External Assets\n\n### UniTask\n\nTo enable [async commands](#asynccommand--asynccommandt) support, you need to add the [UniTask](https://github.com/Cysharp/UniTask) package to your project.\n\nIn addition to async commands **UnityMvvmToolkit** provides extensions to make [USS transition](https://docs.unity3d.com/Manual/UIE-Transitions.html)'s awaitable.\n\nFor example, your `VisualElement` has the following transitions.\n```css\n.panel--animation {\n    transition-property: opacity, padding-bottom;\n    transition-duration: 65ms, 150ms;\n}\n```\n\nYou can `await` these transitions using several methods.\n```csharp\npublic async UniTask DeactivatePanel()\n{\n    try\n    {\n        panel.style.opacity = 0;\n        panel.style.paddingBottom = 0;\n        \n        // Await for the 'opacity' || 'paddingBottom' to end or cancel.\n        await panel.WaitForAnyTransitionEnd();\n        \n        // Await for the 'opacity' \u0026 'paddingBottom' to end or cancel.\n        await panel.WaitForAllTransitionsEnd();\n        \n        // Await 150ms.\n        await panel.WaitForLongestTransitionEnd();\n\n        // Await 65ms.\n        await panel.WaitForTransitionEnd(0);\n        \n        // Await for the 'paddingBottom' to end or cancel.\n        await panel.WaitForTransitionEnd(new StylePropertyName(\"padding-bottom\"));\n        \n        // Await for the 'paddingBottom' to end or cancel.\n        // Uses ReadOnlySpan to match property names to avoid memory allocation.\n        await panel.WaitForTransitionEnd(nameof(panel.style.paddingBottom));\n        \n        // Await for the 'opacity' || 'paddingBottom' to end or cancel.\n        // You can write your own transition predicates, just implement a 'ITransitionPredicate' interface.\n        await panel.WaitForTransitionEnd(new TransitionAnyPredicate());\n    }\n    finally\n    {\n        panel.visible = false;\n    }\n}\n```\n\n\u003e **Note:** All transition extensions have a `timeoutMs` parameter (default value is `2500ms`).\n\n## :rocket: Performance\n\n### Memory allocation\n\nThe **UnityMvvmToolkit** uses object pools under the hood and reuses created objects. You can warm up certain objects in advance to avoid allocations during execution time.\n\n```csharp\npublic abstract class BaseView\u003cTBindingContext\u003e : DocumentView\u003cTBindingContext\u003e\n        where TBindingContext : class, IBindingContext\n{\n    protected override IObjectProvider GetObjectProvider()\n    {\n        return new BindingContextObjectProvider(new IValueConverter[] { new IntToStrConverter() })\n            // Finds and warmups all classes from calling assembly that implement IBindingContext.\n            .WarmupAssemblyViewModels()\n            // Finds and warmups all classes from certain assembly that implement IBindingContext.\n            .WarmupAssemblyViewModels(Assembly.GetExecutingAssembly())\n            // Warmups a certain class.\n            .WarmupViewModel\u003cCounterViewModel\u003e()\n            // Warmups a certain class.\n            .WarmupViewModel(typeof(CounterViewModel))\n            // Creates 5 instances to rent 'IProperty\u003cstring\u003e' without any allocations.\n            .WarmupValueConverter\u003cIntToStrConverter\u003e(5);\n    }\n}\n```\n\n## :bookmark_tabs: Contributing\n\nYou may contribute in several ways like creating new features, fixing bugs or improving documentation and examples.\n\n### Discussions\n\nUse [discussions](https://github.com/ChebanovDD/UnityMvvmToolkit/discussions) to have conversations and post answers without opening issues.\n\nDiscussions is a place to:\n* Share ideas\n* Ask questions\n* Engage with other community members\n\n### Report a bug\n\nIf you find a bug in the source code, please [create bug report](https://github.com/ChebanovDD/UnityMvvmToolkit/issues/new?assignees=ChebanovDD\u0026labels=bug\u0026template=bug_report.md\u0026title=).\n\n\u003e Please browse [existing issues](https://github.com/ChebanovDD/UnityMvvmToolkit/issues) to see whether a bug has previously been reported.\n\n### Request a feature\n\nIf you have an idea, or you're missing a capability that would make development easier, please [submit feature request](https://github.com/ChebanovDD/UnityMvvmToolkit/issues/new?assignees=ChebanovDD\u0026labels=enhancement\u0026template=feature_request.md\u0026title=).\n\n\u003e If a similar feature request already exists, don't forget to leave a \"+1\" or add additional information, such as your thoughts and vision about the feature.\n\n### Show your support\n\nGive a :star: if this project helped you!\n\n\u003ca href=\"https://www.buymeacoffee.com/chebanovdd\" target=\"_blank\"\u003e\u003cimg src=\"https://cdn.buymeacoffee.com/buttons/v2/default-orange.png\" alt=\"Buy Me A Coffee\" style=\"height: 60px !important;width: 217px !important;\" \u003e\u003c/a\u003e\n\n## :balance_scale: License\n\nUsage is provided under the [MIT License](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FLibraStack%2FUnityMvvmToolkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FLibraStack%2FUnityMvvmToolkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FLibraStack%2FUnityMvvmToolkit/lists"}