workspace-architect 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +1 -0
- package/.gitattributes +1 -0
- package/.github/workflows/manual-publish.yml +36 -0
- package/.github/workflows/sync-and-publish.yml +58 -0
- package/.release-it.json +20 -0
- package/CHANGELOG.md +43 -0
- package/README.md +62 -0
- package/assets/chatmodes/4.1-Beast.chatmode.md +152 -0
- package/assets/chatmodes/Thinking-Beast-Mode.chatmode.md +337 -0
- package/assets/chatmodes/Ultimate-Transparent-Thinking-Beast-Mode.chatmode.md +644 -0
- package/assets/chatmodes/accessibility.chatmode.md +298 -0
- package/assets/chatmodes/address-comments.chatmode.md +59 -0
- package/assets/chatmodes/aem-frontend-specialist.chatmode.md +385 -0
- package/assets/chatmodes/api-architect.chatmode.md +40 -0
- package/assets/chatmodes/atlassian-requirements-to-jira.chatmode.md +444 -0
- package/assets/chatmodes/azure-logic-apps-expert.chatmode.md +100 -0
- package/assets/chatmodes/azure-principal-architect.chatmode.md +58 -0
- package/assets/chatmodes/azure-saas-architect.chatmode.md +118 -0
- package/assets/chatmodes/azure-verified-modules-bicep.chatmode.md +44 -0
- package/assets/chatmodes/azure-verified-modules-terraform.chatmode.md +58 -0
- package/assets/chatmodes/bicep-implement.chatmode.md +40 -0
- package/assets/chatmodes/bicep-plan.chatmode.md +112 -0
- package/assets/chatmodes/blueprint-mode-codex.chatmode.md +110 -0
- package/assets/chatmodes/blueprint-mode.chatmode.md +171 -0
- package/assets/chatmodes/clojure-interactive-programming.chatmode.md +174 -0
- package/assets/chatmodes/code-tour.chatmode.md +205 -0
- package/assets/chatmodes/critical-thinking.chatmode.md +23 -0
- package/assets/chatmodes/csharp-dotnet-janitor.chatmode.md +83 -0
- package/assets/chatmodes/csharp-mcp-expert.chatmode.md +69 -0
- package/assets/chatmodes/debug.chatmode.md +79 -0
- package/assets/chatmodes/declarative-agents-architect.chatmode.md +76 -0
- package/assets/chatmodes/demonstrate-understanding.chatmode.md +60 -0
- package/assets/chatmodes/dotnet-upgrade.chatmode.md +222 -0
- package/assets/chatmodes/drupal-expert.chatmode.md +687 -0
- package/assets/chatmodes/electron-angular-native.chatmode.md +285 -0
- package/assets/chatmodes/expert-cpp-software-engineer.chatmode.md +27 -0
- package/assets/chatmodes/expert-dotnet-software-engineer.chatmode.md +22 -0
- package/assets/chatmodes/expert-nextjs-developer.chatmode.md +477 -0
- package/assets/chatmodes/expert-react-frontend-engineer.chatmode.md +738 -0
- package/assets/chatmodes/gilfoyle.chatmode.md +66 -0
- package/assets/chatmodes/go-mcp-expert.chatmode.md +122 -0
- package/assets/chatmodes/gpt-5-beast-mode.chatmode.md +109 -0
- package/assets/chatmodes/hlbpa.chatmode.md +232 -0
- package/assets/chatmodes/implementation-plan.chatmode.md +159 -0
- package/assets/chatmodes/janitor.chatmode.md +89 -0
- package/assets/chatmodes/java-mcp-expert.chatmode.md +325 -0
- package/assets/chatmodes/kotlin-mcp-expert.chatmode.md +181 -0
- package/assets/chatmodes/kusto-assistant.chatmode.md +143 -0
- package/assets/chatmodes/laravel-expert-agent.chatmode.md +628 -0
- package/assets/chatmodes/mentor.chatmode.md +32 -0
- package/assets/chatmodes/meta-agentic-project-scaffold.chatmode.md +15 -0
- package/assets/chatmodes/microsoft-agent-framework-dotnet.chatmode.md +62 -0
- package/assets/chatmodes/microsoft-agent-framework-python.chatmode.md +62 -0
- package/assets/chatmodes/microsoft-study-mode.chatmode.md +32 -0
- package/assets/chatmodes/microsoft_learn_contributor.chatmode.md +388 -0
- package/assets/chatmodes/ms-sql-dba.chatmode.md +25 -0
- package/assets/chatmodes/php-mcp-expert.chatmode.md +498 -0
- package/assets/chatmodes/pimcore-expert.chatmode.md +869 -0
- package/assets/chatmodes/plan.chatmode.md +114 -0
- package/assets/chatmodes/planner.chatmode.md +14 -0
- package/assets/chatmodes/playwright-tester.chatmode.md +13 -0
- package/assets/chatmodes/postgresql-dba.chatmode.md +17 -0
- package/assets/chatmodes/power-bi-data-modeling-expert.chatmode.md +319 -0
- package/assets/chatmodes/power-bi-dax-expert.chatmode.md +334 -0
- package/assets/chatmodes/power-bi-performance-expert.chatmode.md +533 -0
- package/assets/chatmodes/power-bi-visualization-expert.chatmode.md +549 -0
- package/assets/chatmodes/power-platform-expert.chatmode.md +116 -0
- package/assets/chatmodes/power-platform-mcp-integration-expert.chatmode.md +149 -0
- package/assets/chatmodes/prd.chatmode.md +201 -0
- package/assets/chatmodes/principal-software-engineer.chatmode.md +41 -0
- package/assets/chatmodes/prompt-builder.chatmode.md +352 -0
- package/assets/chatmodes/prompt-engineer.chatmode.md +72 -0
- package/assets/chatmodes/python-mcp-expert.chatmode.md +99 -0
- package/assets/chatmodes/refine-issue.chatmode.md +34 -0
- package/assets/chatmodes/research-technical-spike.chatmode.md +169 -0
- package/assets/chatmodes/ruby-mcp-expert.chatmode.md +346 -0
- package/assets/chatmodes/rust-gpt-4.1-beast-mode.chatmode.md +197 -0
- package/assets/chatmodes/rust-mcp-expert.chatmode.md +465 -0
- package/assets/chatmodes/search-ai-optimization-expert.chatmode.md +227 -0
- package/assets/chatmodes/semantic-kernel-dotnet.chatmode.md +31 -0
- package/assets/chatmodes/semantic-kernel-python.chatmode.md +28 -0
- package/assets/chatmodes/shopify-expert.chatmode.md +681 -0
- package/assets/chatmodes/simple-app-idea-generator.chatmode.md +134 -0
- package/assets/chatmodes/software-engineer-agent-v1.chatmode.md +164 -0
- package/assets/chatmodes/specification.chatmode.md +127 -0
- package/assets/chatmodes/swift-mcp-expert.chatmode.md +240 -0
- package/assets/chatmodes/task-planner.chatmode.md +374 -0
- package/assets/chatmodes/task-researcher.chatmode.md +254 -0
- package/assets/chatmodes/tdd-green.chatmode.md +59 -0
- package/assets/chatmodes/tdd-red.chatmode.md +59 -0
- package/assets/chatmodes/tdd-refactor.chatmode.md +84 -0
- package/assets/chatmodes/tech-debt-remediation-plan.chatmode.md +49 -0
- package/assets/chatmodes/terraform-azure-implement.chatmode.md +104 -0
- package/assets/chatmodes/terraform-azure-planning.chatmode.md +157 -0
- package/assets/chatmodes/typescript-mcp-expert.chatmode.md +91 -0
- package/assets/chatmodes/voidbeast-gpt41enhanced.chatmode.md +230 -0
- package/assets/chatmodes/wg-code-alchemist.chatmode.md +61 -0
- package/assets/chatmodes/wg-code-sentinel.chatmode.md +55 -0
- package/assets/collections/ai-prompt-engineering.json +18 -0
- package/assets/collections/angular-development.json +7 -0
- package/assets/collections/azure-cloud-architect.json +29 -0
- package/assets/collections/cpp-development.json +6 -0
- package/assets/collections/database-administration.json +8 -0
- package/assets/collections/devops-sre.json +11 -0
- package/assets/collections/dotnet-development.json +22 -0
- package/assets/collections/general-productivity.json +9 -0
- package/assets/collections/go-development.json +7 -0
- package/assets/collections/java-spring-developer.json +26 -0
- package/assets/collections/learning-mentoring.json +10 -0
- package/assets/collections/legacy-migration.json +4 -0
- package/assets/collections/mcp-specialist.json +41 -0
- package/assets/collections/mobile-development.json +4 -0
- package/assets/collections/php-cms-development.json +11 -0
- package/assets/collections/power-platform-specialist.json +31 -0
- package/assets/collections/project-management.json +12 -0
- package/assets/collections/python-development.json +13 -0
- package/assets/collections/quality-assurance.json +13 -0
- package/assets/collections/ruby-development.json +9 -0
- package/assets/collections/rust-development.json +10 -0
- package/assets/collections/security-specialist.json +8 -0
- package/assets/collections/software-architect.json +25 -0
- package/assets/collections/technical-writing.json +9 -0
- package/assets/collections/web-frontend-development.json +14 -0
- package/assets/instructions/a11y.instructions.md +369 -0
- package/assets/instructions/ai-prompt-engineering-safety-best-practices.instructions.md +867 -0
- package/assets/instructions/angular.instructions.md +104 -0
- package/assets/instructions/ansible.instructions.md +88 -0
- package/assets/instructions/aspnet-rest-apis.instructions.md +110 -0
- package/assets/instructions/astro.instructions.md +182 -0
- package/assets/instructions/azure-devops-pipelines.instructions.md +185 -0
- package/assets/instructions/azure-functions-typescript.instructions.md +14 -0
- package/assets/instructions/azure-logic-apps-power-automate.instructions.md +1943 -0
- package/assets/instructions/azure-verified-modules-terraform.instructions.md +229 -0
- package/assets/instructions/bicep-code-best-practices.instructions.md +54 -0
- package/assets/instructions/blazor.instructions.md +77 -0
- package/assets/instructions/clojure.instructions.md +349 -0
- package/assets/instructions/cmake-vcpkg.instructions.md +10 -0
- package/assets/instructions/codexer.instructions.md +428 -0
- package/assets/instructions/coldfusion-cfc.instructions.md +30 -0
- package/assets/instructions/coldfusion-cfm.instructions.md +28 -0
- package/assets/instructions/collections.instructions.md +54 -0
- package/assets/instructions/containerization-docker-best-practices.instructions.md +681 -0
- package/assets/instructions/convert-jpa-to-spring-data-cosmos.instructions.md +949 -0
- package/assets/instructions/copilot-thought-logging.instructions.md +62 -0
- package/assets/instructions/csharp-ja.instructions.md +114 -0
- package/assets/instructions/csharp-ko.instructions.md +77 -0
- package/assets/instructions/csharp-mcp-server.instructions.md +95 -0
- package/assets/instructions/csharp.instructions.md +114 -0
- package/assets/instructions/dart-n-flutter.instructions.md +447 -0
- package/assets/instructions/declarative-agents-microsoft365.instructions.md +316 -0
- package/assets/instructions/devbox-image-definition.instructions.md +302 -0
- package/assets/instructions/devops-core-principles.instructions.md +167 -0
- package/assets/instructions/dotnet-architecture-good-practices.instructions.md +279 -0
- package/assets/instructions/dotnet-framework.instructions.md +113 -0
- package/assets/instructions/dotnet-maui-9-to-dotnet-maui-10-upgrade.instructions.md +1922 -0
- package/assets/instructions/dotnet-maui.instructions.md +69 -0
- package/assets/instructions/dotnet-upgrade.instructions.md +287 -0
- package/assets/instructions/dotnet-wpf.instructions.md +79 -0
- package/assets/instructions/genaiscript.instructions.md +21 -0
- package/assets/instructions/generate-modern-terraform-code-for-azure.instructions.md +82 -0
- package/assets/instructions/gilfoyle-code-review.instructions.md +114 -0
- package/assets/instructions/github-actions-ci-cd-best-practices.instructions.md +607 -0
- package/assets/instructions/go-mcp-server.instructions.md +346 -0
- package/assets/instructions/go.instructions.md +373 -0
- package/assets/instructions/instructions.instructions.md +256 -0
- package/assets/instructions/java-11-to-java-17-upgrade.instructions.md +793 -0
- package/assets/instructions/java-17-to-java-21-upgrade.instructions.md +464 -0
- package/assets/instructions/java-21-to-java-25-upgrade.instructions.md +311 -0
- package/assets/instructions/java-mcp-server.instructions.md +553 -0
- package/assets/instructions/java.instructions.md +81 -0
- package/assets/instructions/joyride-user-project.instructions.md +206 -0
- package/assets/instructions/joyride-workspace-automation.instructions.md +46 -0
- package/assets/instructions/kotlin-mcp-server.instructions.md +481 -0
- package/assets/instructions/kubernetes-deployment-best-practices.instructions.md +307 -0
- package/assets/instructions/langchain-python.instructions.md +229 -0
- package/assets/instructions/localization.instructions.md +39 -0
- package/assets/instructions/makefile.instructions.md +410 -0
- package/assets/instructions/markdown.instructions.md +52 -0
- package/assets/instructions/memory-bank.instructions.md +299 -0
- package/assets/instructions/mongo-dba.instructions.md +25 -0
- package/assets/instructions/ms-sql-dba.instructions.md +25 -0
- package/assets/instructions/nestjs.instructions.md +406 -0
- package/assets/instructions/nextjs-tailwind.instructions.md +72 -0
- package/assets/instructions/nextjs.instructions.md +143 -0
- package/assets/instructions/nodejs-javascript-vitest.instructions.md +30 -0
- package/assets/instructions/object-calisthenics.instructions.md +302 -0
- package/assets/instructions/oqtane.instructions.md +86 -0
- package/assets/instructions/performance-optimization.instructions.md +420 -0
- package/assets/instructions/php-mcp-server.instructions.md +809 -0
- package/assets/instructions/playwright-dotnet.instructions.md +101 -0
- package/assets/instructions/playwright-python.instructions.md +62 -0
- package/assets/instructions/playwright-typescript.instructions.md +86 -0
- package/assets/instructions/power-apps-canvas-yaml.instructions.md +827 -0
- package/assets/instructions/power-apps-code-apps.instructions.md +601 -0
- package/assets/instructions/power-bi-custom-visuals-development.instructions.md +810 -0
- package/assets/instructions/power-bi-data-modeling-best-practices.instructions.md +639 -0
- package/assets/instructions/power-bi-dax-best-practices.instructions.md +795 -0
- package/assets/instructions/power-bi-devops-alm-best-practices.instructions.md +623 -0
- package/assets/instructions/power-bi-report-design-best-practices.instructions.md +752 -0
- package/assets/instructions/power-bi-security-rls-best-practices.instructions.md +504 -0
- package/assets/instructions/power-platform-connector.instructions.md +430 -0
- package/assets/instructions/power-platform-mcp-development.instructions.md +88 -0
- package/assets/instructions/powershell-pester-5.instructions.md +197 -0
- package/assets/instructions/powershell.instructions.md +356 -0
- package/assets/instructions/prompt.instructions.md +73 -0
- package/assets/instructions/python-mcp-server.instructions.md +204 -0
- package/assets/instructions/python.instructions.md +56 -0
- package/assets/instructions/quarkus-mcp-server-sse.instructions.md +49 -0
- package/assets/instructions/quarkus.instructions.md +98 -0
- package/assets/instructions/r.instructions.md +116 -0
- package/assets/instructions/reactjs.instructions.md +162 -0
- package/assets/instructions/ruby-mcp-server.instructions.md +629 -0
- package/assets/instructions/ruby-on-rails.instructions.md +124 -0
- package/assets/instructions/rust-mcp-server.instructions.md +715 -0
- package/assets/instructions/rust.instructions.md +135 -0
- package/assets/instructions/security-and-owasp.instructions.md +51 -0
- package/assets/instructions/self-explanatory-code-commenting.instructions.md +162 -0
- package/assets/instructions/shell.instructions.md +132 -0
- package/assets/instructions/spec-driven-workflow-v1.instructions.md +323 -0
- package/assets/instructions/springboot.instructions.md +68 -0
- package/assets/instructions/sql-sp-generation.instructions.md +74 -0
- package/assets/instructions/svelte.instructions.md +161 -0
- package/assets/instructions/swift-mcp-server.instructions.md +498 -0
- package/assets/instructions/taming-copilot.instructions.md +40 -0
- package/assets/instructions/tanstack-start-shadcn-tailwind.instructions.md +212 -0
- package/assets/instructions/task-implementation.instructions.md +190 -0
- package/assets/instructions/tasksync.instructions.md +352 -0
- package/assets/instructions/terraform-azure.instructions.md +254 -0
- package/assets/instructions/terraform-sap-btp.instructions.md +195 -0
- package/assets/instructions/terraform.instructions.md +113 -0
- package/assets/instructions/typescript-5-es2022.instructions.md +114 -0
- package/assets/instructions/typescript-mcp-server.instructions.md +228 -0
- package/assets/instructions/update-code-from-shorthand.instructions.md +130 -0
- package/assets/instructions/vuejs3.instructions.md +153 -0
- package/assets/instructions/wordpress.instructions.md +186 -0
- package/assets/prompts/add-educational-comments.prompt.md +129 -0
- package/assets/prompts/ai-prompt-engineering-safety-review.prompt.md +230 -0
- package/assets/prompts/architecture-blueprint-generator.prompt.md +322 -0
- package/assets/prompts/aspnet-minimal-api-openapi.prompt.md +42 -0
- package/assets/prompts/az-cost-optimize.prompt.md +305 -0
- package/assets/prompts/azure-resource-health-diagnose.prompt.md +290 -0
- package/assets/prompts/boost-prompt.prompt.md +25 -0
- package/assets/prompts/breakdown-epic-arch.prompt.md +66 -0
- package/assets/prompts/breakdown-epic-pm.prompt.md +58 -0
- package/assets/prompts/breakdown-feature-implementation.prompt.md +128 -0
- package/assets/prompts/breakdown-feature-prd.prompt.md +61 -0
- package/assets/prompts/breakdown-plan.prompt.md +509 -0
- package/assets/prompts/breakdown-test.prompt.md +365 -0
- package/assets/prompts/code-exemplars-blueprint-generator.prompt.md +126 -0
- package/assets/prompts/comment-code-generate-a-tutorial.prompt.md +26 -0
- package/assets/prompts/containerize-aspnet-framework.prompt.md +455 -0
- package/assets/prompts/containerize-aspnetcore.prompt.md +393 -0
- package/assets/prompts/conventional-commit.prompt.md +73 -0
- package/assets/prompts/copilot-instructions-blueprint-generator.prompt.md +294 -0
- package/assets/prompts/cosmosdb-datamodeling.prompt.md +1045 -0
- package/assets/prompts/create-agentsmd.prompt.md +249 -0
- package/assets/prompts/create-architectural-decision-record.prompt.md +97 -0
- package/assets/prompts/create-github-action-workflow-specification.prompt.md +276 -0
- package/assets/prompts/create-github-issue-feature-from-specification.prompt.md +28 -0
- package/assets/prompts/create-github-issues-feature-from-implementation-plan.prompt.md +28 -0
- package/assets/prompts/create-github-issues-for-unmet-specification-requirements.prompt.md +35 -0
- package/assets/prompts/create-github-pull-request-from-specification.prompt.md +24 -0
- package/assets/prompts/create-implementation-plan.prompt.md +157 -0
- package/assets/prompts/create-llms.prompt.md +210 -0
- package/assets/prompts/create-oo-component-documentation.prompt.md +193 -0
- package/assets/prompts/create-readme.prompt.md +21 -0
- package/assets/prompts/create-specification.prompt.md +127 -0
- package/assets/prompts/create-spring-boot-java-project.prompt.md +163 -0
- package/assets/prompts/create-spring-boot-kotlin-project.prompt.md +147 -0
- package/assets/prompts/create-technical-spike.prompt.md +231 -0
- package/assets/prompts/csharp-async.prompt.md +50 -0
- package/assets/prompts/csharp-docs.prompt.md +63 -0
- package/assets/prompts/csharp-mcp-server-generator.prompt.md +59 -0
- package/assets/prompts/csharp-mstest.prompt.md +67 -0
- package/assets/prompts/csharp-nunit.prompt.md +72 -0
- package/assets/prompts/csharp-tunit.prompt.md +101 -0
- package/assets/prompts/csharp-xunit.prompt.md +69 -0
- package/assets/prompts/declarative-agents.prompt.md +93 -0
- package/assets/prompts/documentation-writer.prompt.md +46 -0
- package/assets/prompts/dotnet-best-practices.prompt.md +84 -0
- package/assets/prompts/dotnet-design-pattern-review.prompt.md +41 -0
- package/assets/prompts/dotnet-upgrade.prompt.md +116 -0
- package/assets/prompts/editorconfig.prompt.md +64 -0
- package/assets/prompts/ef-core.prompt.md +76 -0
- package/assets/prompts/finalize-agent-prompt.prompt.md +27 -0
- package/assets/prompts/first-ask.prompt.md +29 -0
- package/assets/prompts/folder-structure-blueprint-generator.prompt.md +405 -0
- package/assets/prompts/gen-specs-as-issues.prompt.md +165 -0
- package/assets/prompts/generate-custom-instructions-from-codebase.prompt.md +240 -0
- package/assets/prompts/git-flow-branch-creator.prompt.md +293 -0
- package/assets/prompts/github-copilot-starter.prompt.md +372 -0
- package/assets/prompts/go-mcp-server-generator.prompt.md +334 -0
- package/assets/prompts/java-docs.prompt.md +24 -0
- package/assets/prompts/java-junit.prompt.md +64 -0
- package/assets/prompts/java-mcp-server-generator.prompt.md +756 -0
- package/assets/prompts/java-refactoring-extract-method.prompt.md +105 -0
- package/assets/prompts/java-refactoring-remove-parameter.prompt.md +85 -0
- package/assets/prompts/java-springboot.prompt.md +66 -0
- package/assets/prompts/javascript-typescript-jest.prompt.md +44 -0
- package/assets/prompts/kotlin-mcp-server-generator.prompt.md +449 -0
- package/assets/prompts/kotlin-springboot.prompt.md +71 -0
- package/assets/prompts/mcp-copilot-studio-server-generator.prompt.md +118 -0
- package/assets/prompts/memory-merger.prompt.md +107 -0
- package/assets/prompts/mkdocs-translations.prompt.md +110 -0
- package/assets/prompts/model-recommendation.prompt.md +677 -0
- package/assets/prompts/multi-stage-dockerfile.prompt.md +47 -0
- package/assets/prompts/my-issues.prompt.md +9 -0
- package/assets/prompts/my-pull-requests.prompt.md +15 -0
- package/assets/prompts/next-intl-add-language.prompt.md +20 -0
- package/assets/prompts/php-mcp-server-generator.prompt.md +522 -0
- package/assets/prompts/playwright-automation-fill-in-form.prompt.md +30 -0
- package/assets/prompts/playwright-explore-website.prompt.md +19 -0
- package/assets/prompts/playwright-generate-test.prompt.md +19 -0
- package/assets/prompts/postgresql-code-review.prompt.md +214 -0
- package/assets/prompts/postgresql-optimization.prompt.md +406 -0
- package/assets/prompts/power-apps-code-app-scaffold.prompt.md +150 -0
- package/assets/prompts/power-bi-dax-optimization.prompt.md +175 -0
- package/assets/prompts/power-bi-model-design-review.prompt.md +405 -0
- package/assets/prompts/power-bi-performance-troubleshooting.prompt.md +384 -0
- package/assets/prompts/power-bi-report-design-consultation.prompt.md +353 -0
- package/assets/prompts/power-platform-mcp-connector-suite.prompt.md +156 -0
- package/assets/prompts/project-workflow-analysis-blueprint-generator.prompt.md +294 -0
- package/assets/prompts/prompt-builder.prompt.md +142 -0
- package/assets/prompts/pytest-coverage.prompt.md +28 -0
- package/assets/prompts/python-mcp-server-generator.prompt.md +105 -0
- package/assets/prompts/readme-blueprint-generator.prompt.md +79 -0
- package/assets/prompts/remember-interactive-programming.prompt.md +13 -0
- package/assets/prompts/remember.prompt.md +125 -0
- package/assets/prompts/repo-story-time.prompt.md +156 -0
- package/assets/prompts/review-and-refactor.prompt.md +15 -0
- package/assets/prompts/ruby-mcp-server-generator.prompt.md +660 -0
- package/assets/prompts/rust-mcp-server-generator.prompt.md +578 -0
- package/assets/prompts/shuffle-json-data.prompt.md +151 -0
- package/assets/prompts/sql-code-review.prompt.md +303 -0
- package/assets/prompts/sql-optimization.prompt.md +298 -0
- package/assets/prompts/suggest-awesome-github-copilot-agents.prompt.md +72 -0
- package/assets/prompts/suggest-awesome-github-copilot-chatmodes.prompt.md +71 -0
- package/assets/prompts/suggest-awesome-github-copilot-collections.prompt.md +149 -0
- package/assets/prompts/suggest-awesome-github-copilot-instructions.prompt.md +88 -0
- package/assets/prompts/suggest-awesome-github-copilot-prompts.prompt.md +71 -0
- package/assets/prompts/swift-mcp-server-generator.prompt.md +669 -0
- package/assets/prompts/technology-stack-blueprint-generator.prompt.md +242 -0
- package/assets/prompts/typescript-mcp-server-generator.prompt.md +90 -0
- package/assets/prompts/update-avm-modules-in-bicep.prompt.md +60 -0
- package/assets/prompts/update-implementation-plan.prompt.md +157 -0
- package/assets/prompts/update-llms.prompt.md +216 -0
- package/assets/prompts/update-markdown-file-index.prompt.md +76 -0
- package/assets/prompts/update-oo-component-documentation.prompt.md +162 -0
- package/assets/prompts/update-specification.prompt.md +127 -0
- package/assets/prompts/write-coding-standards-from-file.prompt.md +316 -0
- package/bin/cli.js +200 -0
- package/package.json +53 -0
- package/scripts/sync.js +99 -0
- package/verdaccio/config.yaml +202 -0
|
@@ -0,0 +1,1922 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: 'Instructions for upgrading .NET MAUI applications from version 9 to version 10, including breaking changes, deprecated APIs, and migration strategies for ListView to CollectionView.'
|
|
3
|
+
applyTo: '**/*.csproj, **/*.cs, **/*.xaml'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Upgrading from .NET MAUI 9 to .NET MAUI 10
|
|
7
|
+
|
|
8
|
+
This guide helps you upgrade your .NET MAUI application from .NET 9 to .NET 10 by focusing on the critical breaking changes and obsolete APIs that require code updates.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Table of Contents
|
|
13
|
+
|
|
14
|
+
1. [Quick Start](#quick-start)
|
|
15
|
+
2. [Update Target Framework](#update-target-framework)
|
|
16
|
+
3. [Breaking Changes (P0 - Must Fix)](#breaking-changes-p0---must-fix)
|
|
17
|
+
- [MessagingCenter Made Internal](#messagingcenter-made-internal)
|
|
18
|
+
- [ListView and TableView Deprecated](#listview-and-tableview-deprecated)
|
|
19
|
+
4. [Deprecated APIs (P1 - Fix Soon)](#deprecated-apis-p1---fix-soon)
|
|
20
|
+
- [Animation Methods](#1-animation-methods)
|
|
21
|
+
- [DisplayAlert and DisplayActionSheet](#2-displayalert-and-displayactionsheet)
|
|
22
|
+
- [Page.IsBusy](#3-pageisbusy)
|
|
23
|
+
- [MediaPicker APIs](#4-mediapicker-apis)
|
|
24
|
+
5. [Recommended Changes (P2)](#recommended-changes-p2)
|
|
25
|
+
6. [Bulk Migration Tools](#bulk-migration-tools)
|
|
26
|
+
7. [Testing Your Upgrade](#testing-your-upgrade)
|
|
27
|
+
8. [Troubleshooting](#troubleshooting)
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
**Five-Step Upgrade Process:**
|
|
34
|
+
|
|
35
|
+
1. **Update TargetFramework** to `net10.0`
|
|
36
|
+
2. **Update CommunityToolkit.Maui** to 12.3.0+ (if you use it) - REQUIRED
|
|
37
|
+
3. **Fix breaking changes** - MessagingCenter (P0)
|
|
38
|
+
4. **Migrate ListView/TableView to CollectionView** (P0 - CRITICAL)
|
|
39
|
+
5. **Fix deprecated APIs** - Animation methods, DisplayAlert, IsBusy, MediaPicker (P1)
|
|
40
|
+
|
|
41
|
+
> ⚠️ **Major Breaking Changes**:
|
|
42
|
+
> - CommunityToolkit.Maui **must** be version 12.3.0 or later
|
|
43
|
+
> - ListView and TableView are now obsolete (most significant migration effort)
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Update Target Framework
|
|
48
|
+
|
|
49
|
+
### Single Platform
|
|
50
|
+
|
|
51
|
+
```xml
|
|
52
|
+
<Project Sdk="Microsoft.NET.Sdk">
|
|
53
|
+
<PropertyGroup>
|
|
54
|
+
<TargetFramework>net10.0</TargetFramework>
|
|
55
|
+
</PropertyGroup>
|
|
56
|
+
</Project>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Multi-Platform
|
|
60
|
+
|
|
61
|
+
```xml
|
|
62
|
+
<Project Sdk="Microsoft.NET.Sdk">
|
|
63
|
+
<PropertyGroup>
|
|
64
|
+
<TargetFrameworks>net10.0-android;net10.0-ios;net10.0-maccatalyst;net10.0-windows10.0.19041.0</TargetFrameworks>
|
|
65
|
+
</PropertyGroup>
|
|
66
|
+
</Project>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Optional: Linux Compatibility (GitHub Copilot, WSL, etc.)
|
|
70
|
+
|
|
71
|
+
> 💡 **For Linux Development**: If you're building on Linux (e.g., GitHub Codespaces, WSL, or using GitHub Copilot), you can make your project compile on Linux by conditionally excluding iOS/Mac Catalyst targets:
|
|
72
|
+
|
|
73
|
+
```xml
|
|
74
|
+
<Project Sdk="Microsoft.NET.Sdk">
|
|
75
|
+
<PropertyGroup>
|
|
76
|
+
<!-- Start with Android (always supported) -->
|
|
77
|
+
<TargetFrameworks>net10.0-android</TargetFrameworks>
|
|
78
|
+
|
|
79
|
+
<!-- Add iOS/Mac Catalyst only when NOT on Linux -->
|
|
80
|
+
<TargetFrameworks Condition="!$([MSBuild]::IsOSPlatform('linux'))">$(TargetFrameworks);net10.0-ios;net10.0-maccatalyst</TargetFrameworks>
|
|
81
|
+
|
|
82
|
+
<!-- Add Windows only when on Windows -->
|
|
83
|
+
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net10.0-windows10.0.19041.0</TargetFrameworks>
|
|
84
|
+
</PropertyGroup>
|
|
85
|
+
</Project>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Benefits:**
|
|
89
|
+
- ✅ Compiles successfully on Linux (no iOS/Mac tools required)
|
|
90
|
+
- ✅ Works with GitHub Codespaces and Copilot
|
|
91
|
+
- ✅ Automatically includes correct targets based on build OS
|
|
92
|
+
- ✅ No changes needed when switching between OS environments
|
|
93
|
+
|
|
94
|
+
**Reference:** [dotnet/maui#32186](https://github.com/dotnet/maui/pull/32186)
|
|
95
|
+
|
|
96
|
+
### Update Required NuGet Packages
|
|
97
|
+
|
|
98
|
+
> ⚠️ **CRITICAL**: If you use CommunityToolkit.Maui, you **must** update to version 12.3.0 or later. Earlier versions are not compatible with .NET 10 and will cause compilation errors.
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# Update CommunityToolkit.Maui (if you use it)
|
|
102
|
+
dotnet add package CommunityToolkit.Maui --version 12.3.0
|
|
103
|
+
|
|
104
|
+
# Update other common packages to .NET 10 compatible versions
|
|
105
|
+
dotnet add package Microsoft.Maui.Controls --version 10.0.0
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Check all your NuGet packages:**
|
|
109
|
+
```bash
|
|
110
|
+
# List all packages and check for updates
|
|
111
|
+
dotnet list package --outdated
|
|
112
|
+
|
|
113
|
+
# Update all packages to latest compatible versions
|
|
114
|
+
dotnet list package --outdated | grep ">" | cut -d '>' -f 1 | xargs -I {} dotnet add package {}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Breaking Changes (P0 - Must Fix)
|
|
120
|
+
|
|
121
|
+
### MessagingCenter Made Internal
|
|
122
|
+
|
|
123
|
+
**Status:** 🚨 **BREAKING** - `MessagingCenter` is now `internal` and cannot be accessed.
|
|
124
|
+
|
|
125
|
+
**Error You'll See:**
|
|
126
|
+
```
|
|
127
|
+
error CS0122: 'MessagingCenter' is inaccessible due to its protection level
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Migration Required:**
|
|
131
|
+
|
|
132
|
+
#### Step 1: Install CommunityToolkit.Mvvm
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
dotnet add package CommunityToolkit.Mvvm --version 8.3.0
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
#### Step 2: Define Message Classes
|
|
139
|
+
|
|
140
|
+
```csharp
|
|
141
|
+
// OLD: No message class needed
|
|
142
|
+
MessagingCenter.Send(this, "UserLoggedIn", userData);
|
|
143
|
+
|
|
144
|
+
// NEW: Create a message class
|
|
145
|
+
public class UserLoggedInMessage
|
|
146
|
+
{
|
|
147
|
+
public UserData Data { get; set; }
|
|
148
|
+
|
|
149
|
+
public UserLoggedInMessage(UserData data)
|
|
150
|
+
{
|
|
151
|
+
Data = data;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
#### Step 3: Update Send Calls
|
|
157
|
+
|
|
158
|
+
```csharp
|
|
159
|
+
// ❌ OLD (Broken in .NET 10)
|
|
160
|
+
using Microsoft.Maui.Controls;
|
|
161
|
+
|
|
162
|
+
MessagingCenter.Send(this, "UserLoggedIn", userData);
|
|
163
|
+
MessagingCenter.Send<App, string>(this, "StatusChanged", "Active");
|
|
164
|
+
|
|
165
|
+
// ✅ NEW (Required)
|
|
166
|
+
using CommunityToolkit.Mvvm.Messaging;
|
|
167
|
+
|
|
168
|
+
WeakReferenceMessenger.Default.Send(new UserLoggedInMessage(userData));
|
|
169
|
+
WeakReferenceMessenger.Default.Send(new StatusChangedMessage("Active"));
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
#### Step 4: Update Subscribe Calls
|
|
173
|
+
|
|
174
|
+
```csharp
|
|
175
|
+
// ❌ OLD (Broken in .NET 10)
|
|
176
|
+
MessagingCenter.Subscribe<App, UserData>(this, "UserLoggedIn", (sender, data) =>
|
|
177
|
+
{
|
|
178
|
+
// Handle message
|
|
179
|
+
CurrentUser = data;
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// ✅ NEW (Required)
|
|
183
|
+
WeakReferenceMessenger.Default.Register<UserLoggedInMessage>(this, (recipient, message) =>
|
|
184
|
+
{
|
|
185
|
+
// Handle message
|
|
186
|
+
CurrentUser = message.Data;
|
|
187
|
+
});
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
#### ⚠️ Important Behavioral Difference: Duplicate Subscriptions
|
|
191
|
+
|
|
192
|
+
**WeakReferenceMessenger** throws an `InvalidOperationException` if you try to register the same message type multiple times on the same recipient (MessagingCenter allowed this):
|
|
193
|
+
|
|
194
|
+
```csharp
|
|
195
|
+
// ❌ This THROWS InvalidOperationException in WeakReferenceMessenger
|
|
196
|
+
WeakReferenceMessenger.Default.Register<UserLoggedInMessage>(this, (r, m) => Handler1(m));
|
|
197
|
+
WeakReferenceMessenger.Default.Register<UserLoggedInMessage>(this, (r, m) => Handler2(m)); // ❌ THROWS!
|
|
198
|
+
|
|
199
|
+
// ✅ Solution 1: Unregister before re-registering
|
|
200
|
+
WeakReferenceMessenger.Default.Unregister<UserLoggedInMessage>(this);
|
|
201
|
+
WeakReferenceMessenger.Default.Register<UserLoggedInMessage>(this, (r, m) => Handler1(m));
|
|
202
|
+
|
|
203
|
+
// ✅ Solution 2: Handle multiple actions in one registration
|
|
204
|
+
WeakReferenceMessenger.Default.Register<UserLoggedInMessage>(this, (r, m) =>
|
|
205
|
+
{
|
|
206
|
+
Handler1(m);
|
|
207
|
+
Handler2(m);
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**Why this matters:** If your code subscribes to the same message in multiple places (e.g., in a page constructor and in `OnAppearing`), you'll get a runtime crash.
|
|
212
|
+
|
|
213
|
+
#### Step 5: Unregister When Done
|
|
214
|
+
|
|
215
|
+
```csharp
|
|
216
|
+
// ❌ OLD
|
|
217
|
+
MessagingCenter.Unsubscribe<App, UserData>(this, "UserLoggedIn");
|
|
218
|
+
|
|
219
|
+
// ✅ NEW (CRITICAL - prevents memory leaks)
|
|
220
|
+
WeakReferenceMessenger.Default.Unregister<UserLoggedInMessage>(this);
|
|
221
|
+
|
|
222
|
+
// Or unregister all messages for this recipient
|
|
223
|
+
WeakReferenceMessenger.Default.UnregisterAll(this);
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
#### Complete Before/After Example
|
|
227
|
+
|
|
228
|
+
**Before (.NET 9):**
|
|
229
|
+
```csharp
|
|
230
|
+
// Sender
|
|
231
|
+
public class LoginViewModel
|
|
232
|
+
{
|
|
233
|
+
public async Task LoginAsync()
|
|
234
|
+
{
|
|
235
|
+
var user = await AuthService.LoginAsync(username, password);
|
|
236
|
+
MessagingCenter.Send(this, "UserLoggedIn", user);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Receiver
|
|
241
|
+
public partial class MainPage : ContentPage
|
|
242
|
+
{
|
|
243
|
+
public MainPage()
|
|
244
|
+
{
|
|
245
|
+
InitializeComponent();
|
|
246
|
+
|
|
247
|
+
MessagingCenter.Subscribe<LoginViewModel, User>(this, "UserLoggedIn", (sender, user) =>
|
|
248
|
+
{
|
|
249
|
+
WelcomeLabel.Text = $"Welcome, {user.Name}!";
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
protected override void OnDisappearing()
|
|
254
|
+
{
|
|
255
|
+
base.OnDisappearing();
|
|
256
|
+
MessagingCenter.Unsubscribe<LoginViewModel, User>(this, "UserLoggedIn");
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
**After (.NET 10):**
|
|
262
|
+
```csharp
|
|
263
|
+
// 1. Define message
|
|
264
|
+
public class UserLoggedInMessage
|
|
265
|
+
{
|
|
266
|
+
public User User { get; }
|
|
267
|
+
|
|
268
|
+
public UserLoggedInMessage(User user)
|
|
269
|
+
{
|
|
270
|
+
User = user;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// 2. Sender
|
|
275
|
+
public class LoginViewModel
|
|
276
|
+
{
|
|
277
|
+
public async Task LoginAsync()
|
|
278
|
+
{
|
|
279
|
+
var user = await AuthService.LoginAsync(username, password);
|
|
280
|
+
WeakReferenceMessenger.Default.Send(new UserLoggedInMessage(user));
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// 3. Receiver
|
|
285
|
+
public partial class MainPage : ContentPage
|
|
286
|
+
{
|
|
287
|
+
public MainPage()
|
|
288
|
+
{
|
|
289
|
+
InitializeComponent();
|
|
290
|
+
|
|
291
|
+
WeakReferenceMessenger.Default.Register<UserLoggedInMessage>(this, (recipient, message) =>
|
|
292
|
+
{
|
|
293
|
+
WelcomeLabel.Text = $"Welcome, {message.User.Name}!";
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
protected override void OnDisappearing()
|
|
298
|
+
{
|
|
299
|
+
base.OnDisappearing();
|
|
300
|
+
WeakReferenceMessenger.Default.UnregisterAll(this);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
**Key Differences:**
|
|
306
|
+
- ✅ Type-safe message classes
|
|
307
|
+
- ✅ No magic strings
|
|
308
|
+
- ✅ Better IntelliSense support
|
|
309
|
+
- ✅ Easier to refactor
|
|
310
|
+
- ⚠️ **Must remember to unregister!**
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
### ListView and TableView Deprecated
|
|
315
|
+
|
|
316
|
+
**Status:** 🚨 **DEPRECATED (P0)** - `ListView`, `TableView`, and all Cell types are now obsolete. Migrate to `CollectionView`.
|
|
317
|
+
|
|
318
|
+
**Warning You'll See:**
|
|
319
|
+
```
|
|
320
|
+
warning CS0618: 'ListView' is obsolete: 'ListView is deprecated. Please use CollectionView instead.'
|
|
321
|
+
warning CS0618: 'TableView' is obsolete: 'Please use CollectionView instead.'
|
|
322
|
+
warning CS0618: 'TextCell' is obsolete: 'The controls which use TextCell (ListView and TableView) are obsolete. Please use CollectionView instead.'
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
**Obsolete Types:**
|
|
326
|
+
- `ListView` → `CollectionView`
|
|
327
|
+
- `TableView` → `CollectionView` (for settings pages, consider vertical StackLayout with BindableLayout)
|
|
328
|
+
- `TextCell` → Custom DataTemplate with Label(s)
|
|
329
|
+
- `ImageCell` → Custom DataTemplate with Image + Label(s)
|
|
330
|
+
- `EntryCell` → Custom DataTemplate with Entry
|
|
331
|
+
- `SwitchCell` → Custom DataTemplate with Switch
|
|
332
|
+
- `ViewCell` → DataTemplate
|
|
333
|
+
|
|
334
|
+
**Impact:** This is a **MAJOR** breaking change. ListView and TableView are among the most commonly used controls in MAUI apps.
|
|
335
|
+
|
|
336
|
+
#### Why This Takes Time
|
|
337
|
+
|
|
338
|
+
Converting ListView/TableView to CollectionView is not a simple find-replace:
|
|
339
|
+
|
|
340
|
+
1. **Different event model** - `ItemSelected` → `SelectionChanged` with different arguments
|
|
341
|
+
2. **Different grouping** - GroupDisplayBinding no longer exists
|
|
342
|
+
3. **Context actions** - Must convert to SwipeView
|
|
343
|
+
4. **Item sizing** - `HasUnevenRows` handled differently
|
|
344
|
+
5. **Platform-specific code** - iOS/Android ListView platform configurations need removal
|
|
345
|
+
6. **Testing required** - CollectionView virtualizes differently, may affect performance
|
|
346
|
+
|
|
347
|
+
#### Migration Strategy
|
|
348
|
+
|
|
349
|
+
**Step 1: Inventory Your ListViews**
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
# Find all ListView/TableView usages
|
|
353
|
+
grep -r "ListView\|TableView" --include="*.xaml" --include="*.cs" .
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
**Step 2: Basic ListView → CollectionView**
|
|
357
|
+
|
|
358
|
+
**Before (ListView):**
|
|
359
|
+
```xaml
|
|
360
|
+
<ListView ItemsSource="{Binding Items}"
|
|
361
|
+
ItemSelected="OnItemSelected"
|
|
362
|
+
HasUnevenRows="True">
|
|
363
|
+
<ListView.ItemTemplate>
|
|
364
|
+
<DataTemplate>
|
|
365
|
+
<TextCell Text="{Binding Title}"
|
|
366
|
+
Detail="{Binding Description}" />
|
|
367
|
+
</DataTemplate>
|
|
368
|
+
</ListView.ItemTemplate>
|
|
369
|
+
</ListView>
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
**After (CollectionView):**
|
|
373
|
+
```xaml
|
|
374
|
+
<CollectionView ItemsSource="{Binding Items}"
|
|
375
|
+
SelectionMode="Single"
|
|
376
|
+
SelectionChanged="OnSelectionChanged">
|
|
377
|
+
<CollectionView.ItemTemplate>
|
|
378
|
+
<DataTemplate>
|
|
379
|
+
<VerticalStackLayout Padding="10">
|
|
380
|
+
<Label Text="{Binding Title}"
|
|
381
|
+
FontAttributes="Bold" />
|
|
382
|
+
<Label Text="{Binding Description}"
|
|
383
|
+
FontSize="12"
|
|
384
|
+
TextColor="{StaticResource Gray600}" />
|
|
385
|
+
</VerticalStackLayout>
|
|
386
|
+
</DataTemplate>
|
|
387
|
+
</CollectionView.ItemTemplate>
|
|
388
|
+
</CollectionView>
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
> ⚠️ **Note:** CollectionView has `SelectionMode="None"` by default (selection disabled). You must explicitly set `SelectionMode="Single"` or `SelectionMode="Multiple"` to enable selection.
|
|
392
|
+
|
|
393
|
+
**Code-behind changes:**
|
|
394
|
+
```csharp
|
|
395
|
+
// ❌ OLD (ListView)
|
|
396
|
+
void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
|
|
397
|
+
{
|
|
398
|
+
if (e.SelectedItem == null)
|
|
399
|
+
return;
|
|
400
|
+
|
|
401
|
+
var item = (MyItem)e.SelectedItem;
|
|
402
|
+
// Handle selection
|
|
403
|
+
|
|
404
|
+
// Deselect
|
|
405
|
+
((ListView)sender).SelectedItem = null;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// ✅ NEW (CollectionView)
|
|
409
|
+
void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
|
|
410
|
+
{
|
|
411
|
+
if (e.CurrentSelection.Count == 0)
|
|
412
|
+
return;
|
|
413
|
+
|
|
414
|
+
var item = (MyItem)e.CurrentSelection.FirstOrDefault();
|
|
415
|
+
// Handle selection
|
|
416
|
+
|
|
417
|
+
// Deselect (optional)
|
|
418
|
+
((CollectionView)sender).SelectedItem = null;
|
|
419
|
+
}
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
**Step 3: Grouped ListView → Grouped CollectionView**
|
|
423
|
+
|
|
424
|
+
**Before (Grouped ListView):**
|
|
425
|
+
```xaml
|
|
426
|
+
<ListView ItemsSource="{Binding GroupedItems}"
|
|
427
|
+
IsGroupingEnabled="True"
|
|
428
|
+
GroupDisplayBinding="{Binding Key}">
|
|
429
|
+
<ListView.ItemTemplate>
|
|
430
|
+
<DataTemplate>
|
|
431
|
+
<TextCell Text="{Binding Name}" />
|
|
432
|
+
</DataTemplate>
|
|
433
|
+
</ListView.ItemTemplate>
|
|
434
|
+
</ListView>
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
**After (Grouped CollectionView):**
|
|
438
|
+
```xaml
|
|
439
|
+
<CollectionView ItemsSource="{Binding GroupedItems}"
|
|
440
|
+
IsGrouped="true">
|
|
441
|
+
<CollectionView.GroupHeaderTemplate>
|
|
442
|
+
<DataTemplate>
|
|
443
|
+
<Label Text="{Binding Key}"
|
|
444
|
+
FontAttributes="Bold"
|
|
445
|
+
BackgroundColor="{StaticResource Gray100}"
|
|
446
|
+
Padding="10,5" />
|
|
447
|
+
</DataTemplate>
|
|
448
|
+
</CollectionView.GroupHeaderTemplate>
|
|
449
|
+
|
|
450
|
+
<CollectionView.ItemTemplate>
|
|
451
|
+
<DataTemplate>
|
|
452
|
+
<VerticalStackLayout Padding="20,10">
|
|
453
|
+
<Label Text="{Binding Name}" />
|
|
454
|
+
</VerticalStackLayout>
|
|
455
|
+
</DataTemplate>
|
|
456
|
+
</CollectionView.ItemTemplate>
|
|
457
|
+
</CollectionView>
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
**Step 4: Context Actions → SwipeView**
|
|
461
|
+
|
|
462
|
+
> ⚠️ **Platform Note:** SwipeView requires touch input. On Windows desktop, it only works with touch screens, not with mouse/trackpad. Consider providing alternative UI for desktop scenarios (e.g., buttons, right-click menu).
|
|
463
|
+
|
|
464
|
+
**Before (ListView with ContextActions):**
|
|
465
|
+
```xaml
|
|
466
|
+
<ListView.ItemTemplate>
|
|
467
|
+
<DataTemplate>
|
|
468
|
+
<ViewCell>
|
|
469
|
+
<ViewCell.ContextActions>
|
|
470
|
+
<MenuItem Text="Delete"
|
|
471
|
+
IsDestructive="True"
|
|
472
|
+
Command="{Binding Source={RelativeSource AncestorType={x:Type local:MyPage}}, Path=DeleteCommand}"
|
|
473
|
+
CommandParameter="{Binding .}" />
|
|
474
|
+
</ViewCell.ContextActions>
|
|
475
|
+
|
|
476
|
+
<Label Text="{Binding Title}" Padding="10" />
|
|
477
|
+
</ViewCell>
|
|
478
|
+
</DataTemplate>
|
|
479
|
+
</ListView.ItemTemplate>
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
**After (CollectionView with SwipeView):**
|
|
483
|
+
```xaml
|
|
484
|
+
<CollectionView.ItemTemplate>
|
|
485
|
+
<DataTemplate>
|
|
486
|
+
<SwipeView>
|
|
487
|
+
<SwipeView.RightItems>
|
|
488
|
+
<SwipeItems>
|
|
489
|
+
<SwipeItem Text="Delete"
|
|
490
|
+
BackgroundColor="Red"
|
|
491
|
+
Command="{Binding Source={RelativeSource AncestorType={x:Type local:MyPage}}, Path=DeleteCommand}"
|
|
492
|
+
CommandParameter="{Binding .}" />
|
|
493
|
+
</SwipeItems>
|
|
494
|
+
</SwipeView.RightItems>
|
|
495
|
+
|
|
496
|
+
<VerticalStackLayout Padding="10">
|
|
497
|
+
<Label Text="{Binding Title}" />
|
|
498
|
+
</VerticalStackLayout>
|
|
499
|
+
</SwipeView>
|
|
500
|
+
</DataTemplate>
|
|
501
|
+
</CollectionView.ItemTemplate>
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
**Step 5: TableView for Settings → Alternative Approaches**
|
|
505
|
+
|
|
506
|
+
TableView is commonly used for settings pages. Here are modern alternatives:
|
|
507
|
+
|
|
508
|
+
**Option 1: CollectionView with Grouped Data**
|
|
509
|
+
```xaml
|
|
510
|
+
<CollectionView ItemsSource="{Binding SettingGroups}"
|
|
511
|
+
IsGrouped="true"
|
|
512
|
+
SelectionMode="None">
|
|
513
|
+
<CollectionView.GroupHeaderTemplate>
|
|
514
|
+
<DataTemplate>
|
|
515
|
+
<Label Text="{Binding Title}"
|
|
516
|
+
FontAttributes="Bold"
|
|
517
|
+
Margin="10,15,10,5" />
|
|
518
|
+
</DataTemplate>
|
|
519
|
+
</CollectionView.GroupHeaderTemplate>
|
|
520
|
+
|
|
521
|
+
<CollectionView.ItemTemplate>
|
|
522
|
+
<DataTemplate>
|
|
523
|
+
<Grid Padding="15,10" ColumnDefinitions="*,Auto">
|
|
524
|
+
<Label Text="{Binding Title}"
|
|
525
|
+
VerticalOptions="Center" />
|
|
526
|
+
<Switch Grid.Column="1"
|
|
527
|
+
IsToggled="{Binding IsEnabled}"
|
|
528
|
+
IsVisible="{Binding ShowSwitch}" />
|
|
529
|
+
</Grid>
|
|
530
|
+
</DataTemplate>
|
|
531
|
+
</CollectionView.ItemTemplate>
|
|
532
|
+
</CollectionView>
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
**Option 2: Vertical StackLayout (for small settings lists)**
|
|
536
|
+
```xaml
|
|
537
|
+
<ScrollView>
|
|
538
|
+
<VerticalStackLayout BindableLayout.ItemsSource="{Binding Settings}"
|
|
539
|
+
Spacing="10"
|
|
540
|
+
Padding="15">
|
|
541
|
+
<BindableLayout.ItemTemplate>
|
|
542
|
+
<DataTemplate>
|
|
543
|
+
<Border StrokeThickness="0"
|
|
544
|
+
BackgroundColor="{StaticResource Gray100}"
|
|
545
|
+
Padding="15,10">
|
|
546
|
+
<Grid ColumnDefinitions="*,Auto">
|
|
547
|
+
<Label Text="{Binding Title}"
|
|
548
|
+
VerticalOptions="Center" />
|
|
549
|
+
<Switch Grid.Column="1"
|
|
550
|
+
IsToggled="{Binding IsEnabled}" />
|
|
551
|
+
</Grid>
|
|
552
|
+
</Border>
|
|
553
|
+
</DataTemplate>
|
|
554
|
+
</BindableLayout.ItemTemplate>
|
|
555
|
+
</VerticalStackLayout>
|
|
556
|
+
</ScrollView>
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
**Step 6: Remove Platform-Specific ListView Code**
|
|
560
|
+
|
|
561
|
+
If you used platform-specific ListView features, remove them:
|
|
562
|
+
|
|
563
|
+
```csharp
|
|
564
|
+
// ❌ OLD - Remove these using statements (NOW OBSOLETE IN .NET 10)
|
|
565
|
+
using Microsoft.Maui.Controls.PlatformConfiguration;
|
|
566
|
+
using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;
|
|
567
|
+
using Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpecific;
|
|
568
|
+
|
|
569
|
+
// ❌ OLD - Remove ListView platform configurations (NOW OBSOLETE IN .NET 10)
|
|
570
|
+
myListView.On<iOS>().SetSeparatorStyle(SeparatorStyle.FullWidth);
|
|
571
|
+
myListView.On<Android>().IsFastScrollEnabled();
|
|
572
|
+
|
|
573
|
+
// ❌ OLD - Remove Cell platform configurations (NOW OBSOLETE IN .NET 10)
|
|
574
|
+
viewCell.On<iOS>().SetDefaultBackgroundColor(Colors.White);
|
|
575
|
+
viewCell.On<Android>().SetIsContextActionsLegacyModeEnabled(false);
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
**Migration:** CollectionView does not have platform-specific configurations in the same way. If you need platform-specific styling:
|
|
579
|
+
|
|
580
|
+
```csharp
|
|
581
|
+
// ✅ NEW - Use conditional compilation
|
|
582
|
+
#if IOS
|
|
583
|
+
var backgroundColor = Colors.White;
|
|
584
|
+
#elif ANDROID
|
|
585
|
+
var backgroundColor = Colors.Transparent;
|
|
586
|
+
#endif
|
|
587
|
+
|
|
588
|
+
var grid = new Grid
|
|
589
|
+
{
|
|
590
|
+
BackgroundColor = backgroundColor,
|
|
591
|
+
// ... rest of cell content
|
|
592
|
+
};
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
Or in XAML:
|
|
596
|
+
```xaml
|
|
597
|
+
<CollectionView.ItemTemplate>
|
|
598
|
+
<DataTemplate>
|
|
599
|
+
<Grid>
|
|
600
|
+
<Grid.BackgroundColor>
|
|
601
|
+
<OnPlatform x:TypeArguments="Color">
|
|
602
|
+
<On Platform="iOS" Value="White" />
|
|
603
|
+
<On Platform="Android" Value="Transparent" />
|
|
604
|
+
</OnPlatform>
|
|
605
|
+
</Grid.BackgroundColor>
|
|
606
|
+
<!-- Cell content -->
|
|
607
|
+
</Grid>
|
|
608
|
+
</DataTemplate>
|
|
609
|
+
</CollectionView.ItemTemplate>
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
#### Common Patterns & Pitfalls
|
|
613
|
+
|
|
614
|
+
**1. Empty View**
|
|
615
|
+
```xaml
|
|
616
|
+
<!-- CollectionView has built-in EmptyView support -->
|
|
617
|
+
<CollectionView ItemsSource="{Binding Items}">
|
|
618
|
+
<CollectionView.EmptyView>
|
|
619
|
+
<ContentView>
|
|
620
|
+
<VerticalStackLayout Padding="50" VerticalOptions="Center">
|
|
621
|
+
<Label Text="No items found"
|
|
622
|
+
HorizontalTextAlignment="Center" />
|
|
623
|
+
</VerticalStackLayout>
|
|
624
|
+
</ContentView>
|
|
625
|
+
</CollectionView.EmptyView>
|
|
626
|
+
<!-- ... -->
|
|
627
|
+
</CollectionView>
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
**2. Pull to Refresh**
|
|
631
|
+
```xaml
|
|
632
|
+
<RefreshView IsRefreshing="{Binding IsRefreshing}"
|
|
633
|
+
Command="{Binding RefreshCommand}">
|
|
634
|
+
<CollectionView ItemsSource="{Binding Items}">
|
|
635
|
+
<!-- ... -->
|
|
636
|
+
</CollectionView>
|
|
637
|
+
</RefreshView>
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
**3. Item Spacing**
|
|
641
|
+
```xaml
|
|
642
|
+
<!-- Use ItemsLayout for spacing -->
|
|
643
|
+
<CollectionView ItemsSource="{Binding Items}">
|
|
644
|
+
<CollectionView.ItemsLayout>
|
|
645
|
+
<LinearItemsLayout Orientation="Vertical"
|
|
646
|
+
ItemSpacing="10" />
|
|
647
|
+
</CollectionView.ItemsLayout>
|
|
648
|
+
<!-- ... -->
|
|
649
|
+
</CollectionView>
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
**4. Header and Footer**
|
|
653
|
+
```xaml
|
|
654
|
+
<CollectionView ItemsSource="{Binding Items}">
|
|
655
|
+
<CollectionView.Header>
|
|
656
|
+
<Label Text="My List"
|
|
657
|
+
FontSize="24"
|
|
658
|
+
Padding="10" />
|
|
659
|
+
</CollectionView.Header>
|
|
660
|
+
|
|
661
|
+
<CollectionView.Footer>
|
|
662
|
+
<Label Text="End of list"
|
|
663
|
+
Padding="10"
|
|
664
|
+
HorizontalTextAlignment="Center" />
|
|
665
|
+
</CollectionView.Footer>
|
|
666
|
+
|
|
667
|
+
<!-- ItemTemplate -->
|
|
668
|
+
</CollectionView>
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
**5. Load More / Infinite Scroll**
|
|
672
|
+
```xaml
|
|
673
|
+
<CollectionView ItemsSource="{Binding Items}"
|
|
674
|
+
RemainingItemsThreshold="5"
|
|
675
|
+
RemainingItemsThresholdReachedCommand="{Binding LoadMoreCommand}">
|
|
676
|
+
<!-- ItemTemplate -->
|
|
677
|
+
</CollectionView>
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
**6. Item Sizing Optimization**
|
|
681
|
+
|
|
682
|
+
CollectionView uses `ItemSizingStrategy` to control item measurement:
|
|
683
|
+
|
|
684
|
+
```xaml
|
|
685
|
+
<!-- Default: Each item measured individually (like HasUnevenRows="True") -->
|
|
686
|
+
<CollectionView ItemSizingStrategy="MeasureAllItems">
|
|
687
|
+
<!-- ... -->
|
|
688
|
+
</CollectionView>
|
|
689
|
+
|
|
690
|
+
<!-- Performance: Only first item measured, rest use same height -->
|
|
691
|
+
<CollectionView ItemSizingStrategy="MeasureFirstItem">
|
|
692
|
+
<!-- Use this when all items have similar heights -->
|
|
693
|
+
</CollectionView>
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
> 💡 **Performance Tip:** If your list items have consistent heights, use `ItemSizingStrategy="MeasureFirstItem"` for better performance with large lists.
|
|
697
|
+
|
|
698
|
+
#### .NET 10 Handler Changes (iOS/Mac Catalyst)
|
|
699
|
+
|
|
700
|
+
> ℹ️ **.NET 10 uses new optimized CollectionView and CarouselView handlers** on iOS and Mac Catalyst by default, providing improved performance and stability.
|
|
701
|
+
|
|
702
|
+
**If you previously opted-in to the new handlers in .NET 9**, you should now **REMOVE** this code:
|
|
703
|
+
|
|
704
|
+
```csharp
|
|
705
|
+
// ❌ REMOVE THIS in .NET 10 (these handlers are now default)
|
|
706
|
+
#if IOS || MACCATALYST
|
|
707
|
+
builder.ConfigureMauiHandlers(handlers =>
|
|
708
|
+
{
|
|
709
|
+
handlers.AddHandler<CollectionView, CollectionViewHandler2>();
|
|
710
|
+
handlers.AddHandler<CarouselView, CarouselViewHandler2>();
|
|
711
|
+
});
|
|
712
|
+
#endif
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
The optimized handlers are used automatically in .NET 10 - no configuration needed!
|
|
716
|
+
|
|
717
|
+
**Only if you experience issues**, you can revert to the legacy handler:
|
|
718
|
+
|
|
719
|
+
```csharp
|
|
720
|
+
// In MauiProgram.cs - only if needed
|
|
721
|
+
#if IOS || MACCATALYST
|
|
722
|
+
builder.ConfigureMauiHandlers(handlers =>
|
|
723
|
+
{
|
|
724
|
+
handlers.AddHandler<Microsoft.Maui.Controls.CollectionView,
|
|
725
|
+
Microsoft.Maui.Controls.Handlers.Items.CollectionViewHandler>();
|
|
726
|
+
});
|
|
727
|
+
#endif
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
However, Microsoft recommends using the new default handlers for best results.
|
|
731
|
+
|
|
732
|
+
#### Testing Checklist
|
|
733
|
+
|
|
734
|
+
After migration, test these scenarios:
|
|
735
|
+
|
|
736
|
+
- [ ] **Item selection** works correctly
|
|
737
|
+
- [ ] **Grouped lists** display with proper headers
|
|
738
|
+
- [ ] **Swipe actions** (if used) work on both iOS and Android
|
|
739
|
+
- [ ] **Empty view** appears when list is empty
|
|
740
|
+
- [ ] **Pull to refresh** works (if used)
|
|
741
|
+
- [ ] **Scroll performance** is acceptable (especially for large lists)
|
|
742
|
+
- [ ] **Item sizing** is correct (CollectionView auto-sizes by default)
|
|
743
|
+
- [ ] **Selection visual state** shows/hides correctly
|
|
744
|
+
- [ ] **Data binding** updates the list correctly
|
|
745
|
+
- [ ] **Navigation** from list items works
|
|
746
|
+
|
|
747
|
+
#### Migration Complexity Factors
|
|
748
|
+
|
|
749
|
+
ListView to CollectionView migration is complex because:
|
|
750
|
+
- Each ListView may have unique behaviors
|
|
751
|
+
- Platform-specific code needs updating
|
|
752
|
+
- Extensive testing required
|
|
753
|
+
- Context actions need SwipeView conversion
|
|
754
|
+
- Grouped lists need template updates
|
|
755
|
+
- ViewModel changes may be needed
|
|
756
|
+
|
|
757
|
+
#### Quick Reference: ListView vs CollectionView
|
|
758
|
+
|
|
759
|
+
| Feature | ListView | CollectionView |
|
|
760
|
+
|---------|----------|----------------|
|
|
761
|
+
| **Selection Event** | `ItemSelected` | `SelectionChanged` |
|
|
762
|
+
| **Selection Args** | `SelectedItemChangedEventArgs` | `SelectionChangedEventArgs` |
|
|
763
|
+
| **Getting Selected** | `e.SelectedItem` | `e.CurrentSelection.FirstOrDefault()` |
|
|
764
|
+
| **Context Menus** | `ContextActions` | `SwipeView` |
|
|
765
|
+
| **Grouping** | `IsGroupingEnabled="True"` | `IsGrouped="true"` |
|
|
766
|
+
| **Group Header** | `GroupDisplayBinding` | `GroupHeaderTemplate` |
|
|
767
|
+
| **Even Rows** | `HasUnevenRows="False"` | Auto-sizes (default) |
|
|
768
|
+
| **Empty State** | Manual | `EmptyView` property |
|
|
769
|
+
| **Cells** | TextCell, ImageCell, etc. | Custom DataTemplate |
|
|
770
|
+
|
|
771
|
+
---
|
|
772
|
+
|
|
773
|
+
## Deprecated APIs (P1 - Fix Soon)
|
|
774
|
+
|
|
775
|
+
These APIs still work in .NET 10 but show compiler warnings. They will be removed in future versions.
|
|
776
|
+
|
|
777
|
+
### 1. Animation Methods
|
|
778
|
+
|
|
779
|
+
**Status:** ⚠️ **DEPRECATED** - All sync animation methods replaced with async versions.
|
|
780
|
+
|
|
781
|
+
**Warning You'll See:**
|
|
782
|
+
```
|
|
783
|
+
warning CS0618: 'ViewExtensions.FadeTo(VisualElement, double, uint, Easing)' is obsolete: 'Please use FadeToAsync instead.'
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
**Migration Table:**
|
|
787
|
+
|
|
788
|
+
| Old Method | New Method | Example |
|
|
789
|
+
|-----------|-----------|---------|
|
|
790
|
+
| `FadeTo()` | `FadeToAsync()` | `await view.FadeToAsync(0, 500);` |
|
|
791
|
+
| `ScaleTo()` | `ScaleToAsync()` | `await view.ScaleToAsync(1.5, 300);` |
|
|
792
|
+
| `TranslateTo()` | `TranslateToAsync()` | `await view.TranslateToAsync(100, 100, 250);` |
|
|
793
|
+
| `RotateTo()` | `RotateToAsync()` | `await view.RotateToAsync(360, 500);` |
|
|
794
|
+
| `RotateXTo()` | `RotateXToAsync()` | `await view.RotateXToAsync(45, 300);` |
|
|
795
|
+
| `RotateYTo()` | `RotateYToAsync()` | `await view.RotateYToAsync(45, 300);` |
|
|
796
|
+
| `ScaleXTo()` | `ScaleXToAsync()` | `await view.ScaleXToAsync(2.0, 300);` |
|
|
797
|
+
| `ScaleYTo()` | `ScaleYToAsync()` | `await view.ScaleYToAsync(2.0, 300);` |
|
|
798
|
+
| `RelRotateTo()` | `RelRotateToAsync()` | `await view.RelRotateToAsync(90, 300);` |
|
|
799
|
+
| `RelScaleTo()` | `RelScaleToAsync()` | `await view.RelScaleToAsync(0.5, 300);` |
|
|
800
|
+
| `LayoutTo()` | `LayoutToAsync()` | See special note below |
|
|
801
|
+
|
|
802
|
+
#### Migration Examples
|
|
803
|
+
|
|
804
|
+
**Simple Animation:**
|
|
805
|
+
```csharp
|
|
806
|
+
// ❌ OLD (Deprecated)
|
|
807
|
+
await myButton.FadeTo(0, 500);
|
|
808
|
+
await myButton.ScaleTo(1.5, 300);
|
|
809
|
+
await myButton.TranslateTo(100, 100, 250);
|
|
810
|
+
|
|
811
|
+
// ✅ NEW (Required)
|
|
812
|
+
await myButton.FadeToAsync(0, 500);
|
|
813
|
+
await myButton.ScaleToAsync(1.5, 300);
|
|
814
|
+
await myButton.TranslateToAsync(100, 100, 250);
|
|
815
|
+
```
|
|
816
|
+
|
|
817
|
+
**Sequential Animations:**
|
|
818
|
+
```csharp
|
|
819
|
+
// ❌ OLD
|
|
820
|
+
await image.FadeTo(0, 300);
|
|
821
|
+
await image.ScaleTo(0.5, 300);
|
|
822
|
+
await image.FadeTo(1, 300);
|
|
823
|
+
|
|
824
|
+
// ✅ NEW
|
|
825
|
+
await image.FadeToAsync(0, 300);
|
|
826
|
+
await image.ScaleToAsync(0.5, 300);
|
|
827
|
+
await image.FadeToAsync(1, 300);
|
|
828
|
+
```
|
|
829
|
+
|
|
830
|
+
**Parallel Animations:**
|
|
831
|
+
```csharp
|
|
832
|
+
// ❌ OLD
|
|
833
|
+
await Task.WhenAll(
|
|
834
|
+
image.FadeTo(0, 300),
|
|
835
|
+
image.ScaleTo(0.5, 300),
|
|
836
|
+
image.RotateTo(360, 300)
|
|
837
|
+
);
|
|
838
|
+
|
|
839
|
+
// ✅ NEW
|
|
840
|
+
await Task.WhenAll(
|
|
841
|
+
image.FadeToAsync(0, 300),
|
|
842
|
+
image.ScaleToAsync(0.5, 300),
|
|
843
|
+
image.RotateToAsync(360, 300)
|
|
844
|
+
);
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
**With Cancellation:**
|
|
848
|
+
```csharp
|
|
849
|
+
// NEW: Async methods support cancellation
|
|
850
|
+
CancellationTokenSource cts = new();
|
|
851
|
+
|
|
852
|
+
try
|
|
853
|
+
{
|
|
854
|
+
await view.FadeToAsync(0, 2000);
|
|
855
|
+
}
|
|
856
|
+
catch (TaskCanceledException)
|
|
857
|
+
{
|
|
858
|
+
// Animation was cancelled
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
// Cancel from elsewhere
|
|
862
|
+
cts.Cancel();
|
|
863
|
+
```
|
|
864
|
+
|
|
865
|
+
#### Special Case: LayoutTo
|
|
866
|
+
|
|
867
|
+
`LayoutToAsync()` is deprecated with a special message: "Use Translation to animate layout changes."
|
|
868
|
+
|
|
869
|
+
```csharp
|
|
870
|
+
// ❌ OLD (Deprecated)
|
|
871
|
+
await view.LayoutToAsync(new Rect(100, 100, 200, 200), 250);
|
|
872
|
+
|
|
873
|
+
// ✅ NEW (Use TranslateToAsync instead)
|
|
874
|
+
await view.TranslateToAsync(100, 100, 250);
|
|
875
|
+
|
|
876
|
+
// Or animate Translation properties directly
|
|
877
|
+
var animation = new Animation(v => view.TranslationX = v, 0, 100);
|
|
878
|
+
animation.Commit(view, "MoveX", length: 250);
|
|
879
|
+
```
|
|
880
|
+
|
|
881
|
+
---
|
|
882
|
+
|
|
883
|
+
### 2. DisplayAlert and DisplayActionSheet
|
|
884
|
+
|
|
885
|
+
**Status:** ⚠️ **DEPRECATED** - Sync methods replaced with async versions.
|
|
886
|
+
|
|
887
|
+
**Warning You'll See:**
|
|
888
|
+
```
|
|
889
|
+
warning CS0618: 'Page.DisplayAlert(string, string, string)' is obsolete: 'Use DisplayAlertAsync instead'
|
|
890
|
+
```
|
|
891
|
+
|
|
892
|
+
#### Migration Examples
|
|
893
|
+
|
|
894
|
+
**DisplayAlert:**
|
|
895
|
+
```csharp
|
|
896
|
+
// ❌ OLD (Deprecated)
|
|
897
|
+
await DisplayAlert("Success", "Data saved successfully", "OK");
|
|
898
|
+
await DisplayAlert("Error", "Failed to save", "Cancel");
|
|
899
|
+
bool result = await DisplayAlert("Confirm", "Delete this item?", "Yes", "No");
|
|
900
|
+
|
|
901
|
+
// ✅ NEW (Required)
|
|
902
|
+
await DisplayAlertAsync("Success", "Data saved successfully", "OK");
|
|
903
|
+
await DisplayAlertAsync("Error", "Failed to save", "Cancel");
|
|
904
|
+
bool result = await DisplayAlertAsync("Confirm", "Delete this item?", "Yes", "No");
|
|
905
|
+
```
|
|
906
|
+
|
|
907
|
+
**DisplayActionSheet:**
|
|
908
|
+
```csharp
|
|
909
|
+
// ❌ OLD (Deprecated)
|
|
910
|
+
string action = await DisplayActionSheet(
|
|
911
|
+
"Choose an action",
|
|
912
|
+
"Cancel",
|
|
913
|
+
"Delete",
|
|
914
|
+
"Edit", "Share", "Duplicate"
|
|
915
|
+
);
|
|
916
|
+
|
|
917
|
+
// ✅ NEW (Required)
|
|
918
|
+
string action = await DisplayActionSheetAsync(
|
|
919
|
+
"Choose an action",
|
|
920
|
+
"Cancel",
|
|
921
|
+
"Delete",
|
|
922
|
+
"Edit", "Share", "Duplicate"
|
|
923
|
+
);
|
|
924
|
+
```
|
|
925
|
+
|
|
926
|
+
**In ViewModels (with IDispatcher):**
|
|
927
|
+
```csharp
|
|
928
|
+
// If you're calling from a ViewModel, you'll need access to a Page
|
|
929
|
+
public class MyViewModel
|
|
930
|
+
{
|
|
931
|
+
private readonly IDispatcher _dispatcher;
|
|
932
|
+
private readonly Page _page;
|
|
933
|
+
|
|
934
|
+
public MyViewModel(IDispatcher dispatcher, Page page)
|
|
935
|
+
{
|
|
936
|
+
_dispatcher = dispatcher;
|
|
937
|
+
_page = page;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
public async Task ShowAlertAsync()
|
|
941
|
+
{
|
|
942
|
+
await _dispatcher.DispatchAsync(async () =>
|
|
943
|
+
{
|
|
944
|
+
await _page.DisplayAlertAsync("Info", "Message from ViewModel", "OK");
|
|
945
|
+
});
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
```
|
|
949
|
+
|
|
950
|
+
---
|
|
951
|
+
|
|
952
|
+
### 3. Page.IsBusy
|
|
953
|
+
|
|
954
|
+
**Status:** ⚠️ **DEPRECATED** - Property will be removed in .NET 11.
|
|
955
|
+
|
|
956
|
+
**Warning You'll See:**
|
|
957
|
+
```
|
|
958
|
+
warning CS0618: 'Page.IsBusy' is obsolete: 'Page.IsBusy has been deprecated and will be removed in .NET 11'
|
|
959
|
+
```
|
|
960
|
+
|
|
961
|
+
**Why It's Deprecated:**
|
|
962
|
+
- Inconsistent behavior across platforms
|
|
963
|
+
- Limited customization options
|
|
964
|
+
- Doesn't work well with modern MVVM patterns
|
|
965
|
+
|
|
966
|
+
#### Migration Examples
|
|
967
|
+
|
|
968
|
+
**Simple Page:**
|
|
969
|
+
```xaml
|
|
970
|
+
<!-- ❌ OLD (Deprecated) -->
|
|
971
|
+
<ContentPage IsBusy="{Binding IsLoading}">
|
|
972
|
+
<StackLayout>
|
|
973
|
+
<Label Text="Content here" />
|
|
974
|
+
</StackLayout>
|
|
975
|
+
</ContentPage>
|
|
976
|
+
|
|
977
|
+
<!-- ✅ NEW (Recommended) -->
|
|
978
|
+
<ContentPage>
|
|
979
|
+
<Grid>
|
|
980
|
+
<!-- Main content -->
|
|
981
|
+
<StackLayout>
|
|
982
|
+
<Label Text="Content here" />
|
|
983
|
+
</StackLayout>
|
|
984
|
+
|
|
985
|
+
<!-- Loading indicator overlay -->
|
|
986
|
+
<ActivityIndicator IsRunning="{Binding IsLoading}"
|
|
987
|
+
IsVisible="{Binding IsLoading}"
|
|
988
|
+
Color="{StaticResource Primary}"
|
|
989
|
+
VerticalOptions="Center"
|
|
990
|
+
HorizontalOptions="Center" />
|
|
991
|
+
</Grid>
|
|
992
|
+
</ContentPage>
|
|
993
|
+
```
|
|
994
|
+
|
|
995
|
+
**With Loading Overlay:**
|
|
996
|
+
```xaml
|
|
997
|
+
<!-- ✅ Better: Custom loading overlay -->
|
|
998
|
+
<ContentPage>
|
|
999
|
+
<Grid>
|
|
1000
|
+
<!-- Main content -->
|
|
1001
|
+
<ScrollView>
|
|
1002
|
+
<VerticalStackLayout Padding="20">
|
|
1003
|
+
<Label Text="Your content here" />
|
|
1004
|
+
</VerticalStackLayout>
|
|
1005
|
+
</ScrollView>
|
|
1006
|
+
|
|
1007
|
+
<!-- Loading overlay -->
|
|
1008
|
+
<Grid IsVisible="{Binding IsLoading}"
|
|
1009
|
+
BackgroundColor="#80000000">
|
|
1010
|
+
<VerticalStackLayout VerticalOptions="Center"
|
|
1011
|
+
HorizontalOptions="Center"
|
|
1012
|
+
Spacing="10">
|
|
1013
|
+
<ActivityIndicator IsRunning="True"
|
|
1014
|
+
Color="White" />
|
|
1015
|
+
<Label Text="Loading..."
|
|
1016
|
+
TextColor="White" />
|
|
1017
|
+
</VerticalStackLayout>
|
|
1018
|
+
</Grid>
|
|
1019
|
+
</Grid>
|
|
1020
|
+
</ContentPage>
|
|
1021
|
+
```
|
|
1022
|
+
|
|
1023
|
+
**In Code-Behind:**
|
|
1024
|
+
```csharp
|
|
1025
|
+
// ❌ OLD (Deprecated)
|
|
1026
|
+
public partial class MyPage : ContentPage
|
|
1027
|
+
{
|
|
1028
|
+
async Task LoadDataAsync()
|
|
1029
|
+
{
|
|
1030
|
+
IsBusy = true;
|
|
1031
|
+
try
|
|
1032
|
+
{
|
|
1033
|
+
await LoadDataFromServerAsync();
|
|
1034
|
+
}
|
|
1035
|
+
finally
|
|
1036
|
+
{
|
|
1037
|
+
IsBusy = false;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
// ✅ NEW (Recommended)
|
|
1043
|
+
public partial class MyPage : ContentPage
|
|
1044
|
+
{
|
|
1045
|
+
async Task LoadDataAsync()
|
|
1046
|
+
{
|
|
1047
|
+
LoadingIndicator.IsVisible = true;
|
|
1048
|
+
LoadingIndicator.IsRunning = true;
|
|
1049
|
+
try
|
|
1050
|
+
{
|
|
1051
|
+
await LoadDataFromServerAsync();
|
|
1052
|
+
}
|
|
1053
|
+
finally
|
|
1054
|
+
{
|
|
1055
|
+
LoadingIndicator.IsVisible = false;
|
|
1056
|
+
LoadingIndicator.IsRunning = false;
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
```
|
|
1061
|
+
|
|
1062
|
+
**In ViewModel:**
|
|
1063
|
+
```csharp
|
|
1064
|
+
public class MyViewModel : INotifyPropertyChanged
|
|
1065
|
+
{
|
|
1066
|
+
private bool _isLoading;
|
|
1067
|
+
public bool IsLoading
|
|
1068
|
+
{
|
|
1069
|
+
get => _isLoading;
|
|
1070
|
+
set
|
|
1071
|
+
{
|
|
1072
|
+
_isLoading = value;
|
|
1073
|
+
OnPropertyChanged();
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
public async Task LoadDataAsync()
|
|
1078
|
+
{
|
|
1079
|
+
IsLoading = true;
|
|
1080
|
+
try
|
|
1081
|
+
{
|
|
1082
|
+
await LoadDataFromServerAsync();
|
|
1083
|
+
}
|
|
1084
|
+
finally
|
|
1085
|
+
{
|
|
1086
|
+
IsLoading = false;
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
```
|
|
1091
|
+
|
|
1092
|
+
---
|
|
1093
|
+
|
|
1094
|
+
### 4. MediaPicker APIs
|
|
1095
|
+
|
|
1096
|
+
**Status:** ⚠️ **DEPRECATED** - Single-selection methods replaced with multi-selection variants.
|
|
1097
|
+
|
|
1098
|
+
**Warning You'll See:**
|
|
1099
|
+
```
|
|
1100
|
+
warning CS0618: 'MediaPicker.PickPhotoAsync(MediaPickerOptions)' is obsolete: 'Switch to PickPhotosAsync which also allows multiple selections.'
|
|
1101
|
+
warning CS0618: 'MediaPicker.PickVideoAsync(MediaPickerOptions)' is obsolete: 'Switch to PickVideosAsync which also allows multiple selections.'
|
|
1102
|
+
```
|
|
1103
|
+
|
|
1104
|
+
**What Changed:**
|
|
1105
|
+
- `PickPhotoAsync()` → `PickPhotosAsync()` (returns `List<FileResult>`)
|
|
1106
|
+
- `PickVideoAsync()` → `PickVideosAsync()` (returns `List<FileResult>`)
|
|
1107
|
+
- New `SelectionLimit` property on `MediaPickerOptions` (default: 1)
|
|
1108
|
+
- Old methods still work but are marked obsolete
|
|
1109
|
+
|
|
1110
|
+
**Key Behavior:**
|
|
1111
|
+
- **Default behavior preserved:** `SelectionLimit = 1` (single selection)
|
|
1112
|
+
- Set `SelectionLimit = 0` for unlimited multi-select
|
|
1113
|
+
- Set `SelectionLimit > 1` for specific limits
|
|
1114
|
+
|
|
1115
|
+
**Platform Notes:**
|
|
1116
|
+
- ✅ **iOS:** Selection limit enforced by native picker UI
|
|
1117
|
+
- ⚠️ **Android:** Not all custom pickers honor `SelectionLimit` - be aware!
|
|
1118
|
+
- ⚠️ **Windows:** `SelectionLimit` not supported - implement your own validation
|
|
1119
|
+
|
|
1120
|
+
#### Migration Examples
|
|
1121
|
+
|
|
1122
|
+
**Simple Photo Picker (maintain single-selection behavior):**
|
|
1123
|
+
```csharp
|
|
1124
|
+
// ❌ OLD (Deprecated)
|
|
1125
|
+
var photo = await MediaPicker.PickPhotoAsync(new MediaPickerOptions
|
|
1126
|
+
{
|
|
1127
|
+
Title = "Pick a photo"
|
|
1128
|
+
});
|
|
1129
|
+
|
|
1130
|
+
if (photo != null)
|
|
1131
|
+
{
|
|
1132
|
+
var stream = await photo.OpenReadAsync();
|
|
1133
|
+
MyImage.Source = ImageSource.FromStream(() => stream);
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
// ✅ NEW (maintains same behavior - picks only 1 photo)
|
|
1137
|
+
var photos = await MediaPicker.PickPhotosAsync(new MediaPickerOptions
|
|
1138
|
+
{
|
|
1139
|
+
Title = "Pick a photo",
|
|
1140
|
+
SelectionLimit = 1 // Explicit: only 1 photo
|
|
1141
|
+
});
|
|
1142
|
+
|
|
1143
|
+
var photo = photos.FirstOrDefault();
|
|
1144
|
+
if (photo != null)
|
|
1145
|
+
{
|
|
1146
|
+
var stream = await photo.OpenReadAsync();
|
|
1147
|
+
MyImage.Source = ImageSource.FromStream(() => stream);
|
|
1148
|
+
}
|
|
1149
|
+
```
|
|
1150
|
+
|
|
1151
|
+
**Simple Video Picker (maintain single-selection behavior):**
|
|
1152
|
+
```csharp
|
|
1153
|
+
// ❌ OLD (Deprecated)
|
|
1154
|
+
var video = await MediaPicker.PickVideoAsync(new MediaPickerOptions
|
|
1155
|
+
{
|
|
1156
|
+
Title = "Pick a video"
|
|
1157
|
+
});
|
|
1158
|
+
|
|
1159
|
+
if (video != null)
|
|
1160
|
+
{
|
|
1161
|
+
VideoPlayer.Source = video.FullPath;
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
// ✅ NEW (maintains same behavior - picks only 1 video)
|
|
1165
|
+
var videos = await MediaPicker.PickVideosAsync(new MediaPickerOptions
|
|
1166
|
+
{
|
|
1167
|
+
Title = "Pick a video",
|
|
1168
|
+
SelectionLimit = 1 // Explicit: only 1 video
|
|
1169
|
+
});
|
|
1170
|
+
|
|
1171
|
+
var video = videos.FirstOrDefault();
|
|
1172
|
+
if (video != null)
|
|
1173
|
+
{
|
|
1174
|
+
VideoPlayer.Source = video.FullPath;
|
|
1175
|
+
}
|
|
1176
|
+
```
|
|
1177
|
+
|
|
1178
|
+
**Photo Picker without Options (uses defaults):**
|
|
1179
|
+
```csharp
|
|
1180
|
+
// ❌ OLD (Deprecated)
|
|
1181
|
+
var photo = await MediaPicker.PickPhotoAsync();
|
|
1182
|
+
|
|
1183
|
+
// ✅ NEW (default SelectionLimit = 1, so same behavior)
|
|
1184
|
+
var photos = await MediaPicker.PickPhotosAsync();
|
|
1185
|
+
var photo = photos.FirstOrDefault();
|
|
1186
|
+
```
|
|
1187
|
+
|
|
1188
|
+
**Multi-Photo Selection (new capability):**
|
|
1189
|
+
```csharp
|
|
1190
|
+
// ✅ NEW: Pick up to 5 photos
|
|
1191
|
+
var photos = await MediaPicker.PickPhotosAsync(new MediaPickerOptions
|
|
1192
|
+
{
|
|
1193
|
+
Title = "Pick up to 5 photos",
|
|
1194
|
+
SelectionLimit = 5
|
|
1195
|
+
});
|
|
1196
|
+
|
|
1197
|
+
foreach (var photo in photos)
|
|
1198
|
+
{
|
|
1199
|
+
var stream = await photo.OpenReadAsync();
|
|
1200
|
+
// Process each photo
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
// ✅ NEW: Unlimited selection
|
|
1204
|
+
var allPhotos = await MediaPicker.PickPhotosAsync(new MediaPickerOptions
|
|
1205
|
+
{
|
|
1206
|
+
Title = "Pick photos",
|
|
1207
|
+
SelectionLimit = 0 // No limit
|
|
1208
|
+
});
|
|
1209
|
+
```
|
|
1210
|
+
|
|
1211
|
+
**Multi-Video Selection (new capability):**
|
|
1212
|
+
```csharp
|
|
1213
|
+
// ✅ NEW: Pick up to 3 videos
|
|
1214
|
+
var videos = await MediaPicker.PickVideosAsync(new MediaPickerOptions
|
|
1215
|
+
{
|
|
1216
|
+
Title = "Pick up to 3 videos",
|
|
1217
|
+
SelectionLimit = 3
|
|
1218
|
+
});
|
|
1219
|
+
|
|
1220
|
+
foreach (var video in videos)
|
|
1221
|
+
{
|
|
1222
|
+
// Process each video
|
|
1223
|
+
Console.WriteLine($"Selected: {video.FileName}");
|
|
1224
|
+
}
|
|
1225
|
+
```
|
|
1226
|
+
|
|
1227
|
+
**Handling Empty Results:**
|
|
1228
|
+
```csharp
|
|
1229
|
+
// NEW: Returns empty list if user cancels (not null)
|
|
1230
|
+
var photos = await MediaPicker.PickPhotosAsync(new MediaPickerOptions
|
|
1231
|
+
{
|
|
1232
|
+
SelectionLimit = 1
|
|
1233
|
+
});
|
|
1234
|
+
|
|
1235
|
+
// ✅ Check for empty list
|
|
1236
|
+
if (photos.Count == 0)
|
|
1237
|
+
{
|
|
1238
|
+
await DisplayAlertAsync("Cancelled", "No photo selected", "OK");
|
|
1239
|
+
return;
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
var photo = photos.First();
|
|
1243
|
+
// Process photo...
|
|
1244
|
+
```
|
|
1245
|
+
|
|
1246
|
+
**With Try-Catch (same as before):**
|
|
1247
|
+
```csharp
|
|
1248
|
+
try
|
|
1249
|
+
{
|
|
1250
|
+
var photos = await MediaPicker.PickPhotosAsync(new MediaPickerOptions
|
|
1251
|
+
{
|
|
1252
|
+
Title = "Pick a photo",
|
|
1253
|
+
SelectionLimit = 1
|
|
1254
|
+
});
|
|
1255
|
+
|
|
1256
|
+
if (photos.Count > 0)
|
|
1257
|
+
{
|
|
1258
|
+
await ProcessPhotoAsync(photos.First());
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
catch (PermissionException)
|
|
1262
|
+
{
|
|
1263
|
+
await DisplayAlertAsync("Permission Denied", "Camera access required", "OK");
|
|
1264
|
+
}
|
|
1265
|
+
catch (Exception ex)
|
|
1266
|
+
{
|
|
1267
|
+
await DisplayAlertAsync("Error", $"Failed to pick photo: {ex.Message}", "OK");
|
|
1268
|
+
}
|
|
1269
|
+
```
|
|
1270
|
+
|
|
1271
|
+
#### Migration Checklist
|
|
1272
|
+
|
|
1273
|
+
When migrating to the new MediaPicker APIs:
|
|
1274
|
+
|
|
1275
|
+
- [ ] Replace `PickPhotoAsync()` with `PickPhotosAsync()`
|
|
1276
|
+
- [ ] Replace `PickVideoAsync()` with `PickVideosAsync()`
|
|
1277
|
+
- [ ] Set `SelectionLimit = 1` to maintain single-selection behavior
|
|
1278
|
+
- [ ] Change `FileResult?` to `List<FileResult>` (or use `.FirstOrDefault()`)
|
|
1279
|
+
- [ ] Update null checks to empty list checks (`photos.Count == 0`)
|
|
1280
|
+
- [ ] Test on Android - ensure custom pickers respect limit (or add validation)
|
|
1281
|
+
- [ ] Test on Windows - add your own limit validation if needed
|
|
1282
|
+
- [ ] Consider if multi-select would improve your UX (optional)
|
|
1283
|
+
|
|
1284
|
+
#### Platform-Specific Validation (Windows & Android)
|
|
1285
|
+
|
|
1286
|
+
```csharp
|
|
1287
|
+
// ✅ Recommended: Validate selection limit on platforms that don't enforce it
|
|
1288
|
+
var photos = await MediaPicker.PickPhotosAsync(new MediaPickerOptions
|
|
1289
|
+
{
|
|
1290
|
+
Title = "Pick up to 5 photos",
|
|
1291
|
+
SelectionLimit = 5
|
|
1292
|
+
});
|
|
1293
|
+
|
|
1294
|
+
// On Windows and some Android pickers, the limit might not be enforced
|
|
1295
|
+
if (photos.Count > 5)
|
|
1296
|
+
{
|
|
1297
|
+
await DisplayAlertAsync(
|
|
1298
|
+
"Too Many Photos",
|
|
1299
|
+
$"Please select up to 5 photos. You selected {photos.Count}.",
|
|
1300
|
+
"OK"
|
|
1301
|
+
);
|
|
1302
|
+
return;
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
// Continue processing...
|
|
1306
|
+
```
|
|
1307
|
+
|
|
1308
|
+
#### Capture Methods (unchanged)
|
|
1309
|
+
|
|
1310
|
+
**Note:** Capture methods (`CapturePhotoAsync`, `CaptureVideoAsync`) are **NOT** deprecated and remain unchanged:
|
|
1311
|
+
|
|
1312
|
+
```csharp
|
|
1313
|
+
// ✅ These still work as-is (no changes needed)
|
|
1314
|
+
var photo = await MediaPicker.CapturePhotoAsync();
|
|
1315
|
+
var video = await MediaPicker.CaptureVideoAsync();
|
|
1316
|
+
```
|
|
1317
|
+
|
|
1318
|
+
#### Quick Migration Pattern
|
|
1319
|
+
|
|
1320
|
+
**For all existing single-selection code, use this pattern:**
|
|
1321
|
+
|
|
1322
|
+
```csharp
|
|
1323
|
+
// ❌ OLD
|
|
1324
|
+
var photo = await MediaPicker.PickPhotoAsync(options);
|
|
1325
|
+
if (photo != null)
|
|
1326
|
+
{
|
|
1327
|
+
// Process photo
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
// ✅ NEW (drop-in replacement)
|
|
1331
|
+
var photos = await MediaPicker.PickPhotosAsync(options ?? new MediaPickerOptions { SelectionLimit = 1 });
|
|
1332
|
+
var photo = photos.FirstOrDefault();
|
|
1333
|
+
if (photo != null)
|
|
1334
|
+
{
|
|
1335
|
+
// Process photo (same code as before)
|
|
1336
|
+
}
|
|
1337
|
+
```
|
|
1338
|
+
|
|
1339
|
+
---
|
|
1340
|
+
|
|
1341
|
+
## Recommended Changes (P2)
|
|
1342
|
+
|
|
1343
|
+
These changes are recommended but not required immediately. Consider migrating during your next refactoring cycle.
|
|
1344
|
+
|
|
1345
|
+
### Application.MainPage
|
|
1346
|
+
|
|
1347
|
+
**Status:** ⚠️ **DEPRECATED** - Property will be removed in future version.
|
|
1348
|
+
|
|
1349
|
+
**Warning You'll See:**
|
|
1350
|
+
```
|
|
1351
|
+
warning CS0618: 'Application.MainPage' is obsolete: 'This property is deprecated. Initialize your application by overriding Application.CreateWindow...'
|
|
1352
|
+
```
|
|
1353
|
+
|
|
1354
|
+
#### Migration Example
|
|
1355
|
+
|
|
1356
|
+
```csharp
|
|
1357
|
+
// ❌ OLD (Deprecated)
|
|
1358
|
+
public partial class App : Application
|
|
1359
|
+
{
|
|
1360
|
+
public App()
|
|
1361
|
+
{
|
|
1362
|
+
InitializeComponent();
|
|
1363
|
+
MainPage = new AppShell();
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
// Changing page later
|
|
1367
|
+
public void SwitchToLoginPage()
|
|
1368
|
+
{
|
|
1369
|
+
MainPage = new LoginPage();
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
// ✅ NEW (Recommended)
|
|
1374
|
+
public partial class App : Application
|
|
1375
|
+
{
|
|
1376
|
+
public App()
|
|
1377
|
+
{
|
|
1378
|
+
InitializeComponent();
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
protected override Window CreateWindow(IActivationState? activationState)
|
|
1382
|
+
{
|
|
1383
|
+
return new Window(new AppShell());
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
// Changing page later
|
|
1387
|
+
public void SwitchToLoginPage()
|
|
1388
|
+
{
|
|
1389
|
+
if (Windows.Count > 0)
|
|
1390
|
+
{
|
|
1391
|
+
Windows[0].Page = new LoginPage();
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
```
|
|
1396
|
+
|
|
1397
|
+
**Benefits of CreateWindow:**
|
|
1398
|
+
- Better multi-window support
|
|
1399
|
+
- More explicit initialization
|
|
1400
|
+
- Cleaner separation of concerns
|
|
1401
|
+
- Works better with Shell
|
|
1402
|
+
|
|
1403
|
+
---
|
|
1404
|
+
|
|
1405
|
+
## Bulk Migration Tools
|
|
1406
|
+
|
|
1407
|
+
Use these find/replace patterns to quickly update your codebase.
|
|
1408
|
+
|
|
1409
|
+
### Visual Studio / VS Code
|
|
1410
|
+
|
|
1411
|
+
**Regex Mode - Find/Replace**
|
|
1412
|
+
|
|
1413
|
+
#### Animation Methods
|
|
1414
|
+
|
|
1415
|
+
```regex
|
|
1416
|
+
Find: \.FadeTo\(
|
|
1417
|
+
Replace: .FadeToAsync(
|
|
1418
|
+
|
|
1419
|
+
Find: \.ScaleTo\(
|
|
1420
|
+
Replace: .ScaleToAsync(
|
|
1421
|
+
|
|
1422
|
+
Find: \.TranslateTo\(
|
|
1423
|
+
Replace: .TranslateToAsync(
|
|
1424
|
+
|
|
1425
|
+
Find: \.RotateTo\(
|
|
1426
|
+
Replace: .RotateToAsync(
|
|
1427
|
+
|
|
1428
|
+
Find: \.RotateXTo\(
|
|
1429
|
+
Replace: .RotateXToAsync(
|
|
1430
|
+
|
|
1431
|
+
Find: \.RotateYTo\(
|
|
1432
|
+
Replace: .RotateYToAsync(
|
|
1433
|
+
|
|
1434
|
+
Find: \.ScaleXTo\(
|
|
1435
|
+
Replace: .ScaleXToAsync(
|
|
1436
|
+
|
|
1437
|
+
Find: \.ScaleYTo\(
|
|
1438
|
+
Replace: .ScaleYToAsync(
|
|
1439
|
+
|
|
1440
|
+
Find: \.RelRotateTo\(
|
|
1441
|
+
Replace: .RelRotateToAsync(
|
|
1442
|
+
|
|
1443
|
+
Find: \.RelScaleTo\(
|
|
1444
|
+
Replace: .RelScaleToAsync(
|
|
1445
|
+
```
|
|
1446
|
+
|
|
1447
|
+
#### Display Methods
|
|
1448
|
+
|
|
1449
|
+
```regex
|
|
1450
|
+
Find: DisplayAlert\(
|
|
1451
|
+
Replace: DisplayAlertAsync(
|
|
1452
|
+
|
|
1453
|
+
Find: DisplayActionSheet\(
|
|
1454
|
+
Replace: DisplayActionSheetAsync(
|
|
1455
|
+
```
|
|
1456
|
+
|
|
1457
|
+
#### MediaPicker Methods
|
|
1458
|
+
|
|
1459
|
+
**⚠️ Note:** MediaPicker migration requires manual code changes due to return type changes (`FileResult?` → `List<FileResult>`). Use these searches to find instances:
|
|
1460
|
+
|
|
1461
|
+
```bash
|
|
1462
|
+
# Find PickPhotoAsync usages
|
|
1463
|
+
grep -rn "PickPhotoAsync" --include="*.cs" .
|
|
1464
|
+
|
|
1465
|
+
# Find PickVideoAsync usages
|
|
1466
|
+
grep -rn "PickVideoAsync" --include="*.cs" .
|
|
1467
|
+
```
|
|
1468
|
+
|
|
1469
|
+
**Manual Migration Pattern:**
|
|
1470
|
+
```csharp
|
|
1471
|
+
// Find: await MediaPicker.PickPhotoAsync(
|
|
1472
|
+
// Replace with:
|
|
1473
|
+
var photos = await MediaPicker.PickPhotosAsync(new MediaPickerOptions { SelectionLimit = 1 });
|
|
1474
|
+
var photo = photos.FirstOrDefault();
|
|
1475
|
+
|
|
1476
|
+
// Find: await MediaPicker.PickVideoAsync(
|
|
1477
|
+
// Replace with:
|
|
1478
|
+
var videos = await MediaPicker.PickVideosAsync(new MediaPickerOptions { SelectionLimit = 1 });
|
|
1479
|
+
var video = videos.FirstOrDefault();
|
|
1480
|
+
```
|
|
1481
|
+
|
|
1482
|
+
#### ListView/TableView Detection (Manual Migration Required)
|
|
1483
|
+
|
|
1484
|
+
**⚠️ Note:** ListView/TableView migration CANNOT be automated. Use these searches to find instances:
|
|
1485
|
+
|
|
1486
|
+
```bash
|
|
1487
|
+
# Find all ListView usages in XAML
|
|
1488
|
+
grep -r "<ListView" --include="*.xaml" .
|
|
1489
|
+
|
|
1490
|
+
# Find all TableView usages in XAML
|
|
1491
|
+
grep -r "<TableView" --include="*.xaml" .
|
|
1492
|
+
|
|
1493
|
+
# Find ListView in C# code
|
|
1494
|
+
grep -r "new ListView\|ListView " --include="*.cs" .
|
|
1495
|
+
|
|
1496
|
+
# Find Cell types in XAML
|
|
1497
|
+
grep -r "TextCell\|ImageCell\|EntryCell\|SwitchCell\|ViewCell" --include="*.xaml" .
|
|
1498
|
+
|
|
1499
|
+
# Find ItemSelected handlers (need to change to SelectionChanged)
|
|
1500
|
+
grep -r "ItemSelected=" --include="*.xaml" .
|
|
1501
|
+
grep -r "ItemSelected\s*\+=" --include="*.cs" .
|
|
1502
|
+
|
|
1503
|
+
# Find ContextActions (need to change to SwipeView)
|
|
1504
|
+
grep -r "ContextActions" --include="*.xaml" .
|
|
1505
|
+
|
|
1506
|
+
# Find platform-specific ListView code (needs removal)
|
|
1507
|
+
grep -r "PlatformConfiguration.*ListView" --include="*.cs" .
|
|
1508
|
+
```
|
|
1509
|
+
|
|
1510
|
+
**Create a Migration Inventory:**
|
|
1511
|
+
```bash
|
|
1512
|
+
# Generate a report of all ListView/TableView instances
|
|
1513
|
+
echo "=== ListView/TableView Migration Inventory ===" > migration-report.txt
|
|
1514
|
+
echo "" >> migration-report.txt
|
|
1515
|
+
echo "XAML ListView instances:" >> migration-report.txt
|
|
1516
|
+
grep -rn "<ListView" --include="*.xaml" . >> migration-report.txt
|
|
1517
|
+
echo "" >> migration-report.txt
|
|
1518
|
+
echo "XAML TableView instances:" >> migration-report.txt
|
|
1519
|
+
grep -rn "<TableView" --include="*.xaml" . >> migration-report.txt
|
|
1520
|
+
echo "" >> migration-report.txt
|
|
1521
|
+
echo "ItemSelected handlers:" >> migration-report.txt
|
|
1522
|
+
grep -rn "ItemSelected" --include="*.xaml" --include="*.cs" . >> migration-report.txt
|
|
1523
|
+
echo "" >> migration-report.txt
|
|
1524
|
+
cat migration-report.txt
|
|
1525
|
+
```
|
|
1526
|
+
|
|
1527
|
+
### PowerShell Script
|
|
1528
|
+
|
|
1529
|
+
```powershell
|
|
1530
|
+
# Replace animation methods in all .cs files
|
|
1531
|
+
Get-ChildItem -Path . -Recurse -Filter *.cs | ForEach-Object {
|
|
1532
|
+
$content = Get-Content $_.FullName -Raw
|
|
1533
|
+
|
|
1534
|
+
# Animation methods
|
|
1535
|
+
$content = $content -replace '\.FadeTo\(', '.FadeToAsync('
|
|
1536
|
+
$content = $content -replace '\.ScaleTo\(', '.ScaleToAsync('
|
|
1537
|
+
$content = $content -replace '\.TranslateTo\(', '.TranslateToAsync('
|
|
1538
|
+
$content = $content -replace '\.RotateTo\(', '.RotateToAsync('
|
|
1539
|
+
$content = $content -replace '\.RotateXTo\(', '.RotateXToAsync('
|
|
1540
|
+
$content = $content -replace '\.RotateYTo\(', '.RotateYToAsync('
|
|
1541
|
+
$content = $content -replace '\.ScaleXTo\(', '.ScaleXToAsync('
|
|
1542
|
+
$content = $content -replace '\.ScaleYTo\(', '.ScaleYToAsync('
|
|
1543
|
+
$content = $content -replace '\.RelRotateTo\(', '.RelRotateToAsync('
|
|
1544
|
+
$content = $content -replace '\.RelScaleTo\(', '.RelScaleToAsync('
|
|
1545
|
+
|
|
1546
|
+
# Display methods
|
|
1547
|
+
$content = $content -replace 'DisplayAlert\(', 'DisplayAlertAsync('
|
|
1548
|
+
$content = $content -replace 'DisplayActionSheet\(', 'DisplayActionSheetAsync('
|
|
1549
|
+
|
|
1550
|
+
Set-Content $_.FullName $content
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
Write-Host "✅ Migration complete!"
|
|
1554
|
+
```
|
|
1555
|
+
|
|
1556
|
+
---
|
|
1557
|
+
|
|
1558
|
+
## Testing Your Upgrade
|
|
1559
|
+
|
|
1560
|
+
### Build Validation
|
|
1561
|
+
|
|
1562
|
+
```bash
|
|
1563
|
+
# Clean solution
|
|
1564
|
+
dotnet clean
|
|
1565
|
+
|
|
1566
|
+
# Restore packages
|
|
1567
|
+
dotnet restore
|
|
1568
|
+
|
|
1569
|
+
# Build for each platform
|
|
1570
|
+
dotnet build -f net10.0-android -c Release
|
|
1571
|
+
dotnet build -f net10.0-ios -c Release
|
|
1572
|
+
dotnet build -f net10.0-maccatalyst -c Release
|
|
1573
|
+
dotnet build -f net10.0-windows -c Release
|
|
1574
|
+
|
|
1575
|
+
# Check for warnings
|
|
1576
|
+
dotnet build --no-incremental 2>&1 | grep -i "warning CS0618"
|
|
1577
|
+
```
|
|
1578
|
+
|
|
1579
|
+
### Enable Warnings as Errors (Temporary)
|
|
1580
|
+
|
|
1581
|
+
```xml
|
|
1582
|
+
<!-- Add to your .csproj to catch all obsolete API usage -->
|
|
1583
|
+
<PropertyGroup>
|
|
1584
|
+
<WarningsAsErrors>CS0618</WarningsAsErrors>
|
|
1585
|
+
</PropertyGroup>
|
|
1586
|
+
```
|
|
1587
|
+
|
|
1588
|
+
### Test Checklist
|
|
1589
|
+
|
|
1590
|
+
- [ ] App launches successfully on all platforms
|
|
1591
|
+
- [ ] All animations work correctly
|
|
1592
|
+
- [ ] Dialogs (alerts/action sheets) display properly
|
|
1593
|
+
- [ ] Loading indicators work (if you used IsBusy)
|
|
1594
|
+
- [ ] Inter-component communication works (MessagingCenter replacement)
|
|
1595
|
+
- [ ] No CS0618 warnings in build output
|
|
1596
|
+
- [ ] No runtime exceptions related to obsolete APIs
|
|
1597
|
+
|
|
1598
|
+
---
|
|
1599
|
+
|
|
1600
|
+
## Troubleshooting
|
|
1601
|
+
|
|
1602
|
+
### Error: 'MessagingCenter' is inaccessible due to its protection level
|
|
1603
|
+
|
|
1604
|
+
**Cause:** MessagingCenter is now internal in .NET 10.
|
|
1605
|
+
|
|
1606
|
+
**Solution:**
|
|
1607
|
+
1. Install `CommunityToolkit.Mvvm` package
|
|
1608
|
+
2. Replace with `WeakReferenceMessenger` (see [MessagingCenter section](#messagingcenter-made-internal))
|
|
1609
|
+
3. Create message classes for each message type
|
|
1610
|
+
4. Don't forget to unregister!
|
|
1611
|
+
|
|
1612
|
+
---
|
|
1613
|
+
|
|
1614
|
+
### Warning: Animation method is obsolete
|
|
1615
|
+
|
|
1616
|
+
**Cause:** Using sync animation methods (`FadeTo`, `ScaleTo`, etc.)
|
|
1617
|
+
|
|
1618
|
+
**Quick Fix:**
|
|
1619
|
+
```bash
|
|
1620
|
+
# Use PowerShell script from Bulk Migration Tools section
|
|
1621
|
+
# Or use Find/Replace patterns
|
|
1622
|
+
```
|
|
1623
|
+
|
|
1624
|
+
**Manual Fix:**
|
|
1625
|
+
Add `Async` to the end of each animation method call:
|
|
1626
|
+
- `FadeTo` → `FadeToAsync`
|
|
1627
|
+
- `ScaleTo` → `ScaleToAsync`
|
|
1628
|
+
- etc.
|
|
1629
|
+
|
|
1630
|
+
---
|
|
1631
|
+
|
|
1632
|
+
### Page.IsBusy doesn't work anymore
|
|
1633
|
+
|
|
1634
|
+
**Cause:** IsBusy still works but is deprecated.
|
|
1635
|
+
|
|
1636
|
+
**Solution:** Replace with explicit ActivityIndicator (see [IsBusy section](#3-pageisbusy))
|
|
1637
|
+
|
|
1638
|
+
---
|
|
1639
|
+
|
|
1640
|
+
### Build fails with "Target framework 'net10.0' not found"
|
|
1641
|
+
|
|
1642
|
+
**Cause:** .NET 10 SDK not installed or not latest version.
|
|
1643
|
+
|
|
1644
|
+
**Solution:**
|
|
1645
|
+
```bash
|
|
1646
|
+
# Check SDK version
|
|
1647
|
+
dotnet --version # Should be 10.0.100 or later
|
|
1648
|
+
|
|
1649
|
+
# Install .NET 10 SDK from:
|
|
1650
|
+
# https://dotnet.microsoft.com/download/dotnet/10.0
|
|
1651
|
+
|
|
1652
|
+
# Update workloads
|
|
1653
|
+
dotnet workload update
|
|
1654
|
+
```
|
|
1655
|
+
|
|
1656
|
+
---
|
|
1657
|
+
|
|
1658
|
+
### MessagingCenter migration breaks existing code
|
|
1659
|
+
|
|
1660
|
+
**Common Issues:**
|
|
1661
|
+
|
|
1662
|
+
1. **Forgot to unregister:**
|
|
1663
|
+
```csharp
|
|
1664
|
+
// ⚠️ Memory leak if you don't unregister
|
|
1665
|
+
protected override void OnDisappearing()
|
|
1666
|
+
{
|
|
1667
|
+
base.OnDisappearing();
|
|
1668
|
+
WeakReferenceMessenger.Default.UnregisterAll(this);
|
|
1669
|
+
}
|
|
1670
|
+
```
|
|
1671
|
+
|
|
1672
|
+
2. **Wrong message type:**
|
|
1673
|
+
```csharp
|
|
1674
|
+
// ❌ Wrong
|
|
1675
|
+
WeakReferenceMessenger.Default.Register<UserLoggedIn>(this, handler);
|
|
1676
|
+
WeakReferenceMessenger.Default.Send(new UserData()); // Wrong type!
|
|
1677
|
+
|
|
1678
|
+
// ✅ Correct
|
|
1679
|
+
WeakReferenceMessenger.Default.Register<UserLoggedInMessage>(this, handler);
|
|
1680
|
+
WeakReferenceMessenger.Default.Send(new UserLoggedInMessage(userData));
|
|
1681
|
+
```
|
|
1682
|
+
|
|
1683
|
+
3. **Recipient parameter confusion:**
|
|
1684
|
+
```csharp
|
|
1685
|
+
// The recipient parameter is the object that registered (this)
|
|
1686
|
+
WeakReferenceMessenger.Default.Register<MyMessage>(this, (recipient, message) =>
|
|
1687
|
+
{
|
|
1688
|
+
// recipient == this
|
|
1689
|
+
// message == the message that was sent
|
|
1690
|
+
});
|
|
1691
|
+
```
|
|
1692
|
+
|
|
1693
|
+
---
|
|
1694
|
+
|
|
1695
|
+
### Warning: MediaPicker methods are obsolete
|
|
1696
|
+
|
|
1697
|
+
**Cause:** Using deprecated `PickPhotoAsync` or `PickVideoAsync` methods.
|
|
1698
|
+
|
|
1699
|
+
**Solution:** Migrate to `PickPhotosAsync` or `PickVideosAsync`:
|
|
1700
|
+
|
|
1701
|
+
```csharp
|
|
1702
|
+
// ❌ OLD
|
|
1703
|
+
var photo = await MediaPicker.PickPhotoAsync(options);
|
|
1704
|
+
|
|
1705
|
+
// ✅ NEW (maintain single-selection)
|
|
1706
|
+
var photos = await MediaPicker.PickPhotosAsync(new MediaPickerOptions
|
|
1707
|
+
{
|
|
1708
|
+
Title = options?.Title,
|
|
1709
|
+
SelectionLimit = 1
|
|
1710
|
+
});
|
|
1711
|
+
var photo = photos.FirstOrDefault();
|
|
1712
|
+
```
|
|
1713
|
+
|
|
1714
|
+
**Key Changes:**
|
|
1715
|
+
- Return type changes from `FileResult?` to `List<FileResult>`
|
|
1716
|
+
- Use `.FirstOrDefault()` to get single result
|
|
1717
|
+
- Set `SelectionLimit = 1` to maintain old behavior
|
|
1718
|
+
- Check `photos.Count == 0` instead of `photo == null`
|
|
1719
|
+
|
|
1720
|
+
---
|
|
1721
|
+
|
|
1722
|
+
### MediaPicker returns more items than SelectionLimit
|
|
1723
|
+
|
|
1724
|
+
**Cause:** Windows and some Android custom pickers don't enforce `SelectionLimit`.
|
|
1725
|
+
|
|
1726
|
+
**Solution:** Add manual validation:
|
|
1727
|
+
|
|
1728
|
+
```csharp
|
|
1729
|
+
var photos = await MediaPicker.PickPhotosAsync(new MediaPickerOptions
|
|
1730
|
+
{
|
|
1731
|
+
SelectionLimit = 5
|
|
1732
|
+
});
|
|
1733
|
+
|
|
1734
|
+
if (photos.Count > 5)
|
|
1735
|
+
{
|
|
1736
|
+
await DisplayAlertAsync("Error", "Too many photos selected", "OK");
|
|
1737
|
+
return;
|
|
1738
|
+
}
|
|
1739
|
+
```
|
|
1740
|
+
|
|
1741
|
+
---
|
|
1742
|
+
|
|
1743
|
+
### Animation doesn't complete after migration
|
|
1744
|
+
|
|
1745
|
+
**Cause:** Forgetting `await` keyword.
|
|
1746
|
+
|
|
1747
|
+
```csharp
|
|
1748
|
+
// ❌ Wrong - animation runs but code continues immediately
|
|
1749
|
+
view.FadeToAsync(0, 500);
|
|
1750
|
+
DoSomethingElse();
|
|
1751
|
+
|
|
1752
|
+
// ✅ Correct - wait for animation to complete
|
|
1753
|
+
await view.FadeToAsync(0, 500);
|
|
1754
|
+
DoSomethingElse();
|
|
1755
|
+
```
|
|
1756
|
+
|
|
1757
|
+
---
|
|
1758
|
+
|
|
1759
|
+
### Warning: ListView/TableView/TextCell is obsolete
|
|
1760
|
+
|
|
1761
|
+
**Cause:** Using deprecated ListView, TableView, or Cell types.
|
|
1762
|
+
|
|
1763
|
+
**Solution:** Migrate to CollectionView (see [ListView and TableView section](#listview-and-tableview-deprecated))
|
|
1764
|
+
|
|
1765
|
+
**Quick Decision Guide:**
|
|
1766
|
+
- **Simple list** → CollectionView with custom DataTemplate
|
|
1767
|
+
- **Settings page with <20 items** → VerticalStackLayout with BindableLayout
|
|
1768
|
+
- **Settings page with 20+ items** → Grouped CollectionView
|
|
1769
|
+
- **Grouped data list** → CollectionView with `IsGrouped="True"`
|
|
1770
|
+
|
|
1771
|
+
---
|
|
1772
|
+
|
|
1773
|
+
### CollectionView doesn't have SelectedItem event
|
|
1774
|
+
|
|
1775
|
+
**Cause:** CollectionView uses `SelectionChanged` instead of `ItemSelected`.
|
|
1776
|
+
|
|
1777
|
+
**Solution:**
|
|
1778
|
+
```csharp
|
|
1779
|
+
// ❌ OLD (ListView)
|
|
1780
|
+
void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
|
|
1781
|
+
{
|
|
1782
|
+
var item = e.SelectedItem as MyItem;
|
|
1783
|
+
}
|
|
1784
|
+
|
|
1785
|
+
// ✅ NEW (CollectionView)
|
|
1786
|
+
void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
|
|
1787
|
+
{
|
|
1788
|
+
var item = e.CurrentSelection.FirstOrDefault() as MyItem;
|
|
1789
|
+
}
|
|
1790
|
+
```
|
|
1791
|
+
|
|
1792
|
+
---
|
|
1793
|
+
|
|
1794
|
+
### Platform-specific ListView configuration is obsolete
|
|
1795
|
+
|
|
1796
|
+
**Cause:** Using `Microsoft.Maui.Controls.PlatformConfiguration.*Specific.ListView` extensions.
|
|
1797
|
+
|
|
1798
|
+
**Error:**
|
|
1799
|
+
```
|
|
1800
|
+
warning CS0618: 'ListView' is obsolete: 'With the deprecation of ListView, this class is obsolete. Please use CollectionView instead.'
|
|
1801
|
+
```
|
|
1802
|
+
|
|
1803
|
+
**Solution:**
|
|
1804
|
+
1. Remove platform-specific ListView using statements:
|
|
1805
|
+
```csharp
|
|
1806
|
+
// ❌ Remove these
|
|
1807
|
+
using Microsoft.Maui.Controls.PlatformConfiguration;
|
|
1808
|
+
using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;
|
|
1809
|
+
using Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpecific;
|
|
1810
|
+
```
|
|
1811
|
+
|
|
1812
|
+
2. Remove platform-specific ListView calls:
|
|
1813
|
+
```csharp
|
|
1814
|
+
// ❌ Remove these
|
|
1815
|
+
myListView.On<iOS>().SetSeparatorStyle(SeparatorStyle.FullWidth);
|
|
1816
|
+
myListView.On<Android>().IsFastScrollEnabled();
|
|
1817
|
+
viewCell.On<iOS>().SetDefaultBackgroundColor(Colors.White);
|
|
1818
|
+
```
|
|
1819
|
+
|
|
1820
|
+
3. CollectionView has different platform customization options - consult CollectionView docs for alternatives.
|
|
1821
|
+
|
|
1822
|
+
---
|
|
1823
|
+
|
|
1824
|
+
### CollectionView performance issues after ListView migration
|
|
1825
|
+
|
|
1826
|
+
**Common Causes:**
|
|
1827
|
+
|
|
1828
|
+
1. **Not using DataTemplate caching:**
|
|
1829
|
+
```xaml
|
|
1830
|
+
<!-- ❌ Bad performance -->
|
|
1831
|
+
<CollectionView.ItemTemplate>
|
|
1832
|
+
<DataTemplate>
|
|
1833
|
+
<ComplexView />
|
|
1834
|
+
</DataTemplate>
|
|
1835
|
+
</CollectionView.ItemTemplate>
|
|
1836
|
+
|
|
1837
|
+
<!-- ✅ Better - use simpler templates -->
|
|
1838
|
+
<CollectionView.ItemTemplate>
|
|
1839
|
+
<DataTemplate>
|
|
1840
|
+
<VerticalStackLayout Padding="10">
|
|
1841
|
+
<Label Text="{Binding Title}" />
|
|
1842
|
+
</VerticalStackLayout>
|
|
1843
|
+
</DataTemplate>
|
|
1844
|
+
</CollectionView.ItemTemplate>
|
|
1845
|
+
```
|
|
1846
|
+
|
|
1847
|
+
2. **Complex nested layouts:**
|
|
1848
|
+
- Avoid deeply nested layouts in ItemTemplate
|
|
1849
|
+
- Use Grid instead of StackLayout when possible
|
|
1850
|
+
- Consider FlexLayout for complex layouts
|
|
1851
|
+
|
|
1852
|
+
3. **Images not being cached:**
|
|
1853
|
+
```xaml
|
|
1854
|
+
<Image Source="{Binding ImageUrl}"
|
|
1855
|
+
Aspect="AspectFill"
|
|
1856
|
+
HeightRequest="80"
|
|
1857
|
+
WidthRequest="80">
|
|
1858
|
+
<Image.Behaviors>
|
|
1859
|
+
<!-- Add caching behavior if needed -->
|
|
1860
|
+
</Image.Behaviors>
|
|
1861
|
+
</Image>
|
|
1862
|
+
```
|
|
1863
|
+
|
|
1864
|
+
---
|
|
1865
|
+
|
|
1866
|
+
## Quick Reference Card
|
|
1867
|
+
|
|
1868
|
+
### Priority Checklist
|
|
1869
|
+
|
|
1870
|
+
**Must Fix (P0 - Breaking/Critical):**
|
|
1871
|
+
- [ ] Replace `MessagingCenter` with `WeakReferenceMessenger`
|
|
1872
|
+
- [ ] Migrate `ListView` to `CollectionView`
|
|
1873
|
+
- [ ] Migrate `TableView` to `CollectionView` or `BindableLayout`
|
|
1874
|
+
- [ ] Replace `TextCell`, `ImageCell`, etc. with custom DataTemplates
|
|
1875
|
+
- [ ] Convert `ContextActions` to `SwipeView`
|
|
1876
|
+
- [ ] Remove platform-specific ListView configurations
|
|
1877
|
+
|
|
1878
|
+
**Should Fix (P1 - Deprecated):**
|
|
1879
|
+
- [ ] Update animation methods: add `Async` suffix
|
|
1880
|
+
- [ ] Update `DisplayAlert` → `DisplayAlertAsync`
|
|
1881
|
+
- [ ] Update `DisplayActionSheet` → `DisplayActionSheetAsync`
|
|
1882
|
+
- [ ] Replace `Page.IsBusy` with `ActivityIndicator`
|
|
1883
|
+
- [ ] Replace `PickPhotoAsync` → `PickPhotosAsync` (with `SelectionLimit = 1`)
|
|
1884
|
+
- [ ] Replace `PickVideoAsync` → `PickVideosAsync` (with `SelectionLimit = 1`)
|
|
1885
|
+
|
|
1886
|
+
**Nice to Have (P2):**
|
|
1887
|
+
- [ ] Migrate `Application.MainPage` to `CreateWindow`
|
|
1888
|
+
|
|
1889
|
+
### Common Patterns
|
|
1890
|
+
|
|
1891
|
+
```csharp
|
|
1892
|
+
// Animation
|
|
1893
|
+
await view.FadeToAsync(0, 500);
|
|
1894
|
+
|
|
1895
|
+
// Alert
|
|
1896
|
+
await DisplayAlertAsync("Title", "Message", "OK");
|
|
1897
|
+
|
|
1898
|
+
// Messaging
|
|
1899
|
+
WeakReferenceMessenger.Default.Send(new MyMessage());
|
|
1900
|
+
WeakReferenceMessenger.Default.Register<MyMessage>(this, (r, m) => { });
|
|
1901
|
+
WeakReferenceMessenger.Default.UnregisterAll(this);
|
|
1902
|
+
|
|
1903
|
+
// Loading
|
|
1904
|
+
IsLoading = true;
|
|
1905
|
+
try { await LoadAsync(); }
|
|
1906
|
+
finally { IsLoading = false; }
|
|
1907
|
+
```
|
|
1908
|
+
|
|
1909
|
+
---
|
|
1910
|
+
|
|
1911
|
+
## Additional Resources
|
|
1912
|
+
|
|
1913
|
+
- **Official Docs:** https://learn.microsoft.com/dotnet/maui/
|
|
1914
|
+
- **Migration Guide:** https://learn.microsoft.com/dotnet/maui/migration/
|
|
1915
|
+
- **GitHub Issues:** https://github.com/dotnet/maui/issues
|
|
1916
|
+
- **CommunityToolkit.Mvvm:** https://learn.microsoft.com/dotnet/communitytoolkit/mvvm/
|
|
1917
|
+
|
|
1918
|
+
---
|
|
1919
|
+
|
|
1920
|
+
**Document Version:** 2.0
|
|
1921
|
+
**Last Updated:** November 2025
|
|
1922
|
+
**Applies To:** .NET MAUI 10.0.100 and later
|