Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions samples/Gallery/App.fs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ module App =
| GridSplitterPage
| ImagePage
| ItemsRepeaterPage
| ItemsControlPage
| LabelPage
| LayoutTransformControlPage
| ListBoxPage
Expand Down Expand Up @@ -117,6 +118,7 @@ module App =
| "GridSplitter" -> ValueSome GridSplitterPage
| "Image" -> ValueSome ImagePage
| "ItemsRepeater" -> ValueSome ItemsRepeaterPage
| "ItemsControl" -> ValueSome ItemsControlPage
| "Label" -> ValueSome LabelPage
| "LayoutTransformControl" -> ValueSome LayoutTransformControlPage
| "ListBox" -> ValueSome ListBoxPage
Expand Down Expand Up @@ -195,6 +197,7 @@ module App =
| GridSplitterPage -> ValueSome(AnyView(GridSplitterPage.view()))
| ImagePage -> ValueSome(AnyView(ImagePage.view()))
| ItemsRepeaterPage -> ValueSome(AnyView(ItemsRepeaterPage.view()))
| ItemsControlPage -> ValueSome(AnyView(ItemsControlPage.view()))
| LabelPage -> ValueSome(AnyView(LabelPage.view()))
| ListBoxPage -> ValueSome(AnyView(ListBoxPage.view()))
| LayoutTransformControlPage -> ValueSome(AnyView(LayoutTransformControlPage.view()))
Expand Down
19 changes: 0 additions & 19 deletions samples/Gallery/Common.fs
Original file line number Diff line number Diff line change
Expand Up @@ -119,25 +119,6 @@ type HamburgerMenuModifiers =
static member inline expandedModeThresholdWidth(this: WidgetBuilder<'msg, IFabHamburgerMenu>, value: int) =
this.AddScalar(HamburgerMenuExt.ExpandedModeThresholdWidth.WithValue(value))

type IFabItemsControl =
inherit IFabTemplatedControl

module FabItemsControl =

let WidgetKey = Widgets.register<ItemsControl>()

[<AutoOpen>]
module FabItemsControlBuilders =
type Fabulous.Avalonia.View with

static member ItemsControl<'msg, 'itemData, 'itemMarker when 'itemMarker :> IFabControl>
(
items: seq<'itemData>,
template: 'itemData -> WidgetBuilder<'msg, 'itemMarker>
) =
WidgetHelpers.buildItems<'msg, IFabItemsControl, 'itemData, 'itemMarker> FabItemsControl.WidgetKey ItemsControl.ItemsSource items template


open Avalonia.Layout
open type Fabulous.Avalonia.View

Expand Down
1 change: 1 addition & 0 deletions samples/Gallery/Gallery.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
<Compile Include="Pages\GridSplitterPage.fs" />
<Compile Include="Pages\ImagePage.fs" />
<Compile Include="Pages\ItemsRepeaterPage.fs" />
<Compile Include="Pages\ItemsControlPage.fs" />
<Compile Include="Pages\LabelPage.fs" />
<Compile Include="Pages\LayoutTransformControlPage.fs" />
<Compile Include="Pages\ListBoxPage.fs" />
Expand Down
1 change: 1 addition & 0 deletions samples/Gallery/MainWindow.fs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ module MainWindow =
TabItem("GridSplitterPage", GridSplitterPage.view())
TabItem("ImagePage", ImagePage.view())
TabItem("ItemsRepeaterPage", ItemsRepeaterPage.view())
TabItem("ItemsControlPage", ItemsControlPage.view())
TabItem("LabelPage", LabelPage.view())
TabItem("LayoutTransformControlPage", LayoutTransformControlPage.view())
TabItem("ListBoxPage", ListBoxPage.view())
Expand Down
33 changes: 33 additions & 0 deletions samples/Gallery/Pages/ItemsControlPage.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
namespace Gallery

open System.Diagnostics
open Avalonia
open Avalonia.Controls.Primitives
open Avalonia.Layout
open Avalonia.Media
open Fabulous.Avalonia
open Fabulous
open System.Collections.ObjectModel

open type Fabulous.Avalonia.View

module ItemsControlPage =
type Crockery = { Title: string; Number: int }

let items = [ for i in 1..1000 -> { Title = "dinner plate"; Number = i } ]

let view () =

VStack() {
TextBlock("List of crockery:")

ItemsControl(
items,
fun item ->
HStack() {
TextBlock(item.Title)
TextBlock(item.Number.ToString())
}
)
.itemsPanel(VirtualizingStackPanel())
}
3 changes: 3 additions & 0 deletions src/Fabulous.Avalonia/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
_No unreleased changes_

### Added
- `ItemsControl` and `VirtualizingStackPanel` Widgets by @edgarfgp in https://github.com/fabulous-dev/Fabulous.Avalonia/pull/247

## [3.0.0-pre5] - 2024-05-17
### Added
- TreeViewItem widget by @edgarfgp in https://github.com/fabulous-dev/Fabulous.Avalonia/pull/241
Expand Down
4 changes: 3 additions & 1 deletion src/Fabulous.Avalonia/Fabulous.Avalonia.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@
<Compile Include="Views\_TopLevel.fs" />
<Compile Include="Views\_TextElement.fs" />
<Compile Include="Views\_Panel.fs" />
<Compile Include="Views\_ItemsControl.fs" />
<Compile Include="Views\_VirtualizingPanel.fs" />
<Compile Include="Views\ItemsControl.fs" />
<Compile Include="Views\_HeaderedItemsControl.fs" />
<Compile Include="Views\_SelectingItemsControl.fs" />
<Compile Include="Views\_MenuBase.fs" />
Expand Down Expand Up @@ -211,6 +212,7 @@
<Compile Include="Views\Panels\AdornerLayer.fs" />
<Compile Include="Views\Panels\Grid.fs" />
<Compile Include="Views\Panels\StackPanel.fs" />
<Compile Include="Views\Panels\VirtualizingStackPanel.fs" />
<Compile Include="Views\Panels\ReversibleStackPanel.fs" />
<Compile Include="Views\Panels\DockPanel.fs" />
<Compile Include="Views\Panels\WrapPanel.fs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ type IFabItemsControl =
inherit IFabTemplatedControl

module ItemsControl =
let WidgetKey = Widgets.register<ItemsControl>()

let Items =
Attributes.defineAvaloniaNonGenericListWidgetCollection "ItemsControl_Items" (fun target ->
let target = target :?> ItemsControl
Expand Down Expand Up @@ -39,12 +41,12 @@ module ItemsControl =

let ItemsPanel =
Attributes.defineSimpleScalar<Widget> "ItemsControl_ItemsPanel" ScalarAttributeComparers.equalityCompare (fun _ newValueOpt node ->
let treeView = node.Target :?> TreeView
let itemsControl = node.Target :?> ItemsControl

match newValueOpt with
| ValueNone -> treeView.ClearValue(ItemsControl.ItemsPanelProperty)
| ValueNone -> itemsControl.ClearValue(ItemsControl.ItemsPanelProperty)
| ValueSome value ->
treeView.SetValue(ItemsControl.ItemsPanelProperty, WidgetItemsPanel(node, value))
itemsControl.SetValue(ItemsControl.ItemsPanelProperty, WidgetItemsPanel(node, value))
|> ignore)

let ContainerClearing =
Expand All @@ -56,6 +58,18 @@ module ItemsControl =
let ContainerPrepared =
Attributes.defineEvent "ItemsControl_ContainerPrepared" (fun target -> (target :?> ItemsControl).ContainerPrepared)


[<AutoOpen>]
module ItemsControlBuilders =
type Fabulous.Avalonia.View with

static member ItemsControl<'msg, 'itemData, 'itemMarker when 'itemMarker :> IFabControl>
(
items: seq<'itemData>,
template: 'itemData -> WidgetBuilder<'msg, 'itemMarker>
) =
WidgetHelpers.buildItems<'msg, IFabItemsControl, 'itemData, 'itemMarker> ItemsControl.WidgetKey ItemsControl.ItemsSource items template

type ItemsControlModifiers =
/// <summary>Listens to the ItemsControl ContainerClearing event.</summary>
/// <param name="this">Current widget.</param>
Expand Down
76 changes: 76 additions & 0 deletions src/Fabulous.Avalonia/Views/Panels/VirtualizingStackPanel.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
namespace Fabulous.Avalonia

open System.Runtime.CompilerServices
open Avalonia.Controls
open Avalonia.Interactivity
open Fabulous
open Fabulous.StackAllocatedCollections.StackList

type IFabVirtualizingStackPanel =
inherit IFabVirtualizingPanel

module VirtualizingStackPanel =
let WidgetKey = Widgets.register<VirtualizingStackPanel>()

let Orientation =
Attributes.defineAvaloniaPropertyWithEquality VirtualizingStackPanel.OrientationProperty

let AreHorizontalSnapPointsRegular =
Attributes.defineAvaloniaPropertyWithEquality VirtualizingStackPanel.AreHorizontalSnapPointsRegularProperty

let AreVerticalSnapPointsRegular =
Attributes.defineAvaloniaPropertyWithEquality VirtualizingStackPanel.AreVerticalSnapPointsRegularProperty

let HorizontalSnapPointsChanged =
Attributes.defineEvent "VirtualizingStackPanel_HorizontalSnapPointsChanged" (fun target ->
(target :?> VirtualizingStackPanel)
.HorizontalSnapPointsChanged)

let VerticalSnapPointsChanged =
Attributes.defineEvent "VirtualizingStackPanel_VerticalSnapPointsChanged" (fun target ->
(target :?> VirtualizingStackPanel)
.VerticalSnapPointsChanged)

[<AutoOpen>]
module VirtualizingStackPanelBuilders =
type Fabulous.Avalonia.View with

/// <summary>Creates a VirtualizingStackPanel widget.</summary>
static member VirtualizingStackPanel() =
WidgetBuilder<'msg, IFabVirtualizingStackPanel>(VirtualizingStackPanel.WidgetKey, AttributesBundle(StackList.empty(), ValueNone, ValueNone))

type VirtualizingStackPanelModifiers =
/// <summary>Sets the AreHorizontalSnapPointsRegular property.</summary>
/// <param name="this">Current widget.</param>
/// <param name="value">The AreHorizontalSnapPointsRegular value.</param>
[<Extension>]
static member inline areHorizontalSnapPointsRegular(this: WidgetBuilder<'msg, #IFabVirtualizingStackPanel>, value: bool) =
this.AddScalar(VirtualizingStackPanel.AreHorizontalSnapPointsRegular.WithValue(value))

/// <summary>Sets the AreVerticalSnapPointsRegular property.</summary>
/// <param name="this">Current widget.</param>
/// <param name="value">The AreVerticalSnapPointsRegular value.</param>
[<Extension>]
static member inline areVerticalSnapPointsRegular(this: WidgetBuilder<'msg, #IFabVirtualizingStackPanel>, value: bool) =
this.AddScalar(VirtualizingStackPanel.AreVerticalSnapPointsRegular.WithValue(value))

/// <summary>Listens to the StackPanel HorizontalSnapPointsChanged event.</summary>
/// <param name="this">Current widget.</param>
/// <param name="fn">Raised when the HorizontalSnapPointsChanged event fires.</param>
[<Extension>]
static member inline onHorizontalSnapPointsChanged(this: WidgetBuilder<'msg, #IFabVirtualizingStackPanel>, fn: RoutedEventArgs -> 'msg) =
this.AddScalar(VirtualizingStackPanel.HorizontalSnapPointsChanged.WithValue(fn))

/// <summary>Listens to the StackPanel VerticalSnapPointsChanged event.</summary>
/// <param name="this">Current widget.</param>
/// <param name="fn">Raised when the VerticalSnapPointsChanged event fires.</param>
[<Extension>]
static member inline onVerticalSnapPointsChanged(this: WidgetBuilder<'msg, #IFabVirtualizingStackPanel>, fn: RoutedEventArgs -> 'msg) =
this.AddScalar(VirtualizingStackPanel.VerticalSnapPointsChanged.WithValue(fn))

/// <summary>Link a ViewRef to access the direct VirtualizingStackPanel control instance.</summary>
/// <param name="this">Current widget.</param>
/// <param name="value">The ViewRef instance that will receive access to the underlying control.</param>
[<Extension>]
static member inline reference(this: WidgetBuilder<'msg, IFabVirtualizingStackPanel>, value: ViewRef<VirtualizingStackPanel>) =
this.AddScalar(ViewRefAttributes.ViewRef.WithValue(value.Unbox))
4 changes: 4 additions & 0 deletions src/Fabulous.Avalonia/Views/_VirtualizingPanel.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
namespace Fabulous.Avalonia

type IFabVirtualizingPanel =
inherit IFabPanel
17 changes: 6 additions & 11 deletions src/Fabulous.Avalonia/VirtualizedCollection.fs
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,10 @@ type WidgetTreeDataTemplate(node: IViewNode, childrenFn: obj -> IEnumerable, tem

view :?> Control

type WidgetItemsPanel(node: IViewNode, widget: Widget) =
type WidgetItemsPanel(node: IViewNode, widget: Widget) as this =
inherit FuncTemplate<Panel>(fun _ -> this.BuildPanel())

interface ITemplate<Panel> with
member this.Build() =
let definition = WidgetDefinitionStore.get widget.Key
let struct (_, view) = definition.CreateView(widget, node.TreeContext, ValueNone)
view :?> Panel

member this.Build() =
let definition = WidgetDefinitionStore.get widget.Key
let struct (_, view) = definition.CreateView(widget, node.TreeContext, ValueNone)
view
member this.BuildPanel() =
let definition = WidgetDefinitionStore.get widget.Key
let struct (_, view) = definition.CreateView(widget, node.TreeContext, ValueNone)
view :?> Panel