diff --git a/DLToolkit.Forms.Controls.sln b/DLToolkit.Forms.Controls.sln index efc3d20..39d1c9b 100644 --- a/DLToolkit.Forms.Controls.sln +++ b/DLToolkit.Forms.Controls.sln @@ -1,13 +1,15 @@  -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2012 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DLToolkit.Forms.Controls.FlowListView", "FlowListView\DLToolkit.Forms.Controls.FlowListView\DLToolkit.Forms.Controls.FlowListView.csproj", "{AA188FD6-4D5A-4BAA-9055-9DFECC943225}" +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.4.33110.190 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DLToolkit.Forms.Controls.FlowListView", "FlowListView\DLToolkit.Forms.Controls.FlowListView\DLToolkit.Forms.Controls.FlowListView.csproj", "{AA188FD6-4D5A-4BAA-9055-9DFECC943225}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DLToolkit.Forms.Controls.TagEntryView", "TagEntryView\DLToolkit.Forms.Controls.TagEntryView\DLToolkit.Forms.Controls.TagEntryView.csproj", "{41CD9002-B025-41A2-A37C-C121BEADF62B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DLToolkit.Forms.Controls.TagEntryView", "TagEntryView\DLToolkit.Forms.Controls.TagEntryView\DLToolkit.Forms.Controls.TagEntryView.csproj", "{41CD9002-B025-41A2-A37C-C121BEADF62B}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{F3A9DC82-4F6F-4BA1-BCB8-29662D36B330}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DLToolkitControlsSamples", "Samples\DLToolkitControlsSamples\DLToolkitControlsSamples.csproj", "{824A9A23-F91C-4DD5-9C0B-8D1EE4F7E251}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DLToolkitControlsSamples", "Samples\DLToolkitControlsSamples\DLToolkitControlsSamples.csproj", "{824A9A23-F91C-4DD5-9C0B-8D1EE4F7E251}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DLToolkitControlsSamples.iOS", "Samples\iOS\DLToolkitControlsSamples.iOS.csproj", "{34D8A8BC-C10F-4D70-AB4C-DE90577D2B1E}" EndProject @@ -21,34 +23,24 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DLToolkit.Forms.Controls.Re EndProject Project("{5DD5E4FA-CB73-4610-85AB-557B54E96AA9}") = "DLToolkit.Forms.Controls.RecyclerView.NuGet", "RecyclerView\DLToolkit.Forms.Controls.RecyclerView.NuGet\DLToolkit.Forms.Controls.RecyclerView.NuGet.nuproj", "{619D2647-3D05-4011-BF84-492D389D90BE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DLToolkit.Forms.Controls.RecyclerView", "RecyclerView\DLToolkit.Forms.Controls.RecyclerView\DLToolkit.Forms.Controls.RecyclerView.csproj", "{51A68880-A43E-4CF8-A034-C70B2EF31BE6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DLToolkit.Forms.Controls.RecyclerView", "RecyclerView\DLToolkit.Forms.Controls.RecyclerView\DLToolkit.Forms.Controls.RecyclerView.csproj", "{51A68880-A43E-4CF8-A034-C70B2EF31BE6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DLToolkit.Forms.Controls.ImageCropView", "ImageCropView\DLToolkit.Forms.Controls.ImageCropView.csproj", "{E4E74594-E008-43EA-A836-BCC049F0E483}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DLToolkit.Forms.Controls.ImageCropView", "ImageCropView\DLToolkit.Forms.Controls.ImageCropView.csproj", "{E4E74594-E008-43EA-A836-BCC049F0E483}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DLToolkitControlsSamples.Mac", "Samples\DLToolkitControlsSamples.Mac\DLToolkitControlsSamples.Mac.csproj", "{A97FA55C-A44F-4562-83E9-68CE2C72CAFE}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DLToolkit.Maui.Controls.FlowListView", "..\..\Desktop\flowlistBU\DLToolkit.Maui.Controls.FlowListView\DLToolkit.Maui.Controls.FlowListView.csproj", "{FD3ACBFD-669C-4AE9-BF1C-17D88A0F7BAD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU + Debug|iPhone = Debug|iPhone Debug|iPhoneSimulator = Debug|iPhoneSimulator + Release|Any CPU = Release|Any CPU Release|iPhone = Release|iPhone Release|iPhoneSimulator = Release|iPhoneSimulator - Debug|iPhone = Debug|iPhone EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {41CD9002-B025-41A2-A37C-C121BEADF62B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {41CD9002-B025-41A2-A37C-C121BEADF62B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {41CD9002-B025-41A2-A37C-C121BEADF62B}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {41CD9002-B025-41A2-A37C-C121BEADF62B}.Debug|iPhone.Build.0 = Debug|Any CPU - {41CD9002-B025-41A2-A37C-C121BEADF62B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU - {41CD9002-B025-41A2-A37C-C121BEADF62B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU - {41CD9002-B025-41A2-A37C-C121BEADF62B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {41CD9002-B025-41A2-A37C-C121BEADF62B}.Release|Any CPU.Build.0 = Release|Any CPU - {41CD9002-B025-41A2-A37C-C121BEADF62B}.Release|iPhone.ActiveCfg = Release|Any CPU - {41CD9002-B025-41A2-A37C-C121BEADF62B}.Release|iPhone.Build.0 = Release|Any CPU - {41CD9002-B025-41A2-A37C-C121BEADF62B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU - {41CD9002-B025-41A2-A37C-C121BEADF62B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {AA188FD6-4D5A-4BAA-9055-9DFECC943225}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AA188FD6-4D5A-4BAA-9055-9DFECC943225}.Debug|Any CPU.Build.0 = Debug|Any CPU {AA188FD6-4D5A-4BAA-9055-9DFECC943225}.Debug|iPhone.ActiveCfg = Debug|Any CPU @@ -61,114 +53,141 @@ Global {AA188FD6-4D5A-4BAA-9055-9DFECC943225}.Release|iPhone.Build.0 = Release|Any CPU {AA188FD6-4D5A-4BAA-9055-9DFECC943225}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {AA188FD6-4D5A-4BAA-9055-9DFECC943225}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {41CD9002-B025-41A2-A37C-C121BEADF62B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {41CD9002-B025-41A2-A37C-C121BEADF62B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {41CD9002-B025-41A2-A37C-C121BEADF62B}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {41CD9002-B025-41A2-A37C-C121BEADF62B}.Debug|iPhone.Build.0 = Debug|Any CPU + {41CD9002-B025-41A2-A37C-C121BEADF62B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {41CD9002-B025-41A2-A37C-C121BEADF62B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {41CD9002-B025-41A2-A37C-C121BEADF62B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {41CD9002-B025-41A2-A37C-C121BEADF62B}.Release|Any CPU.Build.0 = Release|Any CPU + {41CD9002-B025-41A2-A37C-C121BEADF62B}.Release|iPhone.ActiveCfg = Release|Any CPU + {41CD9002-B025-41A2-A37C-C121BEADF62B}.Release|iPhone.Build.0 = Release|Any CPU + {41CD9002-B025-41A2-A37C-C121BEADF62B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {41CD9002-B025-41A2-A37C-C121BEADF62B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU {824A9A23-F91C-4DD5-9C0B-8D1EE4F7E251}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {824A9A23-F91C-4DD5-9C0B-8D1EE4F7E251}.Debug|Any CPU.Build.0 = Debug|Any CPU - {824A9A23-F91C-4DD5-9C0B-8D1EE4F7E251}.Release|Any CPU.ActiveCfg = Release|Any CPU - {824A9A23-F91C-4DD5-9C0B-8D1EE4F7E251}.Release|Any CPU.Build.0 = Release|Any CPU + {824A9A23-F91C-4DD5-9C0B-8D1EE4F7E251}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {824A9A23-F91C-4DD5-9C0B-8D1EE4F7E251}.Debug|iPhone.Build.0 = Debug|Any CPU {824A9A23-F91C-4DD5-9C0B-8D1EE4F7E251}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU {824A9A23-F91C-4DD5-9C0B-8D1EE4F7E251}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {824A9A23-F91C-4DD5-9C0B-8D1EE4F7E251}.Release|Any CPU.ActiveCfg = Release|Any CPU + {824A9A23-F91C-4DD5-9C0B-8D1EE4F7E251}.Release|Any CPU.Build.0 = Release|Any CPU {824A9A23-F91C-4DD5-9C0B-8D1EE4F7E251}.Release|iPhone.ActiveCfg = Release|Any CPU {824A9A23-F91C-4DD5-9C0B-8D1EE4F7E251}.Release|iPhone.Build.0 = Release|Any CPU {824A9A23-F91C-4DD5-9C0B-8D1EE4F7E251}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {824A9A23-F91C-4DD5-9C0B-8D1EE4F7E251}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {824A9A23-F91C-4DD5-9C0B-8D1EE4F7E251}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {824A9A23-F91C-4DD5-9C0B-8D1EE4F7E251}.Debug|iPhone.Build.0 = Debug|Any CPU {34D8A8BC-C10F-4D70-AB4C-DE90577D2B1E}.Debug|Any CPU.ActiveCfg = Debug|iPhoneSimulator {34D8A8BC-C10F-4D70-AB4C-DE90577D2B1E}.Debug|Any CPU.Build.0 = Debug|iPhoneSimulator - {34D8A8BC-C10F-4D70-AB4C-DE90577D2B1E}.Release|Any CPU.ActiveCfg = Release|iPhone - {34D8A8BC-C10F-4D70-AB4C-DE90577D2B1E}.Release|Any CPU.Build.0 = Release|iPhone + {34D8A8BC-C10F-4D70-AB4C-DE90577D2B1E}.Debug|iPhone.ActiveCfg = Debug|iPhone + {34D8A8BC-C10F-4D70-AB4C-DE90577D2B1E}.Debug|iPhone.Build.0 = Debug|iPhone {34D8A8BC-C10F-4D70-AB4C-DE90577D2B1E}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator {34D8A8BC-C10F-4D70-AB4C-DE90577D2B1E}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator + {34D8A8BC-C10F-4D70-AB4C-DE90577D2B1E}.Release|Any CPU.ActiveCfg = Release|iPhone + {34D8A8BC-C10F-4D70-AB4C-DE90577D2B1E}.Release|Any CPU.Build.0 = Release|iPhone {34D8A8BC-C10F-4D70-AB4C-DE90577D2B1E}.Release|iPhone.ActiveCfg = Release|iPhone {34D8A8BC-C10F-4D70-AB4C-DE90577D2B1E}.Release|iPhone.Build.0 = Release|iPhone {34D8A8BC-C10F-4D70-AB4C-DE90577D2B1E}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator {34D8A8BC-C10F-4D70-AB4C-DE90577D2B1E}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator - {34D8A8BC-C10F-4D70-AB4C-DE90577D2B1E}.Debug|iPhone.ActiveCfg = Debug|iPhone - {34D8A8BC-C10F-4D70-AB4C-DE90577D2B1E}.Debug|iPhone.Build.0 = Debug|iPhone {29796117-7BE3-41DD-8B04-FEBCFFFB5A14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {29796117-7BE3-41DD-8B04-FEBCFFFB5A14}.Debug|Any CPU.Build.0 = Debug|Any CPU - {29796117-7BE3-41DD-8B04-FEBCFFFB5A14}.Release|Any CPU.ActiveCfg = Release|Any CPU - {29796117-7BE3-41DD-8B04-FEBCFFFB5A14}.Release|Any CPU.Build.0 = Release|Any CPU + {29796117-7BE3-41DD-8B04-FEBCFFFB5A14}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {29796117-7BE3-41DD-8B04-FEBCFFFB5A14}.Debug|iPhone.Build.0 = Debug|Any CPU {29796117-7BE3-41DD-8B04-FEBCFFFB5A14}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU {29796117-7BE3-41DD-8B04-FEBCFFFB5A14}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {29796117-7BE3-41DD-8B04-FEBCFFFB5A14}.Release|Any CPU.ActiveCfg = Release|Any CPU + {29796117-7BE3-41DD-8B04-FEBCFFFB5A14}.Release|Any CPU.Build.0 = Release|Any CPU {29796117-7BE3-41DD-8B04-FEBCFFFB5A14}.Release|iPhone.ActiveCfg = Release|Any CPU {29796117-7BE3-41DD-8B04-FEBCFFFB5A14}.Release|iPhone.Build.0 = Release|Any CPU {29796117-7BE3-41DD-8B04-FEBCFFFB5A14}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {29796117-7BE3-41DD-8B04-FEBCFFFB5A14}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {29796117-7BE3-41DD-8B04-FEBCFFFB5A14}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {29796117-7BE3-41DD-8B04-FEBCFFFB5A14}.Debug|iPhone.Build.0 = Debug|Any CPU {775A5844-4A21-457A-88FB-F10F08B154E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {775A5844-4A21-457A-88FB-F10F08B154E8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {775A5844-4A21-457A-88FB-F10F08B154E8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {775A5844-4A21-457A-88FB-F10F08B154E8}.Release|Any CPU.Build.0 = Release|Any CPU + {775A5844-4A21-457A-88FB-F10F08B154E8}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {775A5844-4A21-457A-88FB-F10F08B154E8}.Debug|iPhone.Build.0 = Debug|Any CPU {775A5844-4A21-457A-88FB-F10F08B154E8}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU {775A5844-4A21-457A-88FB-F10F08B154E8}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {775A5844-4A21-457A-88FB-F10F08B154E8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {775A5844-4A21-457A-88FB-F10F08B154E8}.Release|Any CPU.Build.0 = Release|Any CPU {775A5844-4A21-457A-88FB-F10F08B154E8}.Release|iPhone.ActiveCfg = Release|Any CPU {775A5844-4A21-457A-88FB-F10F08B154E8}.Release|iPhone.Build.0 = Release|Any CPU {775A5844-4A21-457A-88FB-F10F08B154E8}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {775A5844-4A21-457A-88FB-F10F08B154E8}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {775A5844-4A21-457A-88FB-F10F08B154E8}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {775A5844-4A21-457A-88FB-F10F08B154E8}.Debug|iPhone.Build.0 = Debug|Any CPU {C33CA06A-FA1D-4E60-AEC5-D1E6A324CA1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C33CA06A-FA1D-4E60-AEC5-D1E6A324CA1E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C33CA06A-FA1D-4E60-AEC5-D1E6A324CA1E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C33CA06A-FA1D-4E60-AEC5-D1E6A324CA1E}.Release|Any CPU.Build.0 = Release|Any CPU + {C33CA06A-FA1D-4E60-AEC5-D1E6A324CA1E}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {C33CA06A-FA1D-4E60-AEC5-D1E6A324CA1E}.Debug|iPhone.Build.0 = Debug|Any CPU {C33CA06A-FA1D-4E60-AEC5-D1E6A324CA1E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU {C33CA06A-FA1D-4E60-AEC5-D1E6A324CA1E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {C33CA06A-FA1D-4E60-AEC5-D1E6A324CA1E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C33CA06A-FA1D-4E60-AEC5-D1E6A324CA1E}.Release|Any CPU.Build.0 = Release|Any CPU {C33CA06A-FA1D-4E60-AEC5-D1E6A324CA1E}.Release|iPhone.ActiveCfg = Release|Any CPU {C33CA06A-FA1D-4E60-AEC5-D1E6A324CA1E}.Release|iPhone.Build.0 = Release|Any CPU {C33CA06A-FA1D-4E60-AEC5-D1E6A324CA1E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {C33CA06A-FA1D-4E60-AEC5-D1E6A324CA1E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {C33CA06A-FA1D-4E60-AEC5-D1E6A324CA1E}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {C33CA06A-FA1D-4E60-AEC5-D1E6A324CA1E}.Debug|iPhone.Build.0 = Debug|Any CPU {619D2647-3D05-4011-BF84-492D389D90BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {619D2647-3D05-4011-BF84-492D389D90BE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {619D2647-3D05-4011-BF84-492D389D90BE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {619D2647-3D05-4011-BF84-492D389D90BE}.Release|Any CPU.Build.0 = Release|Any CPU + {619D2647-3D05-4011-BF84-492D389D90BE}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {619D2647-3D05-4011-BF84-492D389D90BE}.Debug|iPhone.Build.0 = Debug|Any CPU {619D2647-3D05-4011-BF84-492D389D90BE}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU {619D2647-3D05-4011-BF84-492D389D90BE}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {619D2647-3D05-4011-BF84-492D389D90BE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {619D2647-3D05-4011-BF84-492D389D90BE}.Release|Any CPU.Build.0 = Release|Any CPU {619D2647-3D05-4011-BF84-492D389D90BE}.Release|iPhone.ActiveCfg = Release|Any CPU {619D2647-3D05-4011-BF84-492D389D90BE}.Release|iPhone.Build.0 = Release|Any CPU {619D2647-3D05-4011-BF84-492D389D90BE}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {619D2647-3D05-4011-BF84-492D389D90BE}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {619D2647-3D05-4011-BF84-492D389D90BE}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {619D2647-3D05-4011-BF84-492D389D90BE}.Debug|iPhone.Build.0 = Debug|Any CPU {51A68880-A43E-4CF8-A034-C70B2EF31BE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {51A68880-A43E-4CF8-A034-C70B2EF31BE6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {51A68880-A43E-4CF8-A034-C70B2EF31BE6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {51A68880-A43E-4CF8-A034-C70B2EF31BE6}.Release|Any CPU.Build.0 = Release|Any CPU + {51A68880-A43E-4CF8-A034-C70B2EF31BE6}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {51A68880-A43E-4CF8-A034-C70B2EF31BE6}.Debug|iPhone.Build.0 = Debug|Any CPU {51A68880-A43E-4CF8-A034-C70B2EF31BE6}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU {51A68880-A43E-4CF8-A034-C70B2EF31BE6}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {51A68880-A43E-4CF8-A034-C70B2EF31BE6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {51A68880-A43E-4CF8-A034-C70B2EF31BE6}.Release|Any CPU.Build.0 = Release|Any CPU {51A68880-A43E-4CF8-A034-C70B2EF31BE6}.Release|iPhone.ActiveCfg = Release|Any CPU {51A68880-A43E-4CF8-A034-C70B2EF31BE6}.Release|iPhone.Build.0 = Release|Any CPU {51A68880-A43E-4CF8-A034-C70B2EF31BE6}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {51A68880-A43E-4CF8-A034-C70B2EF31BE6}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {51A68880-A43E-4CF8-A034-C70B2EF31BE6}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {51A68880-A43E-4CF8-A034-C70B2EF31BE6}.Debug|iPhone.Build.0 = Debug|Any CPU {E4E74594-E008-43EA-A836-BCC049F0E483}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E4E74594-E008-43EA-A836-BCC049F0E483}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E4E74594-E008-43EA-A836-BCC049F0E483}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E4E74594-E008-43EA-A836-BCC049F0E483}.Release|Any CPU.Build.0 = Release|Any CPU + {E4E74594-E008-43EA-A836-BCC049F0E483}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {E4E74594-E008-43EA-A836-BCC049F0E483}.Debug|iPhone.Build.0 = Debug|Any CPU {E4E74594-E008-43EA-A836-BCC049F0E483}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU {E4E74594-E008-43EA-A836-BCC049F0E483}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {E4E74594-E008-43EA-A836-BCC049F0E483}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E4E74594-E008-43EA-A836-BCC049F0E483}.Release|Any CPU.Build.0 = Release|Any CPU {E4E74594-E008-43EA-A836-BCC049F0E483}.Release|iPhone.ActiveCfg = Release|Any CPU {E4E74594-E008-43EA-A836-BCC049F0E483}.Release|iPhone.Build.0 = Release|Any CPU {E4E74594-E008-43EA-A836-BCC049F0E483}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {E4E74594-E008-43EA-A836-BCC049F0E483}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {E4E74594-E008-43EA-A836-BCC049F0E483}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {E4E74594-E008-43EA-A836-BCC049F0E483}.Debug|iPhone.Build.0 = Debug|Any CPU {A97FA55C-A44F-4562-83E9-68CE2C72CAFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A97FA55C-A44F-4562-83E9-68CE2C72CAFE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A97FA55C-A44F-4562-83E9-68CE2C72CAFE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A97FA55C-A44F-4562-83E9-68CE2C72CAFE}.Release|Any CPU.Build.0 = Release|Any CPU + {A97FA55C-A44F-4562-83E9-68CE2C72CAFE}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {A97FA55C-A44F-4562-83E9-68CE2C72CAFE}.Debug|iPhone.Build.0 = Debug|Any CPU {A97FA55C-A44F-4562-83E9-68CE2C72CAFE}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU {A97FA55C-A44F-4562-83E9-68CE2C72CAFE}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {A97FA55C-A44F-4562-83E9-68CE2C72CAFE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A97FA55C-A44F-4562-83E9-68CE2C72CAFE}.Release|Any CPU.Build.0 = Release|Any CPU {A97FA55C-A44F-4562-83E9-68CE2C72CAFE}.Release|iPhone.ActiveCfg = Release|Any CPU {A97FA55C-A44F-4562-83E9-68CE2C72CAFE}.Release|iPhone.Build.0 = Release|Any CPU {A97FA55C-A44F-4562-83E9-68CE2C72CAFE}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {A97FA55C-A44F-4562-83E9-68CE2C72CAFE}.Release|iPhoneSimulator.Build.0 = Release|Any CPU - {A97FA55C-A44F-4562-83E9-68CE2C72CAFE}.Debug|iPhone.ActiveCfg = Debug|Any CPU - {A97FA55C-A44F-4562-83E9-68CE2C72CAFE}.Debug|iPhone.Build.0 = Debug|Any CPU + {FD3ACBFD-669C-4AE9-BF1C-17D88A0F7BAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FD3ACBFD-669C-4AE9-BF1C-17D88A0F7BAD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FD3ACBFD-669C-4AE9-BF1C-17D88A0F7BAD}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {FD3ACBFD-669C-4AE9-BF1C-17D88A0F7BAD}.Debug|iPhone.Build.0 = Debug|Any CPU + {FD3ACBFD-669C-4AE9-BF1C-17D88A0F7BAD}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {FD3ACBFD-669C-4AE9-BF1C-17D88A0F7BAD}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {FD3ACBFD-669C-4AE9-BF1C-17D88A0F7BAD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FD3ACBFD-669C-4AE9-BF1C-17D88A0F7BAD}.Release|Any CPU.Build.0 = Release|Any CPU + {FD3ACBFD-669C-4AE9-BF1C-17D88A0F7BAD}.Release|iPhone.ActiveCfg = Release|Any CPU + {FD3ACBFD-669C-4AE9-BF1C-17D88A0F7BAD}.Release|iPhone.Build.0 = Release|Any CPU + {FD3ACBFD-669C-4AE9-BF1C-17D88A0F7BAD}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {FD3ACBFD-669C-4AE9-BF1C-17D88A0F7BAD}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {824A9A23-F91C-4DD5-9C0B-8D1EE4F7E251} = {F3A9DC82-4F6F-4BA1-BCB8-29662D36B330} @@ -180,4 +199,7 @@ Global {51A68880-A43E-4CF8-A034-C70B2EF31BE6} = {46F0CF95-C8FD-4B7E-920D-A48C79E538DB} {A97FA55C-A44F-4562-83E9-68CE2C72CAFE} = {F3A9DC82-4F6F-4BA1-BCB8-29662D36B330} EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E8397010-0E8C-4885-8D1E-4AF0771C436E} + EndGlobalSection EndGlobal diff --git a/DLToolkit.Maui.Controls.FlowListView/DLToolkit.Maui.Controls.FlowListView.csproj b/DLToolkit.Maui.Controls.FlowListView/DLToolkit.Maui.Controls.FlowListView.csproj new file mode 100644 index 0000000..2f23e9e --- /dev/null +++ b/DLToolkit.Maui.Controls.FlowListView/DLToolkit.Maui.Controls.FlowListView.csproj @@ -0,0 +1,20 @@ + + + + net7.0; + + + + true + Library + enable + + + + + diff --git a/DLToolkit.Maui.Controls.FlowListView/DLToolkit.Maui.Controls.FlowListView.sln b/DLToolkit.Maui.Controls.FlowListView/DLToolkit.Maui.Controls.FlowListView.sln new file mode 100644 index 0000000..9dbf242 --- /dev/null +++ b/DLToolkit.Maui.Controls.FlowListView/DLToolkit.Maui.Controls.FlowListView.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.4.33110.190 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DLToolkit.Maui.Controls.FlowListView", "DLToolkit.Maui.Controls.FlowListView.csproj", "{C515707C-0F5F-4DB6-AB57-BA47C526992E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C515707C-0F5F-4DB6-AB57-BA47C526992E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C515707C-0F5F-4DB6-AB57-BA47C526992E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C515707C-0F5F-4DB6-AB57-BA47C526992E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C515707C-0F5F-4DB6-AB57-BA47C526992E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {CEDB42AE-1F4A-49EC-A38D-A3B1C12C3974} + EndGlobalSection +EndGlobal diff --git a/DLToolkit.Maui.Controls.FlowListView/Epsilon.cs b/DLToolkit.Maui.Controls.FlowListView/Epsilon.cs new file mode 100644 index 0000000..c6edd53 --- /dev/null +++ b/DLToolkit.Maui.Controls.FlowListView/Epsilon.cs @@ -0,0 +1,11 @@ +using System; + +namespace DLToolkit.Maui.Controls.FlowListView +{ + [Helpers.Preserve(AllMembers = true)] + internal struct Epsilon + { + public const double DoubleValue = 2.22044604925031E-16; + } +} + diff --git a/DLToolkit.Maui.Controls.FlowListView/FlowCells/FlowGridCell.cs b/DLToolkit.Maui.Controls.FlowListView/FlowCells/FlowGridCell.cs new file mode 100644 index 0000000..a3ffbd6 --- /dev/null +++ b/DLToolkit.Maui.Controls.FlowListView/FlowCells/FlowGridCell.cs @@ -0,0 +1,27 @@ +using System; +using Microsoft.Maui.Controls; + +namespace DLToolkit.Maui.Controls.FlowListView.FlowCells +{ + /// + /// FlowListView grid cell. + /// + [Helpers.Preserve(AllMembers = true)] + public class FlowGridCell : Grid, IFlowViewCell + { + /// + /// Initializes a new instance of the class. + /// + public FlowGridCell() + { + } + + /// + /// Raised when cell is tapped. + /// + public virtual void OnTapped() + { + } + } +} + diff --git a/DLToolkit.Maui.Controls.FlowListView/FlowCells/FlowScrollCell.cs b/DLToolkit.Maui.Controls.FlowListView/FlowCells/FlowScrollCell.cs new file mode 100644 index 0000000..07afed3 --- /dev/null +++ b/DLToolkit.Maui.Controls.FlowListView/FlowCells/FlowScrollCell.cs @@ -0,0 +1,27 @@ +using System; +using Microsoft.Maui.Controls; + +namespace DLToolkit.Maui.Controls.FlowListView.FlowCells +{ + /// + /// FlowListView scroll cell. + /// + [Helpers.Preserve(AllMembers = true)] + public class FlowScrollCell : ScrollView, IFlowViewCell + { + /// + /// Initializes a new instance of the class. + /// + public FlowScrollCell() + { + } + + /// + /// Raised when cell is tapped. + /// + public virtual void OnTapped() + { + } + } +} + diff --git a/DLToolkit.Maui.Controls.FlowListView/FlowCells/FlowStackCell.cs b/DLToolkit.Maui.Controls.FlowListView/FlowCells/FlowStackCell.cs new file mode 100644 index 0000000..afc18f6 --- /dev/null +++ b/DLToolkit.Maui.Controls.FlowListView/FlowCells/FlowStackCell.cs @@ -0,0 +1,27 @@ +using System; +using Microsoft.Maui.Controls; + +namespace DLToolkit.Maui.Controls.FlowListView.FlowCells +{ + /// + /// FlowListView stack cell. + /// + [Helpers.Preserve(AllMembers = true)] + public class FlowStackCell : StackLayout, IFlowViewCell + { + /// + /// Initializes a new instance of the class. + /// + public FlowStackCell() + { + } + + /// + /// Raised when cell is tapped. + /// + public virtual void OnTapped() + { + } + } +} + diff --git a/DLToolkit.Maui.Controls.FlowListView/FlowCells/FlowViewCell.cs b/DLToolkit.Maui.Controls.FlowListView/FlowCells/FlowViewCell.cs new file mode 100644 index 0000000..b5088c6 --- /dev/null +++ b/DLToolkit.Maui.Controls.FlowListView/FlowCells/FlowViewCell.cs @@ -0,0 +1,27 @@ +using System; +using Microsoft.Maui.Controls; + +namespace DLToolkit.Maui.Controls.FlowListView.FlowCells +{ + /// + /// FlowListView content view cell. + /// + [Helpers.Preserve(AllMembers = true)] + public class FlowViewCell : ContentView, IFlowViewCell + { + /// + /// Initializes a new instance of the class. + /// + public FlowViewCell() + { + } + + /// + /// Raised when cell is tapped. + /// + public virtual void OnTapped() + { + } + } +} + diff --git a/DLToolkit.Maui.Controls.FlowListView/FlowCells/IFlowViewCell.cs b/DLToolkit.Maui.Controls.FlowListView/FlowCells/IFlowViewCell.cs new file mode 100644 index 0000000..a14e028 --- /dev/null +++ b/DLToolkit.Maui.Controls.FlowListView/FlowCells/IFlowViewCell.cs @@ -0,0 +1,18 @@ +using System; +using Microsoft.Maui.Controls; + +namespace DLToolkit.Maui.Controls.FlowListView.FlowCells +{ + /// + /// IFlowViewCell. + /// + [Helpers.Preserve(AllMembers = true)] + public interface IFlowViewCell + { + /// + /// Raised when cell is tapped. + /// + void OnTapped(); + } +} + diff --git a/DLToolkit.Maui.Controls.FlowListView/FlowColumnExpand.cs b/DLToolkit.Maui.Controls.FlowListView/FlowColumnExpand.cs new file mode 100644 index 0000000..9437fa7 --- /dev/null +++ b/DLToolkit.Maui.Controls.FlowListView/FlowColumnExpand.cs @@ -0,0 +1,44 @@ +using System; + +namespace DLToolkit.Maui.Controls.FlowListView +{ + /// + /// FlowListView column expand mode. + /// + [Helpers.Preserve(AllMembers = true)] + public enum FlowColumnExpand + { + /// + /// None (default) + /// + None, + + /// + /// Only first column is expanded + /// + First, + + /// + /// Only last column is expanded + /// + Last, + + /// + /// Columns are expanded proportionally + /// + Proportional, + + /// + /// Columns are expanded proportionally + /// First column expand more to keep columns parallel + /// + ProportionalFirst, + + /// + /// Columns are expanded proportionally + /// Last column expand more to keep columns parallel + /// + ProportionalLast, + } +} + diff --git a/DLToolkit.Maui.Controls.FlowListView/FlowDataTemplateSelector.cs b/DLToolkit.Maui.Controls.FlowListView/FlowDataTemplateSelector.cs new file mode 100644 index 0000000..cfa4218 --- /dev/null +++ b/DLToolkit.Maui.Controls.FlowListView/FlowDataTemplateSelector.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections; +using System.Linq; +using Microsoft.Maui.Controls; + +namespace DLToolkit.Maui.Controls.FlowListView +{ + [Helpers.Preserve(AllMembers = true)] + public class FlowDataTemplateSelector : DataTemplateSelector + { + readonly WeakReference _flowListViewRef; + + private readonly DataTemplate _defaultTemplate; + + public FlowDataTemplateSelector(WeakReference flowListViewRef) + { + _flowListViewRef = flowListViewRef; + _defaultTemplate = new DataTemplate(() => new FlowListViewInternalCell(flowListViewRef)); + } + + protected override DataTemplate OnSelectTemplate(object item, BindableObject container) + { + if (item is IFlowLoadingModel) + { + if (_flowListViewRef.TryGetTarget(out FlowListView flowListView)) + { + return flowListView.FlowLoadingTemplate; + } + } + else if (item is IFlowEmptyModel) + { + if (_flowListViewRef.TryGetTarget(out FlowListView flowListView)) + { + return flowListView.FlowEmptyTemplate; + } + } + + return _defaultTemplate; + } + } +} diff --git a/DLToolkit.Maui.Controls.FlowListView/FlowGroup.cs b/DLToolkit.Maui.Controls.FlowListView/FlowGroup.cs new file mode 100644 index 0000000..2b6824f --- /dev/null +++ b/DLToolkit.Maui.Controls.FlowListView/FlowGroup.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.ObjectModel; + +namespace DLToolkit.Maui.Controls.FlowListView +{ + [Helpers.Preserve(AllMembers = true)] + internal class FlowGroup : FlowObservableCollection + { + public object Key { get; private set; } + + public object Model { get; private set; } + + public FlowGroup(object key, object model) + { + Key = key; + Model = model; + } + } +} diff --git a/DLToolkit.Maui.Controls.FlowListView/FlowGroupColumn.cs b/DLToolkit.Maui.Controls.FlowListView/FlowGroupColumn.cs new file mode 100644 index 0000000..68f966b --- /dev/null +++ b/DLToolkit.Maui.Controls.FlowListView/FlowGroupColumn.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.ObjectModel; + +namespace DLToolkit.Maui.Controls.FlowListView +{ + [Helpers.Preserve(AllMembers = true)] + internal class FlowGroupColumn : FlowObservableCollection + { + public int ColumnCount { get; set; } + + public bool ForceInvalidateColumns { get; set; } + + public FlowGroupColumn(int columnCount) + { + ColumnCount = columnCount; + } + } +} diff --git a/DLToolkit.Maui.Controls.FlowListView/FlowListView.cs b/DLToolkit.Maui.Controls.FlowListView/FlowListView.cs new file mode 100644 index 0000000..b7409c0 --- /dev/null +++ b/DLToolkit.Maui.Controls.FlowListView/FlowListView.cs @@ -0,0 +1,989 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Linq; +using System.Reflection; +using System.Windows.Input; +using Microsoft.Maui.Controls; +using System.Threading.Tasks; + +namespace DLToolkit.Maui.Controls.FlowListView +{ + /// + /// FlowListView. + /// + [Helpers.Preserve(AllMembers = true)] + public class FlowListView : ListView + { + /// + /// Used to avoid linking issues + /// eg. when using only XAML + /// + public static void Init() + { +#pragma warning disable 0219 + var dummy1 = typeof(FlowListView); + var dummy2 = typeof(FlowListViewInternalCell); + var dummy3 = typeof(FlowDataTemplateSelector); +#pragma warning restore 0219 + } + + /// + /// Initializes a new instance of the class. + /// + public FlowListView() : base(ListViewCachingStrategy.RecycleElement) + { + InitialSetup(); + } + + /// + /// Initializes a new instance of the class. + /// + /// Caching strategy. + public FlowListView(ListViewCachingStrategy cachingStrategy) : base(cachingStrategy) + { + InitialSetup(); + } + + private void InitialSetup() + { + RefreshDesiredColumnCount(); + SizeChanged += FlowListSizeChanged; + PropertyChanged += FlowListViewPropertyChanged; + PropertyChanging += FlowListViewPropertyChanging; + + FlowColumnExpand = FlowColumnExpand.None; + FlowColumnCount = default(int?); + FlowColumnMinWidth = 50d; + FlowRowBackgroundColor = Colors.Transparent; + FlowTappedBackgroundColor = Colors.Transparent; + FlowTappedBackgroundDelay = 0; + + var flowListViewRef = new WeakReference(this); + ItemTemplate = new FlowDataTemplateSelector(flowListViewRef); + SeparatorVisibility = SeparatorVisibility.None; + SeparatorColor = Colors.Transparent; + GroupDisplayBinding = new Binding(nameof(FlowGroup.Key)); + + ItemSelected += FlowListViewItemSelected; + ItemAppearing += FlowListViewItemAppearing; + ItemDisappearing += FlowListViewItemDisappearing; + } + + /// + /// The flow group grouping key selector property. + /// + public static BindableProperty FlowColumnExpandProperty = BindableProperty.Create(nameof(FlowColumnExpand), typeof(FlowColumnExpand), typeof(FlowListView), FlowColumnExpand.None); + + /// + /// Gets or sets FlowListView column expand mode. + /// It defines how columns should expand when + /// row current column count is less than defined columns templates count + /// + /// FlowListView column expand mode. + public FlowColumnExpand FlowColumnExpand + { + get { return (FlowColumnExpand)GetValue(FlowColumnExpandProperty); } + set { SetValue(FlowColumnExpandProperty, value); } + } + + BindingBase _flowGroupColumnCountBinding; + + /// + /// Gets or sets the flow column count. + /// + /// The flow column count binding. + public BindingBase FlowGroupColumnCountBinding + { + get { return _flowGroupColumnCountBinding; } + set + { + if (_flowGroupColumnCountBinding == value) + return; + + OnPropertyChanging(); + _flowGroupColumnCountBinding = value; + OnPropertyChanged(); + } + } + + BindingBase _flowItemVisibleBinding; + + /// + /// Gets or sets the flow item is visible. + /// + /// The flow item is visible. + public BindingBase FlowItemVisibleBinding + { + get { return _flowItemVisibleBinding; } + set + { + if (_flowItemVisibleBinding == value) + return; + + OnPropertyChanging(); + _flowItemVisibleBinding = value; + OnPropertyChanged(); + } + } + + BindingBase _flowGroupHeaderModelBinding; + + /// + /// Gets or sets the flow group model binding. + /// It will be available as Model property in your + /// group header BindingContext + /// + /// The flow group model binding. + public BindingBase FlowGroupHeaderModelBinding + { + get { return _flowGroupHeaderModelBinding; } + set + { + if (_flowGroupHeaderModelBinding == value) + return; + + OnPropertyChanging(); + _flowGroupHeaderModelBinding = value; + OnPropertyChanged(); + } + } + + BindingBase _flowGroupDisplayBinding = new Binding(nameof(FlowGroup.Key)); + + /// + /// Gets or sets the flow group display binding. + /// + /// The flow group display binding. + public BindingBase FlowGroupDisplayBinding + { + get { return _flowGroupDisplayBinding; } + set + { + if (_flowGroupDisplayBinding == value) + return; + + OnPropertyChanging(); + _flowGroupDisplayBinding = value; + OnPropertyChanged(); + + GroupDisplayBinding = value; + } + } + + BindingBase _flowGroupShortNameBinding; + + /// + /// Gets or sets the flow group short name binding. + /// + /// The flow group short name binding. + public BindingBase FlowGroupShortNameBinding + { + get { return _flowGroupShortNameBinding; } + set + { + if (_flowGroupShortNameBinding == value) + return; + + OnPropertyChanging(); + _flowGroupShortNameBinding = value; + OnPropertyChanged(); + + GroupShortNameBinding = value; + } + } + + /// + /// The flow column count property. + /// + public static BindableProperty FlowColumnCountProperty = BindableProperty.Create(nameof(FlowColumnCount), typeof(int?), typeof(FlowListView), default(int?), propertyChanged: (bindable, oldValue, newValue) => + { + var list = (FlowListView)bindable; + if (list.FlowItemsSource == null) + return; + + if (!list.FlowColumnCount.HasValue || list.FlowColumnCount != list.FlowDesiredColumnCount) + list.ForceReload(); + }); + + /// + /// Enables or disables FlowListView auto/manual column count. + /// Auto Column count is calculated basing on View width + /// and FlowColumnMinWidth property + /// + /// The flow column count. + public int? FlowColumnCount + { + get { return (int?)GetValue(FlowColumnCountProperty); } + set { SetValue(FlowColumnCountProperty, value); } + } + + /// + /// The flow desired column count property key. + /// + public static readonly BindablePropertyKey FlowDesiredColumnCountPropertyKey = BindableProperty.CreateReadOnly(nameof(FlowDesiredColumnCount), typeof(int), typeof(FlowListView), 1, BindingMode.OneWayToSource); + /// + /// The flow column count property. + /// + public static readonly BindableProperty FlowDesiredColumnCountProperty = FlowDesiredColumnCountPropertyKey.BindableProperty; + + /// + /// Flow Column count desired. + /// + /// The flow column count desired. + public int FlowDesiredColumnCount + { + get { return (int)GetValue(FlowDesiredColumnCountProperty); } + private set { SetValue(FlowDesiredColumnCountPropertyKey, value > 0 ? value : 1); } + } + + /// + /// The flow column default minimum width property. + /// + public static BindableProperty FlowColumnMinWidthProperty = BindableProperty.Create(nameof(FlowColumnMinWidth), typeof(double), typeof(FlowListView), 50d); + + /// + /// Gets or sets the minimum column width of FlowListView. + /// Currently used only with FlowAutoColumnCount option + /// + /// The minimum column width. + public double FlowColumnMinWidth + { + get { return (double)GetValue(FlowColumnMinWidthProperty); } + set { SetValue(FlowColumnMinWidthProperty, value); } + } + + /// + /// The flow row background color property. + /// + public static BindableProperty FlowRowBackgroundColorProperty = BindableProperty.Create(nameof(FlowRowBackgroundColor), typeof(Color), typeof(FlowListView), Colors.Transparent); + + /// + /// Gets or sets the color of the flow default row background. + /// Default: Transparent + /// + /// The color of the flow default row background. + public Color FlowRowBackgroundColor + { + get { return (Color)GetValue(FlowRowBackgroundColorProperty); } + set { SetValue(FlowRowBackgroundColorProperty, value); } + } + + /// + /// Occurs when FlowListView item is tapped. + /// + public event EventHandler FlowItemTapped; + + /// + /// Occurs when flow item is appearing. + /// + public event EventHandler FlowItemAppearing; + + /// + /// Occurs when flow item is disappearing. + /// + public event EventHandler FlowItemDisappearing; + + /// + /// FlowTappedBackgroundColor property. + /// + public static BindableProperty FlowTappedBackgroundColorProperty = BindableProperty.Create(nameof(FlowTappedBackgroundColor), typeof(Color), typeof(FlowListView), Colors.Transparent); + + /// + /// Forces FlowListView to use AbsoluteLayout internally + /// When Enabled, auto row height can't be measured automatically, + /// but it can improve performance + /// + /// true if flow use absolute layout internally; otherwise, false. + public bool FlowUseAbsoluteLayoutInternally { get; set; } = false; + + /// + /// Gets or sets the background color of the cell when tapped. + /// + /// The color of the flow tapped background. + public Color FlowTappedBackgroundColor + { + get { return (Color)GetValue(FlowTappedBackgroundColorProperty); } + set { SetValue(FlowTappedBackgroundColorProperty, value); } + } + + /// + /// FlowTappedBackgroundDelay property. + /// + public static BindableProperty FlowTappedBackgroundDelayProperty = BindableProperty.Create(nameof(FlowTappedBackgroundDelay), typeof(int), typeof(FlowListView), 0); + + /// + /// Gets or sets the background color delay of the cell when tapped (miliseconds). + /// + /// The flow tapped background delay. + public int FlowTappedBackgroundDelay + { + get { return (int)GetValue(FlowTappedBackgroundDelayProperty); } + set { SetValue(FlowTappedBackgroundDelayProperty, value); } + } + + /// + /// FlowLastTappedItemProperty. + /// + public static BindableProperty FlowLastTappedItemProperty = BindableProperty.Create(nameof(FlowLastTappedItem), typeof(object), typeof(FlowListView), default(object), BindingMode.OneWayToSource); + + /// + /// Gets FlowListView last tapped item. + /// + /// FlowListView last tapped item. + public object FlowLastTappedItem + { + get { return GetValue(FlowLastTappedItemProperty); } + private set { SetValue(FlowLastTappedItemProperty, value); } + } + + /// + /// FlowItemTappedCommandProperty. + /// + public static BindableProperty FlowItemTappedCommandProperty = BindableProperty.Create(nameof(FlowItemTappedCommand), typeof(ICommand), typeof(FlowListView), null); + + /// + /// Gets or sets FlowListView item tapped command. + /// + /// FlowListView item tapped command. + public ICommand FlowItemTappedCommand + { + get { return (ICommand)GetValue(FlowItemTappedCommandProperty); } + set { SetValue(FlowItemTappedCommandProperty, value); } + } + + /// + /// FlowItemAppearingCommandProperty. + /// + public static BindableProperty FlowItemAppearingCommandProperty = BindableProperty.Create(nameof(FlowItemAppearingCommand), typeof(ICommand), typeof(FlowListView), null); + + /// + /// Gets or sets FlowListView item tapped command. + /// + /// FlowListView item tapped command. + public ICommand FlowItemAppearingCommand + { + get { return (ICommand)GetValue(FlowItemAppearingCommandProperty); } + set { SetValue(FlowItemAppearingCommandProperty, value); } + } + + /// + /// FlowItemDisappearingCommandProperty. + /// + public static BindableProperty FlowItemDisappearingCommandProperty = BindableProperty.Create(nameof(FlowItemDisappearingCommand), typeof(ICommand), typeof(FlowListView), null); + + /// + /// Gets or sets FlowListView item tapped command. + /// + /// FlowListView item tapped command. + public ICommand FlowItemDisappearingCommand + { + get { return (ICommand)GetValue(FlowItemDisappearingCommandProperty); } + set { SetValue(FlowItemDisappearingCommandProperty, value); } + } + + /// + /// FlowItemsSourceProperty. + /// + public static BindableProperty FlowItemsSourceProperty = BindableProperty.Create(nameof(FlowItemsSource), typeof(ICollection), typeof(FlowListView), default(ICollection)); + + /// + /// Gets FlowListView items source. + /// + /// FlowListView items source. + public ICollection FlowItemsSource + { + get { return (IList)GetValue(FlowItemsSourceProperty); } + set { SetValue(FlowItemsSourceProperty, value); } + } + + /// + /// FlowColumnsTemplatesProperty. + /// + public static readonly BindableProperty FlowColumnTemplateProperty = BindableProperty.Create(nameof(FlowColumnTemplate), typeof(DataTemplate), typeof(FlowListView), default(DataTemplate)); + + /// + /// Gets or sets FlowListView columns templates. + /// Use instance of FlowColumnSimpleTemplateSelector for simple single view scenarios + /// or implement your own FlowColumnTemplateSelector which can return cell type + /// basing on current cell BindingContext + /// + /// FlowListView columns templates. + public DataTemplate FlowColumnTemplate + { + get + { + return (DataTemplate)GetValue(FlowColumnTemplateProperty); + } + set + { + SetValue(FlowColumnTemplateProperty, value); + } + } + + /// + /// The is loading infinite is enabled property. + /// + public static BindableProperty FlowIsLoadingInfiniteEnabledProperty = BindableProperty.Create(nameof(FlowIsLoadingInfiniteEnabled), typeof(bool), typeof(FlowListView), false); + + /// + /// Gets or sets FlowIsLoadingInfiniteEnabled loading is enabled. + /// + /// FlowIsLoadingInfiniteEnabled loading is enabled. + public bool FlowIsLoadingInfiniteEnabled + { + get { return (bool)GetValue(FlowIsLoadingInfiniteEnabledProperty); } + set { SetValue(FlowIsLoadingInfiniteEnabledProperty, value); } + } + + /// + /// The is loading infinite is running property. + /// + public static BindableProperty FlowIsLoadingInfiniteProperty = BindableProperty.Create(nameof(FlowIsLoadingInfinite), typeof(bool), typeof(FlowListView), false, BindingMode.TwoWay); + + /// + /// Gets or sets FlowIsLoadingInfinite loading is running. + /// + /// FlowIsLoadingInfinite loading is running + public bool FlowIsLoadingInfinite + { + get { return (bool)GetValue(FlowIsLoadingInfiniteProperty); } + set { SetValue(FlowIsLoadingInfiniteProperty, value); } + } + + /// + /// The total of records to loading infinite property. + /// + public static BindableProperty FlowTotalRecordsProperty = BindableProperty.Create(nameof(FlowTotalRecords), typeof(int), typeof(FlowListView), 0); + + /// + /// Gets or sets FlowTotalRecords total records to loading infinite. + /// It defines how columns should expand when + /// row current column count is less than defined columns templates count + /// + /// FlowTotalRecords total records to loading infinite. + public int FlowTotalRecords + { + get { return (int)GetValue(FlowTotalRecordsProperty); } + set { SetValue(FlowTotalRecordsProperty, value); } + } + + /// + /// FlowLoadingTemplateProperty. + /// + public static readonly BindableProperty FlowLoadingTemplateProperty = BindableProperty.Create(nameof(FlowLoadingTemplate), typeof(DataTemplate), typeof(FlowListView), default(DataTemplate)); + + /// + /// Gets or sets FlowLoadingTemplate loading template (ViewCell type). + /// + /// FlowLoadingTemplate loading template (ViewCell type). + public DataTemplate FlowLoadingTemplate + { + get { return (DataTemplate)GetValue(FlowLoadingTemplateProperty); } + set { SetValue(FlowLoadingTemplateProperty, value); } + } + + /// + /// FlowLoadingCommandProperty. + /// + public static BindableProperty FlowLoadingCommandProperty = BindableProperty.Create(nameof(FlowLoadingCommand), typeof(ICommand), typeof(FlowListView), null); + + /// + /// Gets or sets FlowLoadingCommand loading execute command. + /// + /// FlowLoadingCommand loading execute command. + public ICommand FlowLoadingCommand + { + get { return (ICommand)GetValue(FlowLoadingCommandProperty); } + set { SetValue(FlowLoadingCommandProperty, value); } + } + + /// + /// FlowEmptyTemplateProperty. + /// + public static readonly BindableProperty FlowEmptyTemplateProperty = BindableProperty.Create(nameof(FlowEmptyTemplate), typeof(DataTemplate), typeof(FlowListView), default(DataTemplate)); + + /// + /// Gets or sets FlowEmptyTemplate empty data template (ViewCell type). + /// + /// FlowEmptyTemplate empty data template (ViewCell type). + public DataTemplate FlowEmptyTemplate + { + get { return (DataTemplate)GetValue(FlowEmptyTemplateProperty); } + set { SetValue(FlowEmptyTemplateProperty, value); } + } + + /// + /// Forces FlowListView reload. + /// + public void ForceReload(bool updateOnly = false) + { + System.Diagnostics.Debug.WriteLine("ForceReload"); + + if (updateOnly) + { + if (IsGroupingEnabled) + UpdateGroupedContainerList(); + else + UpdateContainerList(); + } + else + { + RefreshDesiredColumnCount(); + + if (IsGroupingEnabled) + ReloadGroupedContainerList(); + else + ReloadContainerList(); + } + } + + /// + /// OnSizeAllocated. + /// + /// Width. + /// Height. + protected override void OnSizeAllocated(double width, double height) + { + base.OnSizeAllocated(width, height); + + RefreshDesiredColumnCount(); + } + + internal void FlowPerformTap(object sender, object item) + { + FlowLastTappedItem = item; + FlowItemTapped?.Invoke(this, new ItemTappedEventArgs(sender, item, 0)); + + var command = FlowItemTappedCommand; + if (command != null && command.CanExecute(item)) + { + command.Execute(item); + } + } + + private void RefreshDesiredColumnCount() + { + if (!FlowColumnCount.HasValue) + { + var oldColumnCount = FlowDesiredColumnCount; + double listWidth = Math.Max(Math.Max(Width, WidthRequest), MinimumWidthRequest); + + if (listWidth > 0) + { + FlowDesiredColumnCount = (int)Math.Floor(listWidth / FlowColumnMinWidth); + } + + if (ItemsSource != null && oldColumnCount != FlowDesiredColumnCount) + { + foreach (var item in ItemsSource) + { + if (item is FlowGroupColumn internalModel) + internalModel.ForceInvalidateColumns = true; + } + } + } + else + { + FlowDesiredColumnCount = FlowColumnCount.Value; + } + } + + double? lastWidth; + private void FlowListSizeChanged(object sender, EventArgs e) + { + if (!FlowColumnCount.HasValue) + { + double listWidth = Math.Max(Math.Max(Width, WidthRequest), MinimumWidthRequest); + + if (listWidth > 0) + { + if ((lastWidth.HasValue && Math.Abs(lastWidth.Value - listWidth) > Epsilon.DoubleValue) + || !lastWidth.HasValue) + { + if (ItemsSource != null) + ForceReload(); + } + + lastWidth = listWidth; + } + } + } + + private void FlowListViewPropertyChanging(object sender, Microsoft.Maui.Controls.PropertyChangingEventArgs e) + { + if (e.PropertyName == FlowItemsSourceProperty.PropertyName) + { + if (FlowItemsSource is INotifyCollectionChanged flowItemSource) + flowItemSource.CollectionChanged -= FlowItemsSourceCollectionChanged; + + if (IsGroupingEnabled) + { + var groupedSource = FlowItemsSource; + if (groupedSource != null) + { + foreach (var gr in groupedSource) + { + if (gr is INotifyCollectionChanged collectionChanged) + collectionChanged.CollectionChanged -= FlowItemsSourceCollectionChanged; + } + } + } + } + } + + private void FlowListViewPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == FlowItemsSourceProperty.PropertyName) + { + if (FlowColumnTemplate == null || FlowItemsSource == null) + { + ItemsSource = null; + return; + } + + if (FlowItemsSource is INotifyCollectionChanged flowItemSource) + flowItemSource.CollectionChanged += FlowItemsSourceCollectionChanged; + + if (IsGroupingEnabled) + { + var groupedSource = FlowItemsSource; + if (groupedSource != null) + { + foreach (var gr in groupedSource) + { + if (gr is INotifyCollectionChanged collectionChanged) + collectionChanged.CollectionChanged += FlowItemsSourceCollectionChanged; + } + } + } + + ForceReload(); + } + } + + private void FlowItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + ForceReload(updateOnly: true); + } + + private void FlowListViewItemSelected(object sender, SelectedItemChangedEventArgs e) + { + SelectedItem = null; + } + + private void FlowListViewItemAppearing(object sender, ItemVisibilityEventArgs e) + { + if (IsRefreshing || FlowIsLoadingInfinite || ItemsSource == null || !ItemsSource.Cast().Any()) + return; + + if (!FlowIsLoadingInfinite && e.Item is IFlowLoadingModel) + { + FlowIsLoadingInfinite = true; + + if (FlowLoadingCommand != null && FlowLoadingCommand.CanExecute(null)) + { + FlowLoadingCommand.Execute(null); + } + } + + EventHandler handler = FlowItemAppearing; + var command = FlowItemAppearingCommand; + + if (handler == null && command == null) + return; + + if (e.Item is IEnumerable container) + { + foreach (var item in container) + { + handler?.Invoke(this, new ItemVisibilityEventArgs(item, 0)); + + if (command != null && command.CanExecute(item)) + command.Execute(item); + } + } + else + { + handler?.Invoke(this, new ItemVisibilityEventArgs(e.Item, 0)); + + if (command != null && command.CanExecute(e.Item)) + command.Execute(e.Item); + } + } + + private void FlowListViewItemDisappearing(object sender, ItemVisibilityEventArgs e) + { + if (e.Item is IEnumerable container) + { + EventHandler handler = FlowItemDisappearing; + var command = FlowItemDisappearingCommand; + + if (handler == null && command == null) + return; + + foreach (var item in container) + { + handler?.Invoke(this, new ItemVisibilityEventArgs(item, 0)); + + if (command != null && command.CanExecute(item)) + command.Execute(item); + } + } + } + + private FlowObservableCollection GetContainerList() + { + var colCount = FlowDesiredColumnCount; + var tempList = new List(); + + var flowItemVisibleBindingPropertyName = (FlowItemVisibleBinding as Binding)?.Path; + + if (FlowItemsSource.Count <= 0 && FlowEmptyTemplate != null) + { + tempList.Add(new FlowEmptyModel()); + } + else + { + int position = -1; + var i = 0; + var ix = 0; + + foreach (var item in FlowItemsSource) + { + if (flowItemVisibleBindingPropertyName != null) + { + var itemVisibleBindingPropertyName = item.GetType().GetRuntimeProperty(flowItemVisibleBindingPropertyName); + + if (itemVisibleBindingPropertyName != null) + { + if (!(bool)itemVisibleBindingPropertyName.GetValue(item)) + { + i++; + continue; + } + } + } + + if (ix % colCount == 0) + { + position++; + + tempList.Add(new FlowObservableCollection() { item }); + } + else + { + var exContItm = (tempList[position] as IList); + exContItm?.Add(item); + } + + i++; + ix++; + } + + if (FlowIsLoadingInfiniteEnabled && FlowItemsSource.Count < FlowTotalRecords) + { + tempList.Add(new FlowLoadingModel()); + } + } + + return new FlowObservableCollection(tempList); + } + + private void UpdateContainerList() + { + if (ItemsSource is FlowObservableCollection currentSource && currentSource.Count > 0) + { + var tempList = GetContainerList(); + currentSource.Sync(tempList); + } + else + { + ReloadContainerList(); + } + } + + private void ReloadContainerList() + { + ItemAppearing -= FlowListViewItemAppearing; + ItemsSource = GetContainerList(); + ItemAppearing += FlowListViewItemAppearing; + } + + private void UpdateGroupedContainerList() + { + if (ItemsSource is FlowObservableCollection currentSource && currentSource.Count > 0) + { + var tempList = GetGroupedContainerList(); + currentSource.Sync(tempList); + } + else + { + ReloadGroupedContainerList(); + } + } + + private FlowObservableCollection GetGroupedContainerList() + { + var colCount = FlowDesiredColumnCount; + var flowGroupsList = new List(FlowItemsSource.Count); + var groupDisplayPropertyName = (FlowGroupDisplayBinding as Binding)?.Path; + var groupModelPropertyName = (FlowGroupHeaderModelBinding as Binding)?.Path; + var groupColumnCountPropertyName = (FlowGroupColumnCountBinding as Binding)?.Path; + var flowItemVisibleBindingPropertyName = (FlowItemVisibleBinding as Binding)?.Path; + + if (FlowItemsSource.Count <= 0 && FlowEmptyTemplate != null) + { + flowGroupsList.Add(new FlowGroup(null, null) { new FlowEmptyModel() }); + } + + foreach (var groupContainer in FlowItemsSource) + { + if (groupContainer is FlowGroup isAlreadyFlowGroup) + { + flowGroupsList.Add(isAlreadyFlowGroup); + } + else + { + if (groupContainer is ICollection gr) + { + var type = gr?.GetType(); + + object groupKeyValue = null; + object groupModelValue = null; + int? groupColumnCount = colCount; + + if (type != null && groupDisplayPropertyName != null) + { + PropertyInfo groupDisplayProperty = type?.GetRuntimeProperty(groupDisplayPropertyName); + groupKeyValue = groupDisplayProperty?.GetValue(gr); + } + + if (type != null && groupModelPropertyName != null) + { + PropertyInfo groupModelProperty = type?.GetRuntimeProperty(groupModelPropertyName); + groupModelValue = groupModelProperty?.GetValue(gr); + } + + if (type != null && groupColumnCountPropertyName != null) + { + PropertyInfo groupColumnCountProperty = type?.GetRuntimeProperty(groupColumnCountPropertyName); + + groupColumnCount = (int?)groupColumnCountProperty?.GetValue(gr); + groupColumnCount = groupColumnCount.GetValueOrDefault() > 0 ? groupColumnCount.Value : colCount; + } + + if (groupKeyValue == null) + { + groupKeyValue = groupContainer; + } + + var flowGroup = new FlowGroup(groupKeyValue, groupModelValue); + + if (gr.Count <= 0 && FlowEmptyTemplate != null) + { + flowGroup.Add(new FlowEmptyModel()); + } + else + { + int position = -1; + var ix = 0; + var i = 0; + + foreach (var item in gr) + { + if (flowItemVisibleBindingPropertyName != null) + { + var itemVisibleBindingPropertyName = item.GetType().GetRuntimeProperty(flowItemVisibleBindingPropertyName); + + if (itemVisibleBindingPropertyName != null) + { + if (!(bool)itemVisibleBindingPropertyName.GetValue(item)) + { + i++; + continue; + } + } + } + + if (ix % groupColumnCount == 0) + { + position++; + + flowGroup.Add(new FlowGroupColumn(groupColumnCount.GetValueOrDefault()) { item }); + } + else + { + var exContItm = (flowGroup[position] as IList); + exContItm?.Add(item); + } + + ix++; + i++; + } + } + + flowGroupsList.Add(flowGroup); + } + } + } + + if (FlowIsLoadingInfiniteEnabled && FlowItemsSource != null) + { + var sum = FlowItemsSource.Cast().Sum(s => (s as IList)?.Count ?? 0); + + if (sum < FlowTotalRecords) + { + flowGroupsList.LastOrDefault()?.Add(new FlowLoadingModel()); + } + } + + return new FlowObservableCollection(flowGroupsList); + } + + private void ReloadGroupedContainerList() + { + ItemAppearing -= FlowListViewItemAppearing; + var ctrList = GetGroupedContainerList(); + + try + { + + ItemsSource = ctrList; + } + catch (NullReferenceException ex) + { + System.Diagnostics.Debug.WriteLine(ex); + //TODO HACK some strange Xamarin.Forms exceptionw when using grouping + fast scroll shortname list !? + } + + ItemAppearing += FlowListViewItemAppearing; + } + + /// + /// Scrolls list to specified item + /// + /// Item. + /// Position. + /// If set to true animated. + public void FlowScrollTo(object item, ScrollToPosition position, bool animated) + { + if (!IsGroupingEnabled) + { + var castedItemsSource = ItemsSource as IEnumerable; + var internalItem = castedItemsSource?.FirstOrDefault(v => v == item || ((v as IEnumerable)?.Cast().Contains(item)).GetValueOrDefault()); + ScrollTo(internalItem, position, animated); + } + else + { + var castedItemsSource = ItemsSource as IEnumerable; + var internalItem = castedItemsSource?.Select(v => v.FirstOrDefault(itm => ((itm as IEnumerable)?.Cast().Contains(item)).GetValueOrDefault())).FirstOrDefault(v => v != null); + ScrollTo(internalItem, position, animated); + } + } + } +} + diff --git a/DLToolkit.Maui.Controls.FlowListView/FlowListViewInternalCell.cs b/DLToolkit.Maui.Controls.FlowListView/FlowListViewInternalCell.cs new file mode 100644 index 0000000..cd176d4 --- /dev/null +++ b/DLToolkit.Maui.Controls.FlowListView/FlowListViewInternalCell.cs @@ -0,0 +1,584 @@ +using System; +using Microsoft.Maui.Controls; +using System.Collections.Generic; +using System.Collections; +using System.Threading.Tasks; +using System.Collections.Specialized; +using Microsoft.Maui.Controls.Internals; +using Microsoft.Maui.Layouts; +using DLToolkit.Maui.Controls.FlowListView.FlowCells; +using ViewCell = Microsoft.Maui.Controls.ViewCell; +using Microsoft.Maui.Controls.Platform; +using Microsoft.Maui.Controls.Shapes; + +namespace DLToolkit.Maui.Controls.FlowListView +{ + /// + /// Flow list view internal cell. + /// + [Helpers.Preserve(AllMembers = true)] + public class FlowListViewInternalCell : ViewCell + { + readonly WeakReference _flowListViewRef; + readonly AbsoluteLayout _rootLayout; + readonly Grid _rootLayoutAuto; + readonly bool _useGridAsMainRoot; + int _desiredColumnCount; + DataTemplate _flowColumnTemplate; + FlowColumnExpand _flowColumnExpand; + IList _currentColumnTemplates; + + /// + /// Initializes a new instance of the class. + /// + /// Flow list view reference. + public FlowListViewInternalCell(WeakReference flowListViewRef) + { + _flowListViewRef = flowListViewRef; + flowListViewRef.TryGetTarget(out FlowListView flowListView); + _useGridAsMainRoot = !flowListView.FlowUseAbsoluteLayoutInternally; + + if (!_useGridAsMainRoot) + { + + _rootLayout = new AbsoluteLayout() + { + Padding = 0d, + BackgroundColor = flowListView.FlowRowBackgroundColor, + + }; + + View = _rootLayout; + //AbsoluteLayout.SetLayoutBounds(View, new Rect() { X = 0, Y = 0, Width = 1, Height = 1 }); + //AbsoluteLayout.SetLayoutFlags(View, AbsoluteLayoutFlags.All); + } + else + { + _rootLayoutAuto = new Grid() + { + RowSpacing = 0d, + ColumnSpacing = 0d, + Padding = 0d, + BackgroundColor = flowListView.FlowRowBackgroundColor, + }; + View = _rootLayoutAuto; + } + + _flowColumnTemplate = flowListView.FlowColumnTemplate; + _desiredColumnCount = flowListView.FlowDesiredColumnCount; + _flowColumnExpand = flowListView.FlowColumnExpand; + + View.GestureRecognizers.Clear(); + View.GestureRecognizers.Add(new TapGestureRecognizer()); + } + + private IList GetDataTemplates(IList container) + { + List templates = new List(); + + if (_flowColumnTemplate is FlowTemplateSelector flowTemplateSelector) + { + _flowListViewRef.TryGetTarget(out FlowListView flowListView); + + for (int i = 0; i < container.Count; i++) + { + var template = flowTemplateSelector.SelectTemplate(container[i], i, flowListView); + templates.Add(template); + } + + return templates; + } + + if (_flowColumnTemplate is DataTemplateSelector templateSelector) + { + _flowListViewRef.TryGetTarget(out FlowListView flowListView); + + for (int i = 0; i < container.Count; i++) + { + var template = templateSelector.SelectTemplate(container[i], flowListView); + templates.Add(template); + } + + return templates; + } + + for (int i = 0; i < container.Count; i++) + { + templates.Add(_flowColumnTemplate); + } + + return templates; + } + + private bool RowLayoutChanged(int containerCount, IList templates, int columnCount) + { + // Check if desired number of columns is equal to current number of columns + if (_currentColumnTemplates == null || containerCount != _currentColumnTemplates.Count) + { + return true; + } + + // Check if desired column view types are equal to current columns view types + for (int i = 0; i < containerCount; i++) + { + var currentTemplateType = _currentColumnTemplates[i].GetHashCode(); + var templateType = templates[i].GetHashCode(); + + if (currentTemplateType != templateType) + { + return true; + } + } + + if (_desiredColumnCount != columnCount) + { + return true; + } + + return false; + } + + private void SetBindingContextForView(View view, object bindingContext) + { + if (view != null && view.BindingContext != bindingContext) + view.BindingContext = bindingContext; + } + + void AddViewToLayoutAutoHeightDisabled(View view, int containerCount, int colNumber) + { + double desiredColumnWidth = 1d / _desiredColumnCount; + Rect bounds = Rect.Zero; + + if (_flowColumnExpand != FlowColumnExpand.None && _desiredColumnCount > containerCount) + { + int diff = _desiredColumnCount - containerCount; + bool isLastColumn = colNumber == containerCount - 1; + + switch (_flowColumnExpand) + { + case FlowColumnExpand.First: + + if (colNumber == 0) + { + bounds = new Rect(0d, 0d, desiredColumnWidth + (desiredColumnWidth * diff), 1d); + } + else if (isLastColumn) + { + bounds = new Rect(1d, 0d, desiredColumnWidth, 1d); + } + else + { + bounds = new Rect(desiredColumnWidth * (colNumber + diff) / (1d - desiredColumnWidth), 0d, desiredColumnWidth, 1d); + } + + break; + + case FlowColumnExpand.Last: + + if (colNumber == 0) + { + bounds = new Rect(0d, 0d, desiredColumnWidth + (desiredColumnWidth * diff), 1d); + } + else if (isLastColumn) + { + bounds = new Rect(1d, 0d, desiredColumnWidth + (desiredColumnWidth * diff), 1d); + } + else + { + bounds = new Rect(desiredColumnWidth * colNumber / (1d - desiredColumnWidth), 0d, desiredColumnWidth, 1d); + } + + break; + + case FlowColumnExpand.Proportional: + + double propColumnsWidth = 1d / containerCount; + if (colNumber == 0) + { + bounds = new Rect(0d, 0d, propColumnsWidth, 1d); + } + else if (isLastColumn) + { + bounds = new Rect(1d, 0d, propColumnsWidth, 1d); + } + else + { + bounds = new Rect(propColumnsWidth * colNumber / (1d - propColumnsWidth), 0d, propColumnsWidth, 1d); + } + + break; + + case FlowColumnExpand.ProportionalFirst: + + int propFMod = _desiredColumnCount % containerCount; + double propFSize = desiredColumnWidth * Math.Floor((double)_desiredColumnCount / containerCount); + double propFSizeFirst = propFSize + desiredColumnWidth * propFMod; + + if (colNumber == 0) + { + bounds = new Rect(0d, 0d, propFSizeFirst, 1d); + } + else if (isLastColumn) + { + bounds = new Rect(1d, 0d, propFSize, 1d); + } + else + { + bounds = new Rect(((propFSize * colNumber) + (propFSizeFirst - propFSize)) / (1d - propFSize), 0d, propFSize, 1d); + } + + break; + + case FlowColumnExpand.ProportionalLast: + + int propLMod = _desiredColumnCount % containerCount; + double propLSize = desiredColumnWidth * Math.Floor((double)_desiredColumnCount / containerCount); + double propLSizeLast = propLSize + desiredColumnWidth * propLMod; + + if (colNumber == 0) + { + bounds = new Rect(0d, 0d, propLSize, 1d); + } + else if (isLastColumn) + { + bounds = new Rect(1d, 0d, propLSizeLast, 1d); + } + else + { + bounds = new Rect((propLSize * colNumber) / (1d - propLSize), 0d, propLSize, 1d); + } + + break; + } + } + else + { + if (Math.Abs(1d - desiredColumnWidth) < Epsilon.DoubleValue) + { + bounds = new Rect(1d, 0d, desiredColumnWidth, 1d); + } + else + { + bounds = new Rect(desiredColumnWidth * colNumber / (1d - desiredColumnWidth), 0d, desiredColumnWidth, 1d); + } + } + + _rootLayout.SetLayoutBounds(view, bounds); + _rootLayout.SetLayoutFlags(view, AbsoluteLayoutFlags.All); + _rootLayout.Children.Add(view); + + } + + void AddViewToLayoutAutoHeightEnabled(View view, int containerCount, int colNumber) + { + if (_desiredColumnCount > containerCount) + { + int diff = _desiredColumnCount - containerCount; + bool isLastColumn = colNumber == containerCount - 1; + + switch (_flowColumnExpand) + { + case FlowColumnExpand.None: + + _rootLayoutAuto.SetColumn(view, colNumber); + _rootLayoutAuto.SetRow(view, 0); + + _rootLayoutAuto.Children.Add(view); + + break; + + case FlowColumnExpand.First: + + if (colNumber == 0) + { + //https://github.com/dotnet/maui/issues/780 + //Template from Xamarin.Forms grid.Children.Add(left, right, top, bottom) + //SetColumn(view, left); + //SetColumnSpan(view, right - left); + //SetRow(view, top); + //SetRowSpan(view, bottom - top); + _rootLayoutAuto.SetColumn(view, colNumber); + _rootLayoutAuto.SetColumnSpan(view, (colNumber + diff + 1) - colNumber); + _rootLayoutAuto.SetRow(view, 0); + _rootLayoutAuto.SetRowSpan(view, 1); + _rootLayoutAuto.Children.Add(view/*, colNumber, colNumber + diff + 1, 0, 1*/); + } + else + { + _rootLayoutAuto.SetColumn(view, colNumber + diff); + _rootLayoutAuto.SetColumnSpan(view, (colNumber + diff + 1) - colNumber + diff); + _rootLayoutAuto.SetRow(view, 0); + _rootLayoutAuto.SetRowSpan(view, 1); + _rootLayoutAuto.Children.Add(view/* colNumber + diff, colNumber + diff + 1, 0, 1*/); + } + + break; + + case FlowColumnExpand.Last: + + if (isLastColumn) + { + _rootLayoutAuto.SetColumn(view, colNumber); + _rootLayoutAuto.SetColumnSpan(view,(colNumber + diff + 1) - colNumber); + _rootLayoutAuto.SetRow(view, 0); + _rootLayoutAuto.SetRowSpan(view, 1); + + _rootLayoutAuto.Children.Add(view); + + } + else + { + _rootLayoutAuto.SetColumn(view, colNumber); + _rootLayoutAuto.SetRow(view, 0); + _rootLayoutAuto.Children.Add(view); + } + + break; + + case FlowColumnExpand.Proportional: + + int howManyP = _desiredColumnCount / containerCount - 1; + + var left1 = colNumber + colNumber * howManyP; + _rootLayoutAuto.SetColumn(view, left1); + _rootLayoutAuto.SetColumnSpan(view, (colNumber + colNumber * howManyP + howManyP + 1) - left1); + _rootLayoutAuto.SetRow(view, 0); + _rootLayoutAuto.SetRowSpan(view, 1); + _rootLayoutAuto.Children.Add(view/*, colNumber + colNumber * howManyP, colNumber + colNumber * howManyP + howManyP + 1, 0, 1*/); + + break; + + case FlowColumnExpand.ProportionalFirst: + + int firstSizeAdd = (int)((double)_desiredColumnCount) % containerCount; //1 + int otherSize = (int)Math.Floor((double)_desiredColumnCount / containerCount); //2 + + if (colNumber == 0) + { + _rootLayoutAuto.SetColumn(view, 0); + _rootLayoutAuto.SetColumnSpan(view, otherSize + firstSizeAdd); + _rootLayoutAuto.SetRow(view, 0); + _rootLayoutAuto.SetRowSpan(view, 1); + _rootLayoutAuto.Children.Add(view/*, 0, otherSize + firstSizeAdd, 0, 1*/); + } + + else + { + var left2 = (colNumber * otherSize) + firstSizeAdd; + _rootLayoutAuto.SetColumn(view, left2); + _rootLayoutAuto.SetColumnSpan(view, ((colNumber + 1) * otherSize) + firstSizeAdd - left2); + _rootLayoutAuto.SetRow(view, 0); + _rootLayoutAuto.SetRowSpan(view, 1); + _rootLayoutAuto.Children.Add(view/*, (colNumber * otherSize) + firstSizeAdd, ((colNumber + 1) * otherSize) + firstSizeAdd, 0, 1*/); + } + + + break; + + case FlowColumnExpand.ProportionalLast: + + int lastSizeAdd = (int)((double)_desiredColumnCount) % containerCount; //1 + int otherSize1 = (int)Math.Floor((double)_desiredColumnCount / containerCount); //2 + var left3 = colNumber * otherSize1; + if (isLastColumn) + { + _rootLayoutAuto.SetColumn(view, left3); + _rootLayoutAuto.SetColumnSpan(view, ((colNumber + 1) * otherSize1) + lastSizeAdd - left3); + _rootLayoutAuto.SetRow(view, 0); + _rootLayoutAuto.SetRowSpan(view, 1 - 0); + _rootLayoutAuto.Children.Add(view/*, (colNumber * otherSize1), ((colNumber + 1) * otherSize1) + lastSizeAdd, 0, 1*/); + } + else + { + _rootLayoutAuto.SetColumn(view, (colNumber * otherSize1)); + _rootLayoutAuto.SetColumnSpan(view, ((colNumber + 1) * otherSize1) - left3); + _rootLayoutAuto.SetRow(view, 0); + _rootLayoutAuto.SetRowSpan(view, 1 - 0); + _rootLayoutAuto.Children.Add(view/*, (colNumber * otherSize1), ((colNumber + 1) * otherSize1), 0, 1*/); + } + + break; + } + } + else + { + _rootLayoutAuto.SetColumn(view, colNumber); + _rootLayoutAuto.SetRow(view, 0); + _rootLayoutAuto.Children.Add(view/*, colNumber, 0*/); + } + } + + /// + /// Override this method to execute an action when the BindingContext changes. + /// + /// + protected override void OnBindingContextChanged() + { + base.OnBindingContextChanged(); + + UpdateData(); + + if (BindingContext is INotifyCollectionChanged container) + { + container.CollectionChanged -= Container_CollectionChanged; + container.CollectionChanged += Container_CollectionChanged; + } + } + + private void Container_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + UpdateData(); + } + + private void UpdateData() + { + if (!(BindingContext is IList container)) + return; + + var newDesiredColumnCount = 0; + + if (_flowListViewRef.TryGetTarget(out FlowListView flowListView) && flowListView != null) + { + _flowColumnTemplate = flowListView.FlowColumnTemplate; + newDesiredColumnCount = flowListView.FlowDesiredColumnCount; + _flowColumnExpand = flowListView.FlowColumnExpand; + } + + var flowGroupColumn = BindingContext as FlowGroupColumn; + if (flowGroupColumn != null) + { + newDesiredColumnCount = flowGroupColumn.ColumnCount; + } + + // Getting view types from templates + var containerCount = container.Count; + IList templates = GetDataTemplates(container); + + bool layoutChanged = false; + if (flowGroupColumn != null && flowGroupColumn.ForceInvalidateColumns) + { + layoutChanged = true; + flowGroupColumn.ForceInvalidateColumns = false; + } + else + { + layoutChanged = RowLayoutChanged(containerCount, templates, newDesiredColumnCount); + } + + _desiredColumnCount = newDesiredColumnCount; + + if (!layoutChanged) // REUSE VIEWS + { + if (_useGridAsMainRoot) + { + for (int i = 0; i < containerCount; i++) + { + SetBindingContextForView((View)_rootLayoutAuto.Children[i], container[i]); + } + } + else + { + for (int i = 0; i < containerCount; i++) + { + SetBindingContextForView((View)_rootLayout.Children[i], container[i]); + } + } + } + else // RECREATE COLUMNS + { + _currentColumnTemplates = new List(templates); + + if (_useGridAsMainRoot) + { + if (_rootLayoutAuto.Children.Count > 0) + _rootLayoutAuto.Children.Clear(); + + var colDefs = new ColumnDefinitionCollection(); + for (int i = 0; i < _desiredColumnCount; i++) + { + colDefs.Add(new ColumnDefinition() { Width = new GridLength(1d, GridUnitType.Star) }); + } + _rootLayoutAuto.ColumnDefinitions = colDefs; + + for (int i = 0; i < containerCount; i++) + { + if (!(templates[i].CreateContent() is View view)) + throw new InvalidCastException("DataTemplate must return a View"); + + AddTapGestureToView(view); + + SetBindingContextForView(view, container[i]); + if (containerCount == 0 || _desiredColumnCount == 0) + return; + + AddViewToLayoutAutoHeightEnabled(view, containerCount, i); + } + } + else + { + if (_rootLayout.Children.Count > 0) + _rootLayout.Children.Clear(); + + for (int i = 0; i < containerCount; i++) + { + if (!(templates[i].CreateContent() is View view)) + throw new InvalidCastException("DataTemplate must return a View"); + + AddTapGestureToView(view); + + SetBindingContextForView(view, container[i]); + if (containerCount == 0 || _desiredColumnCount == 0) + return; + + AddViewToLayoutAutoHeightDisabled(view, containerCount, i); + } + } + } + } + + void AddTapGestureToView(View view) + { + var command = new Command(async (obj) => + { + await ExecuteTapGestureRecognizer(view); + }); + + view.GestureRecognizers.Add(new TapGestureRecognizer() { Command = command }); + view.GestureRecognizers.Add(new ClickGestureRecognizer() { Command = command, Buttons = ButtonsMask.Primary, NumberOfClicksRequired = 1 }); + } + + async Task ExecuteTapGestureRecognizer(View view) + { + if (view is IFlowViewCell flowCell) + { + flowCell.OnTapped(); + } + + _flowListViewRef.TryGetTarget(out FlowListView flowListView); + + if (flowListView != null) + { + int tapBackgroundEffectDelay = flowListView.FlowTappedBackgroundDelay; + + try + { + if (tapBackgroundEffectDelay != 0) + { + view.BackgroundColor = flowListView.FlowTappedBackgroundColor; + } + + flowListView.FlowPerformTap(view, view.BindingContext); + } + finally + { + if (tapBackgroundEffectDelay != 0) + { + await Task.Delay(tapBackgroundEffectDelay); + view.BackgroundColor = flowListView.FlowRowBackgroundColor; + } + } + } + } + } +} + diff --git a/DLToolkit.Maui.Controls.FlowListView/FlowObservableCollection.cs b/DLToolkit.Maui.Controls.FlowListView/FlowObservableCollection.cs new file mode 100644 index 0000000..4c670e6 --- /dev/null +++ b/DLToolkit.Maui.Controls.FlowListView/FlowObservableCollection.cs @@ -0,0 +1,227 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Linq; + +namespace DLToolkit.Maui.Controls.FlowListView +{ + /// + /// FlowObservableCollection. + /// + [Helpers.Preserve(AllMembers = true)] + public class FlowObservableCollection : ObservableCollection + { + private bool _disableOnCollectionChanged; + + /// + /// Constructor. + /// + public FlowObservableCollection() : base() { } + + /// + /// Constructor from items. + /// + public FlowObservableCollection(IEnumerable items) : base(items) { } + + /// + /// Adds the range. + /// + /// Items. + public virtual void AddRange(IEnumerable items) + { + _disableOnCollectionChanged = true; + + foreach (var item in items) + Items.Add(item); + + _disableOnCollectionChanged = false; + NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, items)); + } + + /// + /// Repopulate the specified items. + /// + /// The repopulate. + /// Items. + public virtual void Repopulate(IEnumerable items) + { + _disableOnCollectionChanged = true; + Clear(); + + foreach (var item in items) + Items.Add(item); + + _disableOnCollectionChanged = false; + + NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + + /// + /// Removes the range. + /// + /// Items. + public virtual void RemoveRange(IEnumerable items) + { + _disableOnCollectionChanged = false; + + foreach (var item in items) + { + Items.Remove(item); + } + + _disableOnCollectionChanged = true; + + NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, items)); + } + + internal void OnCollectionChangedSuspend() + { + _disableOnCollectionChanged = true; + } + + internal void OnCollectionChangedResume() + { + _disableOnCollectionChanged = false; + NotifyCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + + internal void OnCollectionChangedCancel() + { + _disableOnCollectionChanged = false; + } + + /// + /// Override OnCollectionChanged to Batch process. + /// + protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) + { + if (!_disableOnCollectionChanged) + { + try + { + base.OnCollectionChanged(e); + } + catch (NullReferenceException ex) + { + System.Diagnostics.Debug.WriteLine(ex); + //TODO HACK some strange Xamarin.Forms exceptionw when using grouping + fast scroll shortname list !? + } + } + } + + internal void NotifyCollectionChanged(NotifyCollectionChangedEventArgs args) + { + this.OnCollectionChanged(args); + this.OnPropertyChanged(new PropertyChangedEventArgs("Count")); + this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]")); + } + + internal bool Sync(FlowObservableCollection newItems, bool notify = true) + { + return SyncPrivate(this, newItems, notify); + } + + private static bool SyncPrivate(FlowObservableCollection currentItems, FlowObservableCollection updateItems, bool notify) + { + currentItems?.OnCollectionChangedSuspend(); + + var itemsAdded = notify ? new List() : null; + var itemsRemoved = notify ? new List() : null; + //var itemsMoved = notify ? new List() : null; + //var itemsMovedIndexes = notify ? new List>() : null; + + bool structureIsChanged = false; + bool forceReset = false; + + var flowGroup = updateItems.FirstOrDefault() as FlowGroup; + if (flowGroup != null) + { + var result = updateItems + .Join(currentItems, k => ((FlowGroup)(object)k).Key, i => ((FlowGroup)(object)i).Key, (k, i) => i) + .ToList(); + + for (int i = 0; i < result.Count; i++) + { + var oldGroup = currentItems[i] as FlowGroup; + var newGroup = result[i] as FlowGroup; + + if (oldGroup.Key != newGroup.Key) + { + forceReset = true; + currentItems[i] = (T)(object)newGroup; + } + } + } + + for (int i = 0; i < updateItems.Count; i++) + { + var item = updateItems[i]; + + if (currentItems.Count <= i) + { + structureIsChanged = true; + currentItems.Add(item); + itemsAdded?.Add(item); + } + else + { + var itemList = item as FlowObservableCollection; + var currentItem = currentItems[i]; + var currentItemList = currentItem as FlowObservableCollection; + + if (itemList != null && currentItemList != null) + { + if (currentItemList.Sync(itemList, false)) + { + structureIsChanged = true; + } + } + else if (structureIsChanged) + { + currentItems[i] = item; + } + else if ((object)item != (object)currentItem) + { + structureIsChanged = true; + currentItems[i] = item; + } + } + } + + while (currentItems.Count > updateItems.Count) + { + structureIsChanged = true; + itemsRemoved?.Add(currentItems[currentItems.Count - 1]); + currentItems.RemoveAt(currentItems.Count - 1); + } + + if (forceReset || structureIsChanged) + { + if (!forceReset && itemsAdded != null && itemsRemoved == null && itemsAdded.Count < 100) + { + currentItems?.OnCollectionChangedCancel(); + currentItems?.NotifyCollectionChanged( + new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, itemsAdded)); + } + else if (!forceReset && itemsRemoved != null && itemsAdded == null && itemsRemoved.Count < 100) + { + currentItems?.NotifyCollectionChanged( + new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, itemsRemoved)); + } + else + { + currentItems?.OnCollectionChangedResume(); + } + } + else + { + currentItems?.OnCollectionChangedCancel(); + } + + return structureIsChanged; + } + } +} diff --git a/DLToolkit.Maui.Controls.FlowListView/FlowSelectors/FlowTemplateSelector.cs b/DLToolkit.Maui.Controls.FlowListView/FlowSelectors/FlowTemplateSelector.cs new file mode 100644 index 0000000..6cf7ddb --- /dev/null +++ b/DLToolkit.Maui.Controls.FlowListView/FlowSelectors/FlowTemplateSelector.cs @@ -0,0 +1,36 @@ +using System; +using Microsoft.Maui.Controls; + +namespace DLToolkit.Maui.Controls.FlowListView +{ + /// + /// Flow template selector. + /// + [Helpers.Preserve(AllMembers = true)] + public abstract class FlowTemplateSelector : DataTemplate + { + /// + /// Selects the template. + /// + /// The template. + /// Item. + /// Column index. + /// Container. + public DataTemplate SelectTemplate(object item, int columnIndex, BindableObject container) + { + DataTemplate result = OnSelectTemplate(item, columnIndex, container); + if (result is DataTemplateSelector || result is FlowTemplateSelector) + throw new NotSupportedException("FlowTemplateSelector.OnSelectTemplate must not return another DataTemplateSelector"); + return result; + } + + /// + /// Ons the select template. + /// + /// The select template. + /// Item. + /// Column index. + /// Container. + protected abstract DataTemplate OnSelectTemplate(object item, int columnIndex, BindableObject container); + } +} diff --git a/DLToolkit.Maui.Controls.FlowListView/Helpers/PreserveAttribute.cs b/DLToolkit.Maui.Controls.FlowListView/Helpers/PreserveAttribute.cs new file mode 100644 index 0000000..3b6ecef --- /dev/null +++ b/DLToolkit.Maui.Controls.FlowListView/Helpers/PreserveAttribute.cs @@ -0,0 +1,10 @@ +using System; + +namespace DLToolkit.Maui.Controls.FlowListView.Helpers +{ + public sealed class PreserveAttribute : System.Attribute + { + public bool AllMembers; + public bool Conditional; + } +} diff --git a/DLToolkit.Maui.Controls.FlowListView/IFlowEmptyModel.cs b/DLToolkit.Maui.Controls.FlowListView/IFlowEmptyModel.cs new file mode 100644 index 0000000..360edc3 --- /dev/null +++ b/DLToolkit.Maui.Controls.FlowListView/IFlowEmptyModel.cs @@ -0,0 +1,13 @@ +using System; +namespace DLToolkit.Maui.Controls.FlowListView +{ + [Helpers.Preserve(AllMembers = true)] + public interface IFlowEmptyModel + { + } + + [Helpers.Preserve(AllMembers = true)] + internal class FlowEmptyModel : IFlowEmptyModel + { + } +} \ No newline at end of file diff --git a/DLToolkit.Maui.Controls.FlowListView/IFlowLoadingModel.cs b/DLToolkit.Maui.Controls.FlowListView/IFlowLoadingModel.cs new file mode 100644 index 0000000..b259a5c --- /dev/null +++ b/DLToolkit.Maui.Controls.FlowListView/IFlowLoadingModel.cs @@ -0,0 +1,14 @@ +using System; + +namespace DLToolkit.Maui.Controls.FlowListView +{ + [Helpers.Preserve(AllMembers = true)] + public interface IFlowLoadingModel + { + } + + [Helpers.Preserve(AllMembers = true)] + internal class FlowLoadingModel : IFlowLoadingModel + { + } +}