diff --git a/Epoxy.sln b/Epoxy.sln
index 9fb54fce..35fc1274 100644
--- a/Epoxy.sln
+++ b/Epoxy.sln
@@ -133,6 +133,15 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "EpoxyHello.Avalonia11", "pl
{BF5976C8-2143-4769-A029-0B1C64B18F81} = {BF5976C8-2143-4769-A029-0B1C64B18F81}
EndProjectSection
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Epoxy.Core.Maui", "src\Epoxy.Core.Maui\Epoxy.Core.Maui.csproj", "{8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Epoxy.Maui", "src\Epoxy.Maui\Epoxy.Maui.csproj", "{D60C19BC-5C16-41FD-8C01-37EABE473FF0}"
+ ProjectSection(ProjectDependencies) = postProject
+ {BF5976C8-2143-4769-A029-0B1C64B18F81} = {BF5976C8-2143-4769-A029-0B1C64B18F81}
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EpoxyHello.Maui", "playground\EpoxyHello.Maui\EpoxyHello.Maui.csproj", "{C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
@@ -2003,6 +2012,202 @@ Global
{44942734-7F1C-4466-84AF-C55AFCCB8092}.Release|x64.Build.0 = Release|Any CPU
{44942734-7F1C-4466-84AF-C55AFCCB8092}.Release|x86.ActiveCfg = Release|Any CPU
{44942734-7F1C-4466-84AF-C55AFCCB8092}.Release|x86.Build.0 = Release|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Ad-Hoc|ARM64.ActiveCfg = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Ad-Hoc|ARM64.Build.0 = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.AppStore|ARM.ActiveCfg = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.AppStore|ARM.Build.0 = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.AppStore|ARM64.ActiveCfg = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.AppStore|ARM64.Build.0 = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.AppStore|iPhone.Build.0 = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.AppStore|x64.ActiveCfg = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.AppStore|x64.Build.0 = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.AppStore|x86.ActiveCfg = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.AppStore|x86.Build.0 = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Debug|ARM.Build.0 = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Debug|x64.Build.0 = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Debug|x86.Build.0 = Debug|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Release|ARM.ActiveCfg = Release|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Release|ARM.Build.0 = Release|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Release|ARM64.Build.0 = Release|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Release|iPhone.Build.0 = Release|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Release|x64.ActiveCfg = Release|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Release|x64.Build.0 = Release|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Release|x86.ActiveCfg = Release|Any CPU
+ {8F2C6BDE-385F-42D5-9ABB-E158A3AF2A3B}.Release|x86.Build.0 = Release|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Ad-Hoc|ARM64.ActiveCfg = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Ad-Hoc|ARM64.Build.0 = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.AppStore|ARM.ActiveCfg = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.AppStore|ARM.Build.0 = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.AppStore|ARM64.ActiveCfg = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.AppStore|ARM64.Build.0 = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.AppStore|iPhone.Build.0 = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.AppStore|x64.ActiveCfg = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.AppStore|x64.Build.0 = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.AppStore|x86.ActiveCfg = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.AppStore|x86.Build.0 = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Debug|ARM.Build.0 = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Debug|x64.Build.0 = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Debug|x86.Build.0 = Debug|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Release|ARM.ActiveCfg = Release|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Release|ARM.Build.0 = Release|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Release|ARM64.Build.0 = Release|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Release|iPhone.Build.0 = Release|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Release|x64.ActiveCfg = Release|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Release|x64.Build.0 = Release|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Release|x86.ActiveCfg = Release|Any CPU
+ {D60C19BC-5C16-41FD-8C01-37EABE473FF0}.Release|x86.Build.0 = Release|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Ad-Hoc|Any CPU.Deploy.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Ad-Hoc|ARM.Deploy.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Ad-Hoc|ARM64.ActiveCfg = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Ad-Hoc|ARM64.Build.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Ad-Hoc|ARM64.Deploy.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Ad-Hoc|iPhone.Deploy.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Ad-Hoc|iPhoneSimulator.Deploy.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Ad-Hoc|x64.Deploy.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Ad-Hoc|x86.Deploy.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.AppStore|Any CPU.Build.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.AppStore|Any CPU.Deploy.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.AppStore|ARM.ActiveCfg = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.AppStore|ARM.Build.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.AppStore|ARM.Deploy.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.AppStore|ARM64.ActiveCfg = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.AppStore|ARM64.Build.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.AppStore|ARM64.Deploy.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.AppStore|iPhone.Build.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.AppStore|iPhone.Deploy.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.AppStore|iPhoneSimulator.Deploy.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.AppStore|x64.ActiveCfg = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.AppStore|x64.Build.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.AppStore|x64.Deploy.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.AppStore|x86.ActiveCfg = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.AppStore|x86.Build.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.AppStore|x86.Deploy.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Debug|ARM.ActiveCfg = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Debug|ARM.Build.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Debug|ARM.Deploy.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Debug|ARM64.Deploy.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Debug|iPhone.ActiveCfg = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Debug|iPhone.Build.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Debug|iPhone.Deploy.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Debug|iPhoneSimulator.Deploy.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Debug|x64.Build.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Debug|x64.Deploy.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Debug|x86.Build.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Debug|x86.Deploy.0 = Debug|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Release|ARM.ActiveCfg = Release|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Release|ARM.Build.0 = Release|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Release|ARM.Deploy.0 = Release|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Release|ARM64.Build.0 = Release|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Release|ARM64.Deploy.0 = Release|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Release|iPhone.ActiveCfg = Release|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Release|iPhone.Build.0 = Release|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Release|iPhone.Deploy.0 = Release|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Release|iPhoneSimulator.Deploy.0 = Release|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Release|x64.ActiveCfg = Release|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Release|x64.Build.0 = Release|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Release|x64.Deploy.0 = Release|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Release|x86.ActiveCfg = Release|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Release|x86.Build.0 = Release|Any CPU
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E}.Release|x86.Deploy.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -2029,6 +2234,7 @@ Global
{A1354344-BA2B-4743-9652-82031C174179} = {56F05785-AB19-4429-AC90-B3F8A27CE0DE}
{D92134CD-6769-43EC-B869-509C1135232F} = {21C50AF6-746E-4FA9-9058-AF2301B23ED8}
{44942734-7F1C-4466-84AF-C55AFCCB8092} = {9440E776-42B1-4AB2-942F-87D8266223F7}
+ {C1C3F7EC-38FC-46CA-A97B-1597E7DF018E} = {21C50AF6-746E-4FA9-9058-AF2301B23ED8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {EE3348C3-8B90-461F-A5CC-2FB370C605DD}
diff --git a/playground/EpoxyHello.Avalonia11/Controls/WaitingBlock.xaml.cs b/playground/EpoxyHello.Avalonia11/Controls/WaitingBlock.axaml.cs
similarity index 94%
rename from playground/EpoxyHello.Avalonia11/Controls/WaitingBlock.xaml.cs
rename to playground/EpoxyHello.Avalonia11/Controls/WaitingBlock.axaml.cs
index c9088e48..7fcc1fb3 100644
--- a/playground/EpoxyHello.Avalonia11/Controls/WaitingBlock.xaml.cs
+++ b/playground/EpoxyHello.Avalonia11/Controls/WaitingBlock.axaml.cs
@@ -64,8 +64,8 @@ public WaitingBlock()
}
},
null,
- TimeSpan.Zero,
- TimeSpan.Zero);
+ Timeout.InfiniteTimeSpan,
+ Timeout.InfiniteTimeSpan);
}
private void InitializeComponent() =>
@@ -79,7 +79,7 @@ protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
- this.timer.Change(TimeSpan.Zero, TimeSpan.Zero);
+ this.timer.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);
base.OnDetachedFromVisualTree(e);
}
diff --git a/playground/EpoxyHello.Maui/App.xaml b/playground/EpoxyHello.Maui/App.xaml
new file mode 100644
index 00000000..6a88e37b
--- /dev/null
+++ b/playground/EpoxyHello.Maui/App.xaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/playground/EpoxyHello.Maui/App.xaml.cs b/playground/EpoxyHello.Maui/App.xaml.cs
new file mode 100644
index 00000000..022fe6ff
--- /dev/null
+++ b/playground/EpoxyHello.Maui/App.xaml.cs
@@ -0,0 +1,13 @@
+using Microsoft.Maui.Controls;
+
+namespace EpoxyHello.Maui;
+
+public partial class App : Application
+{
+ public App()
+ {
+ InitializeComponent();
+
+ MainPage = new AppShell();
+ }
+}
diff --git a/playground/EpoxyHello.Maui/AppShell.xaml b/playground/EpoxyHello.Maui/AppShell.xaml
new file mode 100644
index 00000000..ab029ed3
--- /dev/null
+++ b/playground/EpoxyHello.Maui/AppShell.xaml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
diff --git a/playground/EpoxyHello.Maui/AppShell.xaml.cs b/playground/EpoxyHello.Maui/AppShell.xaml.cs
new file mode 100644
index 00000000..f5fdecaa
--- /dev/null
+++ b/playground/EpoxyHello.Maui/AppShell.xaml.cs
@@ -0,0 +1,11 @@
+using Microsoft.Maui.Controls;
+
+namespace EpoxyHello.Maui;
+
+public partial class AppShell : Shell
+{
+ public AppShell()
+ {
+ InitializeComponent();
+ }
+}
diff --git a/playground/EpoxyHello.Maui/Controls/WaitingBlock.xaml b/playground/EpoxyHello.Maui/Controls/WaitingBlock.xaml
new file mode 100644
index 00000000..8be61095
--- /dev/null
+++ b/playground/EpoxyHello.Maui/Controls/WaitingBlock.xaml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/playground/EpoxyHello.Maui/Controls/WaitingBlock.xaml.cs b/playground/EpoxyHello.Maui/Controls/WaitingBlock.xaml.cs
new file mode 100644
index 00000000..68205196
--- /dev/null
+++ b/playground/EpoxyHello.Maui/Controls/WaitingBlock.xaml.cs
@@ -0,0 +1,179 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Epoxy - An independent flexible XAML MVVM library for .NET
+// Copyright (c) Kouji Matsui (@kozy_kekyo, @kekyo@mastodon.cloud)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+using Epoxy;
+using System;
+using System.Threading;
+
+using Microsoft.Maui.Controls;
+using System.Runtime.CompilerServices;
+
+namespace EpoxyHello.Maui.Controls;
+
+///
+/// Interaction logic for WaitingBlock.xaml
+///
+public sealed partial class WaitingBlock : ContentView
+{
+ public static readonly BindableProperty CellBrush0Property =
+ BindableProperty.Create(nameof(CellBrush0), typeof(Brush), typeof(WaitingBlock), Brush.Gray);
+ public static readonly BindableProperty CellBrush1Property =
+ BindableProperty.Create(nameof(CellBrush1), typeof(Brush), typeof(WaitingBlock), Brush.Gray);
+ public static readonly BindableProperty CellBrush2Property =
+ BindableProperty.Create(nameof(CellBrush2), typeof(Brush), typeof(WaitingBlock), Brush.Gray);
+ public static readonly BindableProperty CellBrush3Property =
+ BindableProperty.Create(nameof(CellBrush3), typeof(Brush), typeof(WaitingBlock), Brush.Gray);
+ public static readonly BindableProperty CellBrush4Property =
+ BindableProperty.Create(nameof(CellBrush4), typeof(Brush), typeof(WaitingBlock), Brush.Gray);
+ public static readonly BindableProperty CellBrush5Property =
+ BindableProperty.Create(nameof(CellBrush5), typeof(Brush), typeof(WaitingBlock), Brush.Gray);
+ public static readonly BindableProperty CellBrush6Property =
+ BindableProperty.Create(nameof(CellBrush6), typeof(Brush), typeof(WaitingBlock), Brush.Gray);
+ public static readonly BindableProperty CellBrush7Property =
+ BindableProperty.Create(nameof(CellBrush7), typeof(Brush), typeof(WaitingBlock), Brush.Gray);
+
+ private int currentPosition;
+ private Timer timer;
+
+ public WaitingBlock()
+ {
+ this.InitializeComponent();
+ this.BindingContext = this;
+
+ this.timer = new Timer(async _ =>
+ {
+ if (await UIThread.TryBind())
+ {
+ for (var index = 0; index < 8; index++)
+ {
+ var brush = index == this.currentPosition ? Brush.Red : Brush.Gray;
+ {
+ switch (index)
+ {
+ case 0:
+ this.CellBrush0 = brush;
+ break;
+ case 1:
+ this.CellBrush1 = brush;
+ break;
+ case 2:
+ this.CellBrush2 = brush;
+ break;
+ case 3:
+ this.CellBrush3 = brush;
+ break;
+ case 4:
+ this.CellBrush4 = brush;
+ break;
+ case 5:
+ this.CellBrush5 = brush;
+ break;
+ case 6:
+ this.CellBrush6 = brush;
+ break;
+ case 7:
+ this.CellBrush7 = brush;
+ break;
+ }
+ }
+ }
+
+ if (++this.currentPosition >= 8)
+ {
+ this.currentPosition = 0;
+ }
+ }
+ },
+ null,
+ Timeout.InfiniteTimeSpan,
+ Timeout.InfiniteTimeSpan);
+ }
+
+ protected override void OnPropertyChanging([CallerMemberName] string propertyName = null!)
+ {
+ if (propertyName == nameof(Parent))
+ {
+ if (this.Parent != null)
+ {
+ this.timer.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);
+ }
+ }
+ base.OnPropertyChanging(propertyName);
+ }
+
+ protected override void OnPropertyChanged([CallerMemberName] string propertyName = null!)
+ {
+ base.OnPropertyChanged(propertyName);
+ if (propertyName == nameof(Parent))
+ {
+ if (this.Parent != null)
+ {
+ this.timer.Change(TimeSpan.Zero, TimeSpan.FromMilliseconds(300));
+ }
+ }
+ }
+
+ public Brush CellBrush0
+ {
+ get => (Brush?)GetValue(CellBrush0Property) ?? Brush.Gray;
+ set => SetValue(CellBrush0Property, value);
+ }
+
+ public Brush CellBrush1
+ {
+ get => (Brush?)GetValue(CellBrush1Property) ?? Brush.Gray;
+ set => SetValue(CellBrush1Property, value);
+ }
+
+ public Brush CellBrush2
+ {
+ get => (Brush?)GetValue(CellBrush2Property) ?? Brush.Gray;
+ set => SetValue(CellBrush2Property, value);
+ }
+
+ public Brush CellBrush3
+ {
+ get => (Brush?)GetValue(CellBrush3Property) ?? Brush.Gray;
+ set => SetValue(CellBrush3Property, value);
+ }
+
+ public Brush CellBrush4
+ {
+ get => (Brush?)GetValue(CellBrush4Property) ?? Brush.Gray;
+ set => SetValue(CellBrush4Property, value);
+ }
+
+ public Brush CellBrush5
+ {
+ get => (Brush?)GetValue(CellBrush5Property) ?? Brush.Gray;
+ set => SetValue(CellBrush5Property, value);
+ }
+
+ public Brush CellBrush6
+ {
+ get => (Brush?)GetValue(CellBrush6Property) ?? Brush.Gray;
+ set => SetValue(CellBrush6Property, value);
+ }
+
+ public Brush CellBrush7
+ {
+ get => (Brush?)GetValue(CellBrush7Property) ?? Brush.Gray;
+ set => SetValue(CellBrush7Property, value);
+ }
+}
diff --git a/playground/EpoxyHello.Maui/EpoxyHello.Maui.csproj b/playground/EpoxyHello.Maui/EpoxyHello.Maui.csproj
new file mode 100644
index 00000000..9dd1d7eb
--- /dev/null
+++ b/playground/EpoxyHello.Maui/EpoxyHello.Maui.csproj
@@ -0,0 +1,76 @@
+
+
+
+
+ net8.0-android;net8.0-ios;net8.0-maccatalyst
+ $(TargetFrameworks);net8.0-windows10.0.19041.0
+
+
+
+
+
+
+ Exe
+ EpoxyHello.Maui
+ true
+ true
+ enable
+
+
+ EpoxyHello.Maui
+
+
+ com.companyname.epoxyhello.maui
+
+
+
+
+ 11.0
+ 13.1
+ 21.0
+ 10.0.17763.0
+ 10.0.17763.0
+ 6.5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)\..\..\src\Epoxy.Build\bin\$(Configuration)\$(_EB_PlatformName)'))
+
+
diff --git a/playground/EpoxyHello.Maui/MauiProgram.cs b/playground/EpoxyHello.Maui/MauiProgram.cs
new file mode 100644
index 00000000..b80cbcf8
--- /dev/null
+++ b/playground/EpoxyHello.Maui/MauiProgram.cs
@@ -0,0 +1,26 @@
+using Microsoft.Extensions.Logging;
+using Microsoft.Maui.Controls.Hosting;
+using Microsoft.Maui.Hosting;
+
+namespace EpoxyHello.Maui;
+
+public static class MauiProgram
+{
+ public static MauiApp CreateMauiApp()
+ {
+ var builder = MauiApp.CreateBuilder();
+ builder
+ .UseMauiApp()
+ .ConfigureFonts(fonts =>
+ {
+ fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
+ fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
+ });
+
+#if DEBUG
+ builder.Logging.AddDebug();
+#endif
+
+ return builder.Build();
+ }
+}
diff --git a/playground/EpoxyHello.Maui/Platforms/Android/AndroidManifest.xml b/playground/EpoxyHello.Maui/Platforms/Android/AndroidManifest.xml
new file mode 100644
index 00000000..e9937ad7
--- /dev/null
+++ b/playground/EpoxyHello.Maui/Platforms/Android/AndroidManifest.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/playground/EpoxyHello.Maui/Platforms/Android/MainActivity.cs b/playground/EpoxyHello.Maui/Platforms/Android/MainActivity.cs
new file mode 100644
index 00000000..f7d8b22b
--- /dev/null
+++ b/playground/EpoxyHello.Maui/Platforms/Android/MainActivity.cs
@@ -0,0 +1,11 @@
+using Android.App;
+using Android.Content.PM;
+using Android.OS;
+using Microsoft.Maui;
+
+namespace EpoxyHello.Maui;
+
+[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
+public class MainActivity : MauiAppCompatActivity
+{
+}
diff --git a/playground/EpoxyHello.Maui/Platforms/Android/MainApplication.cs b/playground/EpoxyHello.Maui/Platforms/Android/MainApplication.cs
new file mode 100644
index 00000000..362b250c
--- /dev/null
+++ b/playground/EpoxyHello.Maui/Platforms/Android/MainApplication.cs
@@ -0,0 +1,18 @@
+using Android.App;
+using Android.Runtime;
+using Microsoft.Maui;
+using Microsoft.Maui.Hosting;
+using System;
+
+namespace EpoxyHello.Maui;
+
+[Application]
+public class MainApplication : MauiApplication
+{
+ public MainApplication(IntPtr handle, JniHandleOwnership ownership)
+ : base(handle, ownership)
+ {
+ }
+
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+}
diff --git a/playground/EpoxyHello.Maui/Platforms/Android/Resources/values/colors.xml b/playground/EpoxyHello.Maui/Platforms/Android/Resources/values/colors.xml
new file mode 100644
index 00000000..c04d7492
--- /dev/null
+++ b/playground/EpoxyHello.Maui/Platforms/Android/Resources/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #512BD4
+ #2B0B98
+ #2B0B98
+
\ No newline at end of file
diff --git a/playground/EpoxyHello.Maui/Platforms/MacCatalyst/AppDelegate.cs b/playground/EpoxyHello.Maui/Platforms/MacCatalyst/AppDelegate.cs
new file mode 100644
index 00000000..343a8227
--- /dev/null
+++ b/playground/EpoxyHello.Maui/Platforms/MacCatalyst/AppDelegate.cs
@@ -0,0 +1,11 @@
+using Foundation;
+using Microsoft.Maui;
+using Microsoft.Maui.Hosting;
+
+namespace EpoxyHello.Maui;
+
+[Register("AppDelegate")]
+public class AppDelegate : MauiUIApplicationDelegate
+{
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+}
diff --git a/playground/EpoxyHello.Maui/Platforms/MacCatalyst/Entitlements.plist b/playground/EpoxyHello.Maui/Platforms/MacCatalyst/Entitlements.plist
new file mode 100644
index 00000000..de4adc94
--- /dev/null
+++ b/playground/EpoxyHello.Maui/Platforms/MacCatalyst/Entitlements.plist
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ com.apple.security.app-sandbox
+
+
+ com.apple.security.network.client
+
+
+
+
diff --git a/playground/EpoxyHello.Maui/Platforms/MacCatalyst/Info.plist b/playground/EpoxyHello.Maui/Platforms/MacCatalyst/Info.plist
new file mode 100644
index 00000000..72689771
--- /dev/null
+++ b/playground/EpoxyHello.Maui/Platforms/MacCatalyst/Info.plist
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UIDeviceFamily
+
+ 2
+
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ XSAppIconAssets
+ Assets.xcassets/appicon.appiconset
+
+
diff --git a/playground/EpoxyHello.Maui/Platforms/MacCatalyst/Program.cs b/playground/EpoxyHello.Maui/Platforms/MacCatalyst/Program.cs
new file mode 100644
index 00000000..df5839ab
--- /dev/null
+++ b/playground/EpoxyHello.Maui/Platforms/MacCatalyst/Program.cs
@@ -0,0 +1,16 @@
+using ObjCRuntime;
+using UIKit;
+
+namespace EpoxyHello.Maui
+{
+ public class Program
+ {
+ // This is the main entry point of the application.
+ static void Main(string[] args)
+ {
+ // if you want to use a different Application Delegate class from "AppDelegate"
+ // you can specify it here.
+ UIApplication.Main(args, null, typeof(AppDelegate));
+ }
+ }
+}
diff --git a/playground/EpoxyHello.Maui/Platforms/Tizen/Main.cs b/playground/EpoxyHello.Maui/Platforms/Tizen/Main.cs
new file mode 100644
index 00000000..62d4cadd
--- /dev/null
+++ b/playground/EpoxyHello.Maui/Platforms/Tizen/Main.cs
@@ -0,0 +1,16 @@
+using Microsoft.Maui;
+using Microsoft.Maui.Hosting;
+using System;
+
+namespace EpoxyHello.Maui;
+
+internal class Program : MauiApplication
+{
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+
+ static void Main(string[] args)
+ {
+ var app = new Program();
+ app.Run(args);
+ }
+}
diff --git a/playground/EpoxyHello.Maui/Platforms/Tizen/tizen-manifest.xml b/playground/EpoxyHello.Maui/Platforms/Tizen/tizen-manifest.xml
new file mode 100644
index 00000000..10264d08
--- /dev/null
+++ b/playground/EpoxyHello.Maui/Platforms/Tizen/tizen-manifest.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+ maui-appicon-placeholder
+
+
+
+
+ http://tizen.org/privilege/internet
+
+
+
+
\ No newline at end of file
diff --git a/playground/EpoxyHello.Maui/Platforms/Windows/App.xaml b/playground/EpoxyHello.Maui/Platforms/Windows/App.xaml
new file mode 100644
index 00000000..c611529c
--- /dev/null
+++ b/playground/EpoxyHello.Maui/Platforms/Windows/App.xaml
@@ -0,0 +1,8 @@
+
+
+
diff --git a/playground/EpoxyHello.Maui/Platforms/Windows/App.xaml.cs b/playground/EpoxyHello.Maui/Platforms/Windows/App.xaml.cs
new file mode 100644
index 00000000..ccfc374c
--- /dev/null
+++ b/playground/EpoxyHello.Maui/Platforms/Windows/App.xaml.cs
@@ -0,0 +1,25 @@
+using Microsoft.Maui;
+using Microsoft.Maui.Hosting;
+using Microsoft.UI.Xaml;
+
+// To learn more about WinUI, the WinUI project structure,
+// and more about our project templates, see: http://aka.ms/winui-project-info.
+
+namespace EpoxyHello.Maui.WinUI;
+
+///
+/// Provides application-specific behavior to supplement the default Application class.
+///
+public partial class App : MauiWinUIApplication
+{
+ ///
+ /// Initializes the singleton application object. This is the first line of authored code
+ /// executed, and as such is the logical equivalent of main() or WinMain().
+ ///
+ public App()
+ {
+ this.InitializeComponent();
+ }
+
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+}
diff --git a/playground/EpoxyHello.Maui/Platforms/Windows/Package.appxmanifest b/playground/EpoxyHello.Maui/Platforms/Windows/Package.appxmanifest
new file mode 100644
index 00000000..31c8b380
--- /dev/null
+++ b/playground/EpoxyHello.Maui/Platforms/Windows/Package.appxmanifest
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+ $placeholder$
+ User Name
+ $placeholder$.png
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/playground/EpoxyHello.Maui/Platforms/Windows/app.manifest b/playground/EpoxyHello.Maui/Platforms/Windows/app.manifest
new file mode 100644
index 00000000..2ee501e8
--- /dev/null
+++ b/playground/EpoxyHello.Maui/Platforms/Windows/app.manifest
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+ true/PM
+ PerMonitorV2, PerMonitor
+
+
+
diff --git a/playground/EpoxyHello.Maui/Platforms/iOS/AppDelegate.cs b/playground/EpoxyHello.Maui/Platforms/iOS/AppDelegate.cs
new file mode 100644
index 00000000..343a8227
--- /dev/null
+++ b/playground/EpoxyHello.Maui/Platforms/iOS/AppDelegate.cs
@@ -0,0 +1,11 @@
+using Foundation;
+using Microsoft.Maui;
+using Microsoft.Maui.Hosting;
+
+namespace EpoxyHello.Maui;
+
+[Register("AppDelegate")]
+public class AppDelegate : MauiUIApplicationDelegate
+{
+ protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
+}
diff --git a/playground/EpoxyHello.Maui/Platforms/iOS/Info.plist b/playground/EpoxyHello.Maui/Platforms/iOS/Info.plist
new file mode 100644
index 00000000..0004a4fd
--- /dev/null
+++ b/playground/EpoxyHello.Maui/Platforms/iOS/Info.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ LSRequiresIPhoneOS
+
+ UIDeviceFamily
+
+ 1
+ 2
+
+ UIRequiredDeviceCapabilities
+
+ arm64
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ XSAppIconAssets
+ Assets.xcassets/appicon.appiconset
+
+
diff --git a/playground/EpoxyHello.Maui/Platforms/iOS/Program.cs b/playground/EpoxyHello.Maui/Platforms/iOS/Program.cs
new file mode 100644
index 00000000..7b617ba2
--- /dev/null
+++ b/playground/EpoxyHello.Maui/Platforms/iOS/Program.cs
@@ -0,0 +1,15 @@
+using ObjCRuntime;
+using UIKit;
+
+namespace EpoxyHello.Maui;
+
+public class Program
+{
+ // This is the main entry point of the application.
+ static void Main(string[] args)
+ {
+ // if you want to use a different Application Delegate class from "AppDelegate"
+ // you can specify it here.
+ UIApplication.Main(args, null, typeof(AppDelegate));
+ }
+}
diff --git a/playground/EpoxyHello.Maui/Properties/launchSettings.json b/playground/EpoxyHello.Maui/Properties/launchSettings.json
new file mode 100644
index 00000000..edf8aadc
--- /dev/null
+++ b/playground/EpoxyHello.Maui/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "Windows Machine": {
+ "commandName": "MsixPackage",
+ "nativeDebugging": false
+ }
+ }
+}
\ No newline at end of file
diff --git a/playground/EpoxyHello.Maui/Resources/AppIcon/appicon.svg b/playground/EpoxyHello.Maui/Resources/AppIcon/appicon.svg
new file mode 100644
index 00000000..9d63b651
--- /dev/null
+++ b/playground/EpoxyHello.Maui/Resources/AppIcon/appicon.svg
@@ -0,0 +1,4 @@
+
+
\ No newline at end of file
diff --git a/playground/EpoxyHello.Maui/Resources/AppIcon/appiconfg.svg b/playground/EpoxyHello.Maui/Resources/AppIcon/appiconfg.svg
new file mode 100644
index 00000000..21dfb25f
--- /dev/null
+++ b/playground/EpoxyHello.Maui/Resources/AppIcon/appiconfg.svg
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/playground/EpoxyHello.Maui/Resources/Fonts/OpenSans-Regular.ttf b/playground/EpoxyHello.Maui/Resources/Fonts/OpenSans-Regular.ttf
new file mode 100644
index 00000000..3b69f736
Binary files /dev/null and b/playground/EpoxyHello.Maui/Resources/Fonts/OpenSans-Regular.ttf differ
diff --git a/playground/EpoxyHello.Maui/Resources/Fonts/OpenSans-Semibold.ttf b/playground/EpoxyHello.Maui/Resources/Fonts/OpenSans-Semibold.ttf
new file mode 100644
index 00000000..0fc5a9aa
Binary files /dev/null and b/playground/EpoxyHello.Maui/Resources/Fonts/OpenSans-Semibold.ttf differ
diff --git a/playground/EpoxyHello.Maui/Resources/Images/dotnet_bot.png b/playground/EpoxyHello.Maui/Resources/Images/dotnet_bot.png
new file mode 100644
index 00000000..f93ce025
Binary files /dev/null and b/playground/EpoxyHello.Maui/Resources/Images/dotnet_bot.png differ
diff --git a/playground/EpoxyHello.Maui/Resources/Raw/AboutAssets.txt b/playground/EpoxyHello.Maui/Resources/Raw/AboutAssets.txt
new file mode 100644
index 00000000..15d62448
--- /dev/null
+++ b/playground/EpoxyHello.Maui/Resources/Raw/AboutAssets.txt
@@ -0,0 +1,15 @@
+Any raw assets you want to be deployed with your application can be placed in
+this directory (and child directories). Deployment of the asset to your application
+is automatically handled by the following `MauiAsset` Build Action within your `.csproj`.
+
+
+
+These files will be deployed with you package and will be accessible using Essentials:
+
+ async Task LoadMauiAsset()
+ {
+ using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt");
+ using var reader = new StreamReader(stream);
+
+ var contents = reader.ReadToEnd();
+ }
diff --git a/playground/EpoxyHello.Maui/Resources/Splash/splash.svg b/playground/EpoxyHello.Maui/Resources/Splash/splash.svg
new file mode 100644
index 00000000..21dfb25f
--- /dev/null
+++ b/playground/EpoxyHello.Maui/Resources/Splash/splash.svg
@@ -0,0 +1,8 @@
+
+
+
\ No newline at end of file
diff --git a/playground/EpoxyHello.Maui/Resources/Styles/Colors.xaml b/playground/EpoxyHello.Maui/Resources/Styles/Colors.xaml
new file mode 100644
index 00000000..30307a5d
--- /dev/null
+++ b/playground/EpoxyHello.Maui/Resources/Styles/Colors.xaml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+ #512BD4
+ #ac99ea
+ #242424
+ #DFD8F7
+ #9880e5
+ #2B0B98
+
+ White
+ Black
+ #D600AA
+ #190649
+ #1f1f1f
+
+ #E1E1E1
+ #C8C8C8
+ #ACACAC
+ #919191
+ #6E6E6E
+ #404040
+ #212121
+ #141414
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/playground/EpoxyHello.Maui/Resources/Styles/Styles.xaml b/playground/EpoxyHello.Maui/Resources/Styles/Styles.xaml
new file mode 100644
index 00000000..e0d36bb7
--- /dev/null
+++ b/playground/EpoxyHello.Maui/Resources/Styles/Styles.xaml
@@ -0,0 +1,426 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/playground/EpoxyHello.Maui/ViewModels/ItemViewModel.cs b/playground/EpoxyHello.Maui/ViewModels/ItemViewModel.cs
new file mode 100644
index 00000000..97b69c9e
--- /dev/null
+++ b/playground/EpoxyHello.Maui/ViewModels/ItemViewModel.cs
@@ -0,0 +1,33 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Epoxy - An independent flexible XAML MVVM library for .NET
+// Copyright (c) Kouji Matsui (@kozy_kekyo, @kekyo@mastodon.cloud)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+using Epoxy;
+using Microsoft.Maui.Controls;
+
+namespace EpoxyHello.Maui.ViewModels;
+
+[ViewModel]
+public sealed class ItemViewModel
+{
+ public string? Title { get; set; }
+
+ public ImageSource? Image { get; set; }
+
+ public int Score { get; set; }
+}
diff --git a/playground/EpoxyHello.Maui/ViewModels/MainContentPageViewModel.cs b/playground/EpoxyHello.Maui/ViewModels/MainContentPageViewModel.cs
new file mode 100644
index 00000000..cbcbb0ce
--- /dev/null
+++ b/playground/EpoxyHello.Maui/ViewModels/MainContentPageViewModel.cs
@@ -0,0 +1,109 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Epoxy - An independent flexible XAML MVVM library for .NET
+// Copyright (c) Kouji Matsui (@kozy_kekyo, @kekyo@mastodon.cloud)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+using System;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.Maui.Controls;
+
+using Epoxy;
+using EpoxyHello.Models;
+using EpoxyHello.Maui.Controls;
+
+using Command = Epoxy.Command;
+
+namespace EpoxyHello.Maui.ViewModels;
+
+[ViewModel]
+public sealed class MainContentPageViewModel
+{
+ public Command Ready { get; }
+
+ public bool IsEnabled { get; private set; }
+
+ public ObservableCollection Items { get; } = new();
+
+ public Command Fetch { get; }
+
+ public Pile IndicatorPile { get; } = Pile.Factory.Create();
+
+ public MainContentPageViewModel()
+ {
+ // A handler for page appearing
+ this.Ready = Command.Factory.Create(() =>
+ {
+ this.IsEnabled = true;
+ return default;
+ });
+
+ // A handler for fetch button
+ this.Fetch = Command.Factory.Create(async () =>
+ {
+ this.IsEnabled = false;
+
+ await this.IndicatorPile.RentAsync(async indicator =>
+ {
+ // Show WaitingBlock control
+ var waitingBlock = new WaitingBlock();
+ indicator.Children.Add(waitingBlock);
+
+ try
+ {
+ // Uses The Cat API
+ var cats = await TheCatAPI.FetchTheCatsAsync(10);
+
+ this.Items.Clear();
+
+ static async ValueTask FetchImageAsync(Uri url)
+ {
+ var data = await TheCatAPI.FetchImageAsync(url);
+ return new StreamImageSource
+ {
+ Stream = _ => Task.FromResult((Stream)new MemoryStream(data))
+ };
+ }
+
+ foreach (var cat in cats)
+ {
+ if (cat.Url is { } url)
+ {
+ var bleed = cat?.Bleeds.FirstOrDefault();
+ this.Items.Add(new ItemViewModel
+ {
+ Title = bleed?.Description ?? bleed?.Temperament ?? "(No comment)",
+ Score = bleed?.Intelligence ?? 5,
+ Image = await FetchImageAsync(url)
+ });
+ }
+ }
+ }
+ finally
+ {
+ // Hide WaitingBlock control
+ indicator.Children.Remove(waitingBlock);
+
+ // Re-enable button
+ this.IsEnabled = true;
+ }
+ });
+ });
+ }
+}
diff --git a/playground/EpoxyHello.Maui/Views/Converters/ScoreToBrushConverter.cs b/playground/EpoxyHello.Maui/Views/Converters/ScoreToBrushConverter.cs
new file mode 100644
index 00000000..d4d609cc
--- /dev/null
+++ b/playground/EpoxyHello.Maui/Views/Converters/ScoreToBrushConverter.cs
@@ -0,0 +1,36 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Epoxy - An independent flexible XAML MVVM library for .NET
+// Copyright (c) Kouji Matsui (@kozy_kekyo, @kekyo@mastodon.cloud)
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+using Epoxy;
+using Microsoft.Maui.Controls;
+using Microsoft.Maui.Graphics;
+
+namespace EpoxyHello.Maui.Views.Converters;
+
+public sealed class ScoreToBrushConverter : ValueConverter
+{
+ private static readonly Brush yellow = new SolidColorBrush(Color.FromRgba(96, 96, 0, 255));
+ private static readonly Brush gray = new SolidColorBrush(Color.FromRgba(96, 96, 96, 255));
+
+ public override bool TryConvert(int from, out Brush result)
+ {
+ result = from >= 5 ? yellow : gray;
+ return true;
+ }
+}
diff --git a/playground/EpoxyHello.Maui/Views/MainPage.xaml b/playground/EpoxyHello.Maui/Views/MainPage.xaml
new file mode 100644
index 00000000..26798dcd
--- /dev/null
+++ b/playground/EpoxyHello.Maui/Views/MainPage.xaml
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/playground/EpoxyHello.Maui/Views/MainPage.xaml.cs b/playground/EpoxyHello.Maui/Views/MainPage.xaml.cs
new file mode 100644
index 00000000..b2eb9d65
--- /dev/null
+++ b/playground/EpoxyHello.Maui/Views/MainPage.xaml.cs
@@ -0,0 +1,9 @@
+using Microsoft.Maui.Controls;
+
+namespace EpoxyHello.Maui.Views;
+
+public partial class MainPage : ContentPage
+{
+ public MainPage() =>
+ this.InitializeComponent();
+}
diff --git a/src/Epoxy.Core.Maui/Epoxy.Core.Maui.csproj b/src/Epoxy.Core.Maui/Epoxy.Core.Maui.csproj
new file mode 100644
index 00000000..acfc8a27
--- /dev/null
+++ b/src/Epoxy.Core.Maui/Epoxy.Core.Maui.csproj
@@ -0,0 +1,36 @@
+
+
+
+ Epoxy.Core
+ netstandard2.0;netstandard2.1;net7.0;net8.0
+ Epoxy.Core.Maui
+ Epoxy.Core.Maui
+ $(PackageTags);maui
+
+ false
+
+
+
+ DEBUG;TRACE;MAUI
+
+
+ TRACE;MAUI
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Epoxy.Maui/Epoxy.Maui.csproj b/src/Epoxy.Maui/Epoxy.Maui.csproj
new file mode 100644
index 00000000..7a84033d
--- /dev/null
+++ b/src/Epoxy.Maui/Epoxy.Maui.csproj
@@ -0,0 +1,35 @@
+
+
+
+ Epoxy
+ netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0
+ Epoxy.Maui
+
+ Epoxy.Maui.nuspec
+ true
+ false
+
+
+
+ DEBUG;TRACE;MAUI
+
+
+ TRACE;MAUI
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Epoxy.Maui/Epoxy.Maui.nuspec b/src/Epoxy.Maui/Epoxy.Maui.nuspec
new file mode 100644
index 00000000..14ac5e71
--- /dev/null
+++ b/src/Epoxy.Maui/Epoxy.Maui.nuspec
@@ -0,0 +1,42 @@
+
+
+
+ Epoxy.Maui
+ $PackageVersion$
+ Kouji Matsui (@kozy_kekyo, @kekyo@mastodon.cloud)
+ false
+ Apache-2.0
+ https://licenses.nuget.org/Apache-2.0
+ Epoxy.100.png
+ https://github.com/kekyo/Epoxy.git
+ An independent flexible XAML MVVM library for .NET
+ $PackageReleaseNotes$
+ Copyright (c) Kouji Matsui
+ mvvm viewmodel easy simple combinable async awaitable command maui
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Epoxy.Xamarin.Forms/Properties/AssemblyInfo.cs b/src/Epoxy.Xamarin.Forms/Properties/AssemblyInfo.cs
index 07ead915..4c38144f 100644
--- a/src/Epoxy.Xamarin.Forms/Properties/AssemblyInfo.cs
+++ b/src/Epoxy.Xamarin.Forms/Properties/AssemblyInfo.cs
@@ -19,7 +19,13 @@
#nullable enable
+#if XAMARIN_FORMS
using Xamarin.Forms;
+#endif
+
+#if MAUI
+using Microsoft.Maui.Controls;
+#endif
[assembly: XmlnsDefinition("https://github.com/kekyo/Epoxy", "Epoxy")]
[assembly: XmlnsPrefix("https://github.com/kekyo/Epoxy", "epoxy")]