{"id":13691611,"url":"https://github.com/Keflon/FunctionZero.Maui.Controls","last_synced_at":"2025-05-02T15:32:41.932Z","repository":{"id":50672245,"uuid":"507918876","full_name":"Keflon/FunctionZero.Maui.Controls","owner":"Keflon","description":"Virtualising TreeView and ListView","archived":false,"fork":false,"pushed_at":"2024-03-31T15:52:43.000Z","size":1091,"stargazers_count":97,"open_issues_count":1,"forks_count":6,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-05T15:47:17.909Z","etag":null,"topics":["csharp"],"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/Keflon.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"License.md","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},"funding":{"github":"Keflon"}},"created_at":"2022-06-27T13:23:25.000Z","updated_at":"2024-12-20T03:59:28.000Z","dependencies_parsed_at":"2023-01-18T18:39:42.792Z","dependency_job_id":"ff52f991-72f3-4834-a34d-bd09aa0291cd","html_url":"https://github.com/Keflon/FunctionZero.Maui.Controls","commit_stats":{"total_commits":83,"total_committers":3,"mean_commits":"27.666666666666668","dds":"0.20481927710843373","last_synced_commit":"eb81b089412391c63bc49aa85e18ebff617be839"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Keflon%2FFunctionZero.Maui.Controls","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Keflon%2FFunctionZero.Maui.Controls/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Keflon%2FFunctionZero.Maui.Controls/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Keflon%2FFunctionZero.Maui.Controls/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Keflon","download_url":"https://codeload.github.com/Keflon/FunctionZero.Maui.Controls/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252063123,"owners_count":21688652,"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":["csharp"],"created_at":"2024-08-02T17:00:47.437Z","updated_at":"2025-05-02T15:32:38.572Z","avatar_url":"https://github.com/Keflon.png","language":"C#","funding_links":["https://github.com/sponsors/Keflon"],"categories":["Components","UI"],"sub_categories":[],"readme":"# Latest\n### .NET 8 support. Nuget [here](https://www.nuget.org/packages/FunctionZero.Maui.Controls)  \nUse package [8.0.0](https://www.nuget.org/packages/FunctionZero.Maui.Controls/8.0.0-pre1) and above if you are building against .NET 8.  \nUse package [2.3.4-pre2](https://www.nuget.org/packages/FunctionZero.Maui.Controls/2.3.4-pre2) if you are building against .NET 7.  \nUse package [2.0.0](https://www.nuget.org/packages/FunctionZero.Maui.Controls/2.0.0) if you are building against .NET 6.  \n# Workarounds  \nIf you're having trouble with the MAUI `TabbedPage` or `FlyoutPage` see [below](#workarounds)\n\n# Controls\n[NuGet package](https://www.nuget.org/packages/FunctionZero.Maui.Controls)  \n\n1. [ListViewZero](#listviewzero)\n1. [TreeViewZero](#treeviewzero)\n1. [MaskViewZero](#maskviewzero)\n\n## ListViewZero\n### Features\n- A fully virtualising list-view that doesn't [leak memory](https://github.com/dotnet/maui/issues/8151) or [enforce arbitrary item spacing](https://github.com/dotnet/maui/issues/4520).\n- Very high performance\n- All rendering uses cross-platform code\n\nIf you can use a `CollectionView` or a `ListView` you will have no trouble with a `ListViewZero`  \n\nTODO: Sample image  \n\n### ListViewZero exposes the following properties\nProperty | Type | Bindable | Purpose\n:----- | :---- | :----:      | :-----\nItemContainerStyle           | Style            | Yes  | An optional `Style` that can be applied to the `ListItemZero` instances that represent each node. This can be used to modify how selected-items are rendered.\nItemHeight                   | float            | Yes  | The height of each row in the list-view\nItemsSource                  | object           | Yes  | Set this to the IEnumerable (usually found in your view-model) that contains your items  \nItemTemplate                 | DataTemplate     | Yes  | Used to draw the data for each node. Set this to a `DataTemplate` or a `DataTemplateSelector`. See below.\nScrollOffset                 | float            | YES! | This is the absolute offset and can bound to.\nSelectedItem                 | object           | Yes  | Set to the currently selected item, i.e. an instance of your *ViewModel* data, or null\nSelectedItems                | IList            | Yes  | All currently selected items. Default is an `ObservableCollection\u003cobject\u003e`. You can bind to it or set your own collection, and if it supports `INotifyCollectionChanged` the `ListViewZero` will track it.\nSelectionMode                | SelectionMode    | Yes  | Allows a `SelectionMode` of None, Single or Multiple.\nRemainingItems               | int              | Yes  | This tracks the number of items in the `ItemsSource` that are below the bottom of the `ListViewZero`.\nRemainingItemsChangedCommand | ICommand         | Yes  | This is raised whenever `RemainingItems` changes. The _command parameter_ is set to `RemainingItems`.\n\n### Create a ListViewZero\nGiven a collection of items\n```csharp\npublic IEnumerable\u003cPerson\u003e ListData { get; }\n```\nAdd the namespace:\n```xml\nxmlns:cz=\"clr-namespace:FunctionZero.Maui.Controls;assembly=FunctionZero.Maui.Controls\"\n```\nThen declare a `ListViewZero` like this:\n```xml\n\u003c!--Tip: A generous ItemHeight ensures the items aren't too small to tap with your finger--\u003e\n\u003ccz:ListViewZero \n    ItemsSource=\"{Binding SampleListData}\"\n    ItemHeight=\"40\"\n\n    ... the rest are optional ...\n\n    SelectedItem=\"{Binding SelectedItem}\"\n    SelectedItems=\"{Binding SelectedItems}\"\n    SelectionMode=\"Multiple\"\n    \u003e\n    \u003ccz:ListViewZero.ItemTemplate\u003e\n        \u003cDataTemplate\u003e\n            \u003cLabel Text=\"{Binding Name}\" /\u003e\n        \u003c/DataTemplate\u003e\n    \u003c/cz:ListViewZero.ItemTemplate\u003e\n\u003c/cz:ListViewZero\u003e\n```\n\n### Tracking changes in the data\nIf the ItemsSource supports `INotifyCollectionChanged`, the list-view will track all changes automatically. E.g.  \n```csharp\npublic ObservableCollection\u003cPerson\u003e ListData { get; }\n```\nIf the properties on your items support `INotifyPropertyChanged` then they too will be tracked.  \n\nFor example, `ListViewZero` will track changes to `Name` property on the following node:\n```csharp\npublic class Person : BaseClassWithInpc\n{\n   private string _name;\n   public string Name\n   {\n      get =\u003e _name;\n      set =\u003e SetProperty(ref _name, value);\n   }\n}\n```\n\n### SelectionMode\nSimilar to the `CollectionView`, allowed values are *None, Single or Multiple*. You can change this property at runtime, e.g. via `Binding`  \n\n### SelectedItem / SelectedItems\n`SelectedItem` tracks the currently selected item, and can be databound to your ViewModel  \n\n`SelectedItems` defaults to an `ObservableCollection` and tracks all items whose `IsSelected` property is true. The default `BindingMode` is `TwoWay`  \nIn your view-model you can bind to the default collection (BindingMode OneWayToSource) or replace it  with your own collection (BindingMode OneWay or TwoWay)  \nThe `ListViewZero` will maintain the contents of the collection for you, and you can modify the collection from your view-model to programatically select items\n\n## Styling SelectedItems\nYou can replace this styling by setting the `ItemContainerStyle` property on your `ListViewZero`  \n~~Selected items are rendered using a VisualStateManager and 3 of the 4 *CommonStates*~~  \nSelected items are rendered using a VisualStateManager and the following states  \n\nCommon State | Description | IsSelected | IsPrimary | SelectionMode\n:-----       | :----                                             | :---- | :---- | :----\nNormal       | The ListViewItem is not selected                  | False | False | Any\nItemFocused  | The ListViewItem is the primary-selection         | True  | True  | Single or Multiple\nSelected     | The ListViewItem is selected but not the primary  | True  | False | Multiple\nDisabled     | Not used                                          | n/a   | n/a   | n/a\n\nThis is the default `Style` used to modify the `BackgroundColor` of selected items, and can serve as a baseline for your own  \n```xml\n\u003cStyle x:Key=\"testItemStyle\" TargetType=\"cz:ListItemZero\"\u003e\n    \u003cSetter Property=\"VisualStateManager.VisualStateGroups\" \u003e\n        \u003cVisualStateGroupList\u003e\n            \u003cVisualStateGroup x:Name=\"CommonStates\"\u003e\n\n                \u003c!-- BackgroundColor must have a value set or the other states cannot 'put back' the original color --\u003e\n                \u003c!-- I *think* this is due to a bug in MAUI because unappyling a Setter ought to revert to the original value or default --\u003e\n                \u003cVisualState x:Name=\"Normal\" \u003e\n                    \u003cVisualState.Setters\u003e\n                 \u003cSetter Property=\"BackgroundColor\" Value=\"Transparent\" /\u003e\n                    \u003c/VisualState.Setters\u003e\n                \u003c/VisualState\u003e\n\n                \u003cVisualState x:Name=\"ItemFocused\"\u003e\n                    \u003cVisualState.Setters\u003e\n                        \u003cSetter Property=\"BackgroundColor\" Value=\"Cyan\" /\u003e\n                    \u003c/VisualState.Setters\u003e\n                \u003c/VisualState\u003e\n\n                \u003cVisualState x:Name=\"Selected\"\u003e\n                    \u003cVisualState.Setters\u003e\n                        \u003cSetter Property=\"BackgroundColor\" Value=\"AliceBlue\" /\u003e\n                    \u003c/VisualState.Setters\u003e\n                \u003c/VisualState\u003e\n\n            \u003c/VisualStateGroup\u003e\n        \u003c/VisualStateGroupList\u003e\n    \u003c/Setter\u003e\n\u003c/Style\u003e\n```\nAnd set it like this:\n```xml\n\u003ccz:ListViewZero \n    SelectionMode=\"Multiple\"\n    ItemContainerStyle=\"{StaticResource testItemStyle}\"\n    ...\n```\n\n## TreeViewZero\n![Sample image](https://github.com/Keflon/FunctionZero.Maui.Controls/blob/master/AndroidTree.png?raw=true)\n\nThis control allows you to visualise a tree of any data. Each *trunk* node must provide its children using a public property that supports the IEnumerable interface.  \nIf the children are in a collection that supports `INotifyCollectionChanged` the control will track changes to the underlying tree data.  \nChildren are lazy-loaded and the UI is virtualised.  \n\n### TreeViewZero exposes the following properties\nProperty | Type | Bindable | Purpose\n:----- | :---- | :----: | :-----\nIndentMultiplier        | double           | Yes (OneTime) | How far the TreeNode should be indented for each nest level. Default is 15.0\nIsRootVisible           | bool             | Yes  | Specifies whether the root node should be shown or omitted.\nItemContainerStyle      | Style            | Yes  | An optional `Style` that can be applied to the `ListItemZero` instances that represent each node. This can be used to modify how selected-items are rendered.\nItemHeight              | float            | Yes  | The height of each row in the tree-view\nItemsSource             | object           | Yes  | Set this to your root node  \nScrollOffset            | float            | YES! | This is the absolute offset and can bound to\nSelectedItem            | object           | Yes  | Set to the currently selected item, i.e. an instance of your *ViewModel* data, or null\nSelectedItems           | IList            | Yes  | All currently selected items. Default is an `ObservableCollection\u003cobject\u003e`. You can bind to it or set your own collection, and if it supports `INotifyCollectionChanged` the `TreeViewZero` will track it.\nSelectionMode           | SelectionMode    | Yes  | Alloows a `SelectionMode` of None, Single or Multiple.\nTreeChildren            | IEnumerable      | No   | This is exposed for future capabilities and exposes all items *potentially* visible in the viewport.\nTreeItemControlTemplate | ControlTemplate  | Yes  | Alows you to replace the default `ControlTemplate` used to render each node\nTreeItemTemplate        | TemplateProvider | Yes  | Used to draw the data for each node. Set this to a `TreeItemDataTemplate` or a `TreeItemDataTemplateSelector`. See below.\n\n### TreeItemDataTemplate\n`TreeItemDataTemplate` tells a tree-node how to draw its content, how to get its children and whether it should bind `IsExpanded` to the underlying data.  \nIt declares the following properties:\n\nProperty | Type | Purpose\n:----- | :---- | :-----\nChildrenPropertyName    | string       | The name of the property used to find the node children\nIsExpandedPropertyName  | string       | The name of the property used to store whether the node is expanded\nItemTemplate            | DataTemplate | The DataTemplate used to draw this node\nTargetType              | Type         | When used in a `TreeItemDataTemplateSelector`, identifies the least-derived nodes the ItemTemplate can be applied to.\n\n### Create a TreeViewZero\n\nGiven a hierarchy of `MyNode`\n```csharp\npublic class MyNode\n{\n   public string Name { get; set;}\n   public IEnumerable\u003cMyNode\u003e MyNodeChildren { get; set; }\n}\n```\n\nAdd the namespace:\n```xml\nxmlns:cz=\"clr-namespace:FunctionZero.Maui.Controls;assembly=FunctionZero.Maui.Controls\"\n```\n\nThen declare a `TreeViewZero` like this:\n```xml\n\u003c!--Tip: A generous ItemHeight ensures the chevrons aren't too small to tap with your finger--\u003e\n\u003ccz:TreeViewZero ItemsSource=\"{Binding RootNode}\" ItemHeight=\"100\"\u003e\n    \u003ccz:TreeViewZero.TreeItemTemplate\u003e\n        \u003ccz:TreeItemDataTemplate ChildrenPropertyName=\"MyNodeChildren\"\u003e\n            \u003cDataTemplate\u003e\n                \u003cLabel Text=\"{Binding Name}\" /\u003e\n            \u003c/DataTemplate\u003e\n        \u003c/cz:TreeItemDataTemplate\u003e\n    \u003c/cz:TreeViewZero.TreeItemTemplate\u003e\n\u003c/cz:TreeViewZero\u003e\n```\n\n## Tracking changes in the data\nIf the children of a node support `INotifyCollectionChanged`, the TreeView will track all changes automatically.  \nIf the properties on your node support `INotifyPropertyChanged` then they too will be tracked.  \n\nFor example, TreeViewZero will track changes to `Name`, `IsExpanded` and any \nmodifications to the `Children` collection on the following node:\n```csharp\npublic class MyObservableNode : BaseClassWithInpc\n{\n   private string _name;\n   public string Name\n   {\n      get =\u003e _name;\n      set =\u003e SetProperty(ref _name, value);\n   }\n\n   private bool _isMyNodeExpanded;\n   public bool IsMyNodeExpanded\n   {\n      get =\u003e _isMyNodeExpanded;\n      set =\u003e SetProperty(ref _isMyNodeExpanded, value);\n   }\n\n   public ObservableCollection\u003cMyObservableNode\u003e Children {get; set;}\n}\n```\nThis is how to bind the `IsMyNodeExpanded` from our data, to `IsExpanded` on the TreeNode ...\n\n```xml\n\u003ccz:TreeViewZero.TreeItemTemplate\u003e\n    \u003ccz:TreeItemDataTemplate ChildrenPropertyName=\"Children\" IsExpandedPropertyName=\"IsMyNodeExpanded\"\u003e\n        \u003cDataTemplate\u003e\n            ...\n        \u003c/DataTemplate\u003e\n    \u003c/cz:TreeItemDataTemplate\u003e\n\u003c/cz:TreeViewZero.TreeItemTemplate\u003e\n```\n\n## SelectionMode\nThe `TreeViewZero` allows selection modes *None, Single or Multiple*.\nPlease see the [ListViewZero](#selecteditem-selecteditems) docs for how to use the SelectionMode property.\n\n## Styling SelectedItems\n\nUse this to style each tree-node, e.g. to change how selected items are rendered.  \nSee [Styling SelectedItems](#styling-selecteditems) on the `ListViewZero` for details, or use the following as a guide:  \n\n```xml\n\u003cStyle x:Key=\"testItemStyle\" TargetType=\"cz:ListItemZero\"\u003e\n    \u003cSetter Property=\"VisualStateManager.VisualStateGroups\" \u003e\n        \u003cVisualStateGroupList\u003e\n            \u003cVisualStateGroup x:Name=\"CommonStates\"\u003e\n\n                \u003c!-- BackgroundColor must have a value set or the other states cannot 'put back' the original color --\u003e\n                \u003c!-- I *think* this is due to a bug in MAUI because unappyling a Setter ought to revert to the original value or default --\u003e\n                \u003cVisualState x:Name=\"Normal\" \u003e\n                    \u003cVisualState.Setters\u003e\n                 \u003cSetter Property=\"BackgroundColor\" Value=\"Transparent\" /\u003e\n                    \u003c/VisualState.Setters\u003e\n                \u003c/VisualState\u003e\n\n                \u003cVisualState x:Name=\"ItemFocused\"\u003e\n                    \u003cVisualState.Setters\u003e\n                        \u003cSetter Property=\"BackgroundColor\" Value=\"Cyan\" /\u003e\n                    \u003c/VisualState.Setters\u003e\n                \u003c/VisualState\u003e\n\n                \u003cVisualState x:Name=\"Selected\"\u003e\n                    \u003cVisualState.Setters\u003e\n                        \u003cSetter Property=\"BackgroundColor\" Value=\"AliceBlue\" /\u003e\n                    \u003c/VisualState.Setters\u003e\n                \u003c/VisualState\u003e\n\n            \u003c/VisualStateGroup\u003e\n        \u003c/VisualStateGroupList\u003e\n    \u003c/Setter\u003e\n\u003c/Style\u003e\n```\nAnd set it like this:\n```xml\n\u003ccz:TreeViewZero \n    SelectionMode=\"Multiple\"\n    ItemContainerStyle=\"{StaticResource testItemStyle}\"\n```\n\n\n### TreeItemDataTemplateSelector\nIf your tree of data consists of disparate nodes with different properties for their `Children`, \nuse a `TreeItemDataTemplateSelector` and set `TargetType` for each `TreeItemDataTemplate`.  \n\nNote: In this example, the tree data can contain nodes of type `LevelZero`, `LevelOne` and `LevelTwo` where each type has a different property to provide its children.  \n\nThe first `TargetType` your data-node can be assigned to is used. Put another way, the first `TargetType` the data-node can be cast to, wins.\n```xml\n\u003ccz:TreeViewZero ItemsSource=\"{Binding SampleTemplateTestData}\" ItemHeight=\"20\" \u003e\n    \u003ccz:TreeViewZero.TreeItemTemplate\u003e\n        \u003ccz:TreeItemDataTemplateSelector\u003e\n            \u003ccz:TreeItemDataTemplate ChildrenPropertyName=\"LevelZeroChildren\" TargetType=\"{x:Type test:LevelZero}\" IsExpandedPropertyName=\"IsLevelZeroExpanded\"\u003e\n                \u003cDataTemplate\u003e\n                    \u003cLabel Text=\"{Binding Name}\" BackgroundColor=\"Yellow\" /\u003e\n                \u003c/DataTemplate\u003e\n            \u003c/cz:TreeItemDataTemplate\u003e\n\n            \u003ccz:TreeItemDataTemplate ChildrenPropertyName=\"LevelOneChildren\" TargetType=\"{x:Type test:LevelOne}\" IsExpandedPropertyName=\"IsLevelOneExpanded\"\u003e\n                \u003cDataTemplate\u003e\n                    \u003cLabel Text=\"{Binding Name}\" BackgroundColor=\"Cyan\" /\u003e\n                \u003c/DataTemplate\u003e\n            \u003c/cz:TreeItemDataTemplate\u003e\n\n            \u003ccz:TreeItemDataTemplate ChildrenPropertyName=\"LevelTwoChildren\" TargetType=\"{x:Type test:LevelTwo}\" IsExpandedPropertyName=\"IsLevelTwoExpanded\"\u003e\n                \u003cDataTemplate\u003e\n                    \u003cLabel Text=\"{Binding Name}\" BackgroundColor=\"Pink\" /\u003e\n                \u003c/DataTemplate\u003e\n            \u003c/cz:TreeItemDataTemplate\u003e\n\n            \u003ccz:TreeItemDataTemplate ChildrenPropertyName=\"LevelThreeChildren\" TargetType=\"{x:Type test:LevelThree}\" IsExpandedPropertyName=\"IsLevelThreeExpanded\"\u003e\n                \u003cDataTemplate\u003e\n                    \u003cLabel Text=\"{Binding Name}\" BackgroundColor=\"Crimson\" /\u003e\n                \u003c/DataTemplate\u003e\n            \u003c/cz:TreeItemDataTemplate\u003e\n        \u003c/cz:TreeItemDataTemplateSelector\u003e\n    \u003c/cz:TreeViewZero.TreeItemTemplate\u003e\n\u003c/cz:TreeViewZero\u003e\n```\n### Customising TreeItemDataTemplateSelector\nIf you want **full-control** over the `TreeItemTemplate` per node, you can easily implement your own \n`TreeItemDataTemplateSelector` and override `OnSelectTemplateProvider`. Here's an example that chooses a template \nbased on whether the node has children or not:\n\n```csharp\npublic class MyTreeItemDataTemplateSelector : TemplateProvider\n{\n    /// These should be set in the xaml markup. (or code-behind, if that's how you roll)\n    public TreeItemDataTemplate TrunkTemplate{ get; set; }\n    public TreeItemDataTemplate LeafTemplate{ get; set; }\n\n    public override TreeItemDataTemplate OnSelectTemplateProvider(object item)\n    {\n        if(item is MyTreeNode mtn)\n            if((mtn.Children != null) \u0026\u0026 (mtn.Children.Count != 0))\n                return TrunkTemplate;\n        \n        return LeafTemplate;\n    }\n}\n```\nTake a look at [TreeItemDataTemplateSelector.cs](https://github.com/Keflon/FunctionZero.Maui.Controls/blob/master/FunctionZero.Maui.Controls/TreeItemDataTemplateSelector.cs) \nfor an example of how to provide a *collection* of `TreeItemDataTemplate` instances to your TemplateProvider.\n\n## Drawing your own tree-nodes\nDo this if you want to change the way the whole tree-node is drawn, e.g. to replace the *chevron*. \nIt is a two-step process.\n1. Create a `ControlTemplate` for the node\n1. Apply it to the `TreeViewZero`\n\nThe *templated parent* for the `ControlTemplate` is a `ListItemZero`. It exposes these properties:  \n\n\nProperty    | Type   | Purpose\n:----- | :----: | :-----\nIsPrimary    | bool         | If selection is allowed, this tracks the current `SelectedItem`\nIsSelected   | bool         | If the current item is selected, this is true. Note we can have multiple items *selected*, but only one *SelectedItem*\nItemIndex    | int          | For internal use when managing the cache\nItemTemplate | DataTemplate | The `DataTemplate` used to generate the `ListViewItem` Content\n\nThe `BindingContext` of the *templated parent* is a [TreeNodeContainer](https://github.com/Keflon/FunctionZero.TreeListItemsSourceZero) and includes the following properties: \n\nProperty    | Type   | Purpose\n:----- | :----: | :-----\nIndent      | int    | How deep the node should be indented. It is equal to `NestLevel`, or `NestLevel-1` if the Tree Root is not shown.\nNestLevel   | int    | The depth of the node in the data.\nIsExpanded  | bool   | This property reflects whether the TreeNode is expanded.\nShowChevron | bool   | Whether the chevron is drawn. True if the node has children.\nData        | object | This is the tree-node data for this TreeNodeZero instance, i.e. your data!\n\n\n\n### Step 1 - Create a `ControlTemplate` ...\n\nYou can base the `ControlTemplate` on the default, show here, or bake your own entirely.  \n```xml\n\u003cControlTemplate x:Key=\"defaultControlTemplate\"\u003e\n    \u003cHorizontalStackLayout HeightRequest=\"{Binding Height, Mode=OneWay, Source={x:Reference tcp}}\" \u003e\n        \u003c!--\n        The ControlTemplate sets the TreeNodeSpacer BindingContext to \"{Binding Source={RelativeSource TemplatedParent}}\" for us\n        i.e. a ListItemZero.\n        The TreeNodeSpacer walks up the visual-tree to find the containing TreeViewZero, to get the IndentMultiplier.\n        It then sets its WidthRequest to the IndentMultiplier * (ListItemZero.BindingContext.Indent - 1)\n        --\u003e\n        \u003ccz:TreeNodeSpacer /\u003e\n\n        \u003ccz:Chevron\n            IsExpanded=\"{TemplateBinding BindingContext.IsExpanded, Mode=TwoWay}\" \n            ShowChevron=\"{TemplateBinding BindingContext.ShowChevron, Mode=TwoWay}\" \n            /\u003e\n        \u003c!--This is simply a ContentPresenter that allows us to specify a BindingContext for the Content--\u003e\n        \u003ccz:TreeContentPresenter \n            VerticalOptions=\"Fill\" \n            x:Name=\"tcp\" \n            HorizontalOptions=\"Fill\"   \n            BindingContext=\"{TemplateBinding BindingContext.Data, Mode=OneWay}\"\n            /\u003e\n    \u003c/HorizontalStackLayout\u003e\n\u003c/ControlTemplate\u003e\n```\n \n\n### Step 2 - give it to the TreeView ...\n```xml\n\u003ccz:TreeViewZero\n    ItemsSource=\"{Binding SampleData}\" \n    IndentMultiplier=\"20\" \n    TreeItemControlTemplate=\"{StaticResource MyTreeItemControlTemplate}\"\n    ItemHeight=\"60\"\n    \u003e\n    \u003ccz:TreeViewZero.TreeItemTemplate\u003e\n        \u003ccz:TreeItemDataTemplate ChildrenPropertyName=\"Children\" IsExpandedPropertyName=\"IsDataExpanded\"\u003e\n            \u003cDataTemplate\u003e\n                \u003cLabel Text=\"{Binding Name}\" BackgroundColor=\"Pink\" /\u003e\n            \u003c/DataTemplate\u003e\n        \u003c/cz:TreeItemDataTemplate\u003e\n    \u003c/cz:TreeViewZero.TreeItemTemplate\u003e\n\u003c/cz:TreeViewZero\u003e\n```\n\n\n## MaskViewZero\nThere's a cool new control for masking out areas of the screen.  \nIt's really boring writing documentation so here's a quick sample whilst I finish the control off.\n\n### Put your UI inside a `MaskZero` control, e.g. using a `ControlTemplate` ...\n```xaml\n\u003cContentPage.ControlTemplate\u003e\n    \u003cControlTemplate\u003e\n        \u003ccz:MaskZero \n            BackgroundAlpha=\"0.5\" \n            MaskTargetName=\"{TemplateBinding BindingContext.TargetName}\"\n            MovementEasing=\"{x:Static Easing.CubicInOut}\"\n            MaskRoundnessEasing=\"{x:Static Easing.CubicInOut}\"\n            Duration=\"450\"\n            MaskColorRequest=\"{TemplateBinding BindingContext.MaskColor}\"\n            MaskEdgeColorRequest=\"{TemplateBinding BindingContext.MaskEdgeColor}\"\n            \u003e\n            \u003ccz:MaskZero.Content\u003e\n                \u003cContentPresenter/\u003e\n            \u003c/cz:MaskZero.Content\u003e\n        \u003c/cz:MaskZero\u003e\n    \u003c/ControlTemplate\u003e\n\u003c/ContentPage.ControlTemplate\u003e\n```\nNotice we are binding to the control's `~Request` properties. \nThis means any changes will be animated, using the `Easing` functions you provide.  \n\nNow give some of your controls a `MaskZero.MaskName`  \n```xaml\n\u003cGrid xmlns:cz=\"clr-namespace:FunctionZero.Maui.Controls;assembly=FunctionZero.Maui.Controls\"\n      RowDefinitions=\"*,*\" ColumnDefinitions=\"*,*\" \u003e\n    \u003cLabel Grid.Row=\"0\" Grid.Column=\"0\" Text=\"Banana!\"     cz:MaskZero.MaskName=\"banana\" /\u003e\n    \u003cLabel Grid.Row=\"0\" Grid.Column=\"1\" Text=\"Radish!\"     cz:MaskZero.MaskName=\"radish\" /\u003e\n    \u003cLabel Grid.Row=\"1\" Grid.Column=\"0\" Text=\"Melon!\"      cz:MaskZero.MaskName=\"melon\" /\u003e\n    \u003cLabel Grid.Row=\"1\" Grid.Column=\"1\" Text=\"Grapefruit!\" cz:MaskZero.MaskName=\"grapefruit\" /\u003e\n\u003c/Grid\u003e\n```\n\n\nFinally, add to your `ViewModel` the properties the `ControlTemplate` binds to, and set them, simple as that!  \n```csharp \nprivate async Task DoTheThingAsync()\n{\n    while (true)\n    {\n        await Task.Delay(2000);\n\n        TargetName = \"banana\";\n        MaskColor = Colors.Red;\n        MaskEdgeColor = Colors.Black;\n        await Task.Delay(2000);\n\n        TargetName = \"radish\";\n        MaskColor = Colors.Purple;\n        MaskEdgeColor = Colors.Black;\n        await Task.Delay(2000);\n\n        TargetName = \"melon\";\n        MaskColor = Colors.Blue;\n        MaskEdgeColor = Colors.Red;\n        await Task.Delay(2000);\n\n        TargetName = \"grapefruit\";\n        MaskColor = Colors.Yellow;\n        MaskEdgeColor = Colors.Black;\n    }\n}\n```\n\nRun the demo to see different controls highlighted, with animated color, shape and opacity changes. Code is here:\n- [CircleMaskPage.xaml](https://github.com/Keflon/FunctionZero.Maui.Controls/blob/master/SampleApp/Mvvm/Pages/Mask/CircleMaskPage.xaml)\n- [CircleMaskPageVm.cs](https://github.com/Keflon/FunctionZero.Maui.Controls/blob/master/SampleApp/Mvvm/PageViewModels/Mask/CircleMaskPageVm.cs)\n\n# Workarounds:\n\n## `AdaptedTabbedPage` [MAUI bug 14572](https://github.com/dotnet/maui/issues/14572)  \n- Use it when you want to use `ItemsSource` and `ItemTemplate`. Stick with `TabbedPage` if you're manipulating the `Children` collection directly.  \n- This implementation replaces `ItemsSource` by hiding the base implementation.\nThis means if you set it up in code-behind, you must ensure you have a reference of type `AdaptedTabbedPage` when you set `ItemsSource`.\nIf your reference is of type `TabbedPage` or `MultiPage\u003cPage\u003e` you'll be setting the _base_ `ItemsSource` and the crash will remain.  \n### Update:\n- ~~`SelectedItem` now has limited support. Setting it in code works fine and swaps to the correct Tab, however swapping by interacting with the UI does not \nupdate `SelectedItem`, because doing so would cause the WinUI crash we're trying to dodge.~~  \n- `SelectedItem` is fine. If you think it's causing problems, set `UseExperimentalSelectedItem` to false.\n\n## `AdaptedFlyoutPage` [MAUI bug 13496](https://github.com/dotnet/maui/issues/13496)  \n- Basically if the Flyout loses focus and the FlyoutLayoutBehavior is `Popover`, \nit assumes the flyout has been dismissed and sets the `IsPresented` property to false.\n\n\n\n  \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FKeflon%2FFunctionZero.Maui.Controls","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FKeflon%2FFunctionZero.Maui.Controls","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FKeflon%2FFunctionZero.Maui.Controls/lists"}