Full Code of klemmchr/MvvmBlazor for AI

master 7115907bf7a8 cached
133 files
202.1 KB
53.2k tokens
346 symbols
1 requests
Download .txt
Showing preview only (236K chars total). Download the full file or copy to clipboard to get everything.
Repository: klemmchr/MvvmBlazor
Branch: master
Commit: 7115907bf7a8
Files: 133
Total size: 202.1 KB

Directory structure:
gitextract_455vp84t/

├── .editorconfig
├── .gitattributes
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug.yml
│   │   ├── config.yml
│   │   ├── feature.yml
│   │   ├── other.yml
│   │   └── question.yml
│   └── workflows/
│       ├── build.yaml
│       └── release.yaml
├── .gitignore
├── LICENSE
├── README.md
├── samples/
│   ├── BlazorClientsideSample.Client/
│   │   ├── App.razor
│   │   ├── BlazorClientsideSample.Client.csproj
│   │   ├── Program.cs
│   │   ├── Properties/
│   │   │   └── launchSettings.json
│   │   ├── Services/
│   │   │   └── WeatherForecastGetter.cs
│   │   ├── _Imports.razor
│   │   └── wwwroot/
│   │       └── index.html
│   ├── BlazorClientsideSample.Server/
│   │   ├── BlazorClientsideSample.Server.csproj
│   │   ├── Controllers/
│   │   │   └── WeatherForecastController.cs
│   │   ├── Program.cs
│   │   ├── Properties/
│   │   │   └── launchSettings.json
│   │   └── Startup.cs
│   ├── BlazorSample.Components/
│   │   ├── BlazorSample.Components.csproj
│   │   ├── Extensions/
│   │   │   └── ServiceCollectionExtensions.cs
│   │   ├── Pages/
│   │   │   ├── Clock.razor
│   │   │   ├── Counter.razor
│   │   │   ├── Index.razor
│   │   │   ├── Parameters.razor
│   │   │   ├── TypedParameters.razor
│   │   │   └── WeatherForecasts.razor
│   │   ├── Shared/
│   │   │   ├── CascadingComponent.razor
│   │   │   ├── CustomBaseComponent.cs
│   │   │   ├── MainLayout.razor
│   │   │   └── Navbar.razor
│   │   ├── _Imports.razor
│   │   └── wwwroot/
│   │       └── site.css
│   ├── BlazorSample.Domain/
│   │   ├── BlazorSample.Domain.csproj
│   │   ├── Converters/
│   │   │   └── IdTypeConverter.cs
│   │   ├── Entities/
│   │   │   ├── IdType.cs
│   │   │   └── WeatherForecastEntity.cs
│   │   ├── Extensions/
│   │   │   └── ServiceCollectionExtensions.cs
│   │   └── Services/
│   │       ├── IWeatherForecastGetter.cs
│   │       └── Navbar/
│   │           ├── NavbarItem.cs
│   │           └── NavbarService.cs
│   ├── BlazorSample.ViewModels/
│   │   ├── BlazorSample.ViewModels.csproj
│   │   ├── CascadingViewModel.cs
│   │   ├── ClockViewModel.cs
│   │   ├── CounterViewModel.cs
│   │   ├── Extensions/
│   │   │   └── ServiceCollectionExtensions.cs
│   │   ├── GlobalUsings.cs
│   │   ├── Navbar/
│   │   │   ├── NavbarItemViewModel.cs
│   │   │   └── NavbarViewModel.cs
│   │   ├── ParametersViewModel.cs
│   │   ├── TypedParametersViewModel.cs
│   │   ├── WeatherForecastViewModel.cs
│   │   └── WeatherForecastsViewModel.cs
│   └── BlazorServersideSample/
│       ├── App.razor
│       ├── BlazorServersideSample.csproj
│       ├── Pages/
│       │   ├── Error.razor
│       │   ├── _Host.cshtml
│       │   └── _Imports.razor
│       ├── Program.cs
│       ├── Properties/
│       │   └── launchSettings.json
│       ├── Services/
│       │   └── WeatherForecastGetter.cs
│       ├── Startup.cs
│       ├── _Imports.razor
│       ├── appsettings.Development.json
│       └── appsettings.json
└── src/
    ├── .editorconfig
    ├── Directory.Build.props
    ├── MvvmBlazor/
    │   ├── MvvmBlazor.csproj
    │   └── Properties/
    │       └── AssemblyInfo.cs
    ├── MvvmBlazor.CodeGenerators/
    │   ├── AnalyzerReleases.Shipped.md
    │   ├── AnalyzerReleases.Unshipped.md
    │   ├── Components/
    │   │   ├── MvvmComponentClassContext.cs
    │   │   ├── MvvmComponentGenerator.cs
    │   │   └── MvvmComponentSyntaxReceiver.cs
    │   ├── Extensions/
    │   │   ├── StringBuilderExtensions.cs
    │   │   └── SymbolExtensions.cs
    │   ├── GlobalUsings.cs
    │   ├── MvvmBlazor.CodeGenerators.csproj
    │   ├── NotifyPropertyChanged/
    │   │   ├── NotifyPropertyChangedContext.cs
    │   │   ├── NotifyPropertyChangedGenerator.cs
    │   │   └── NotifyPropertyChangedSyntaxReceiver.cs
    │   └── Properties/
    │       └── AssemblyInfo.cs
    ├── MvvmBlazor.Core/
    │   ├── Components/
    │   │   ├── MvvmComponentAttribute.cs
    │   │   ├── MvvmComponentBase.cs
    │   │   └── MvvmComponentBaseT.cs
    │   ├── Extensions/
    │   │   └── ServiceCollectionExtensions.cs
    │   ├── GlobalUsings.cs
    │   ├── Internal/
    │   │   ├── Bindings/
    │   │   │   ├── Binder.cs
    │   │   │   ├── Binding.cs
    │   │   │   ├── BindingException.cs
    │   │   │   └── BindingFactory.cs
    │   │   ├── Parameters/
    │   │   │   ├── ParameterCache.cs
    │   │   │   ├── ParameterException.cs
    │   │   │   ├── ParameterInfo.cs
    │   │   │   ├── ParameterResolver.cs
    │   │   │   └── ViewModelParameterSetter.cs
    │   │   └── WeakEventListener/
    │   │       ├── WeakEventListener.cs
    │   │       └── WeakEventManager.cs
    │   ├── MvvmBlazor.Core.csproj
    │   ├── NotifyAttribute.cs
    │   ├── Properties/
    │   │   ├── AssemblyInfo.cs
    │   │   └── GlobalSuppressions.cs
    │   └── ViewModel/
    │       └── ViewModelBase.cs
    ├── MvvmBlazor.Tests/
    │   ├── .editorconfig
    │   ├── Abstractions/
    │   │   ├── StrictMock.cs
    │   │   └── UnitTest.cs
    │   ├── Components/
    │   │   ├── MvvmComponentBaseTTests.cs
    │   │   ├── MvvmComponentBaseTests.cs
    │   │   └── TestViewModel.cs
    │   ├── Extensions/
    │   │   ├── ServiceCollectionExtensions.cs
    │   │   └── ServiceProviderExtensions.cs
    │   ├── Generators/
    │   │   ├── MvvmComponentGeneratorTests.cs
    │   │   └── NotifyPropertyChangedGeneratorTests.cs
    │   ├── GlobalUsings.cs
    │   ├── Internal/
    │   │   ├── Bindings/
    │   │   │   ├── BinderTests.cs
    │   │   │   ├── BindingFactoryTests.cs
    │   │   │   └── BindingTests.cs
    │   │   ├── Parameters/
    │   │   │   ├── ParameterCacheTests.cs
    │   │   │   ├── ParameterInfoTests.cs
    │   │   │   ├── ParameterResolverTests.cs
    │   │   │   └── ViewModelParameterSetterTests.cs
    │   │   └── WeakEventListener/
    │   │       └── WeakEventListenerTests.cs
    │   ├── MvvmBlazor.Tests.csproj
    │   ├── Properties/
    │   │   └── AssemblyInfo.cs
    │   ├── ViewModel/
    │   │   └── ViewModelBaseTests.cs
    │   └── xunit.runner.json
    ├── MvvmBlazor.sln
    └── MvvmBlazor.sln.DotSettings

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
root = true

[*]
charset = utf-8
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = false
indent_style = space
indent_size = 4

# Microsoft .NET properties
csharp_new_line_before_members_in_object_initializers = false
csharp_preferred_modifier_order = public, private, protected, internal, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async:suggestion
csharp_style_var_elsewhere = true:suggestion
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
dotnet_naming_rule.unity_serialized_field_rule.import_to_resharper = True
dotnet_naming_rule.unity_serialized_field_rule.resharper_description = Unity serialized field
dotnet_naming_rule.unity_serialized_field_rule.resharper_guid = 5f0fdb63-c892-4d2c-9324-15c80b22a7ef
dotnet_naming_rule.unity_serialized_field_rule.severity = warning
dotnet_naming_rule.unity_serialized_field_rule.style = lower_camel_case_style
dotnet_naming_rule.unity_serialized_field_rule.symbols = unity_serialized_field_symbols
dotnet_naming_style.lower_camel_case_style.capitalization = camel_case
dotnet_naming_symbols.unity_serialized_field_symbols.applicable_accessibilities = *
dotnet_naming_symbols.unity_serialized_field_symbols.applicable_kinds =
dotnet_naming_symbols.unity_serialized_field_symbols.resharper_applicable_kinds = unity_serialised_field
dotnet_naming_symbols.unity_serialized_field_symbols.resharper_required_modifiers = instance
dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none
dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:none
dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion
dotnet_style_qualification_for_event = false:suggestion
dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_method = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion

# ReSharper properties
resharper_autodetect_indent_settings = true
resharper_use_indent_from_vs = false
resharper_keep_existing_arrangement = false

resharper_csharp_wrap_arguments_style = chop_if_long
resharper_csharp_wrap_before_invocation_lpar = false
resharper_csharp_wrap_before_invocation_rpar = true
resharper_csharp_wrap_after_invocation_lpar = true

resharper_csharp_wrap_parameters_style = chop_if_long
resharper_csharp_wrap_before_declaration_lpar = false
resharper_csharp_wrap_before_declaration_rpar = false
resharper_csharp_wrap_after_declaration_lpar = true

resharper_csharp_braces_for_ifelse = required

resharper_csharp_wrap_chained_method_calls = chop_if_long

# ReSharper inspection severities
resharper_arrange_redundant_parentheses_highlighting = hint
resharper_arrange_this_qualifier_highlighting = hint
resharper_arrange_type_member_modifiers_highlighting = hint
resharper_arrange_type_modifiers_highlighting = hint
resharper_built_in_type_reference_style_for_member_access_highlighting = hint
resharper_built_in_type_reference_style_highlighting = hint
resharper_redundant_base_qualifier_highlighting = warning
resharper_suggest_var_or_type_built_in_types_highlighting = hint
resharper_suggest_var_or_type_elsewhere_highlighting = hint
resharper_suggest_var_or_type_simple_types_highlighting = hint
resharper_web_config_module_not_resolved_highlighting = warning
resharper_web_config_type_not_resolved_highlighting = warning
resharper_web_config_wrong_module_highlighting = warning

dotnet_diagnostic.CA2007.severity = none # Call ConfigureAwait()
dotnet_diagnostic.RS2003.severity = none # Rule is no longer shipped

[{*.har,*.inputactions,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.stylelintrc,bowerrc,jest.config}]
indent_style = space
indent_size = 4

[*.{appxmanifest,asax,ascx,aspx,axaml,build,cg,cginc,compute,cs,cshtml,dtd,fs,fsi,fsscript,fsx,hlsl,hlsli,hlslinc,master,ml,mli,nuspec,paml,razor,resw,resx,shader,skin,usf,ush,vb,xaml,xamlx,xoml,xsd}]
indent_style = space
indent_size = 4
tab_width = 4

[*.{yaml,yml}]
tab_width = 2
indent_size = 2


================================================
FILE: .gitattributes
================================================
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto

###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs     diff=csharp

###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following 
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln       merge=binary
#*.csproj    merge=binary
#*.vbproj    merge=binary
#*.vcxproj   merge=binary
#*.vcproj    merge=binary
#*.dbproj    merge=binary
#*.fsproj    merge=binary
#*.lsproj    merge=binary
#*.wixproj   merge=binary
#*.modelproj merge=binary
#*.sqlproj   merge=binary
#*.wwaproj   merge=binary

###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg   binary
#*.png   binary
#*.gif   binary

###############################################################################
# diff behavior for common document formats
# 
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the 
# entries below.
###############################################################################
#*.doc   diff=astextplain
#*.DOC   diff=astextplain
#*.docx  diff=astextplain
#*.DOCX  diff=astextplain
#*.dot   diff=astextplain
#*.DOT   diff=astextplain
#*.pdf   diff=astextplain
#*.PDF   diff=astextplain
#*.rtf   diff=astextplain
#*.RTF   diff=astextplain


================================================
FILE: .github/ISSUE_TEMPLATE/bug.yml
================================================
name: Bug Report
description: File a bug report
labels: ["type: bug", "status: triage"]
body:
  - type: markdown
    attributes:
      value: |
        Thanks for taking the time to fill out this bug report!
  - type: textarea
    id: description
    attributes:
      label: Bug description
      description: Explain the issue you have
    validations:
      required: true
  - type: textarea
    id: expectation
    attributes:
      label: Expectation
      description: Outline what your expectation would be
    validations:
      required: true
  - type: textarea
    id: code-sample
    attributes:
      label: Code sample
      description: Provide a code sample that demonstrates your issue
      render: C#
    validations:
      required: true
  - type: input
    id: version
    attributes:
      label: Version
      description: What MvvmBlazor version are you using?
    validations:
      required: true
  - type: dropdown
    id: blazor-type
    attributes:
      label: Are you using Blazor WASM or Blazor Server?
      multiple: false
      options:
        - Blazor WASM
        - Blazor Server
    validations:
      required: true
  - type: dropdown
    id: os
    attributes:
      label: What operation system are you working with?
      multiple: false
      options:
        - Windows
        - Linux
        - macOS
        - Other
    validations:
      required: true


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
  - name: Security vulnerabilities
    url: https://github.com/klemmchr
    about: Please report security vulnerabilities via email to git+mvvmblazor@klemm.one. A link to my PGP key can be found in my profile.


================================================
FILE: .github/ISSUE_TEMPLATE/feature.yml
================================================
name: Feature request
description: Propose a new feature
labels: ["type: feature", "status: triage"]
body:
  - type: textarea
    id: description
    attributes:
      label: Feature description
      description: Describe the feature you would like to have
    validations:
      required: true
  - type: textarea
    id: code-sample
    attributes:
      label: Code sample
      description: Provide a code sample if applicable 
      render: C#
    validations:
      required: false


================================================
FILE: .github/ISSUE_TEMPLATE/other.yml
================================================
name: Other
description: All other issues
labels: ["status: triage"]
body:
  - type: textarea
    id: description
    attributes:
      label: Description
    validations:
      required: true
  - type: textarea
    id: code-sample
    attributes:
      label: Code sample
      description: Provide a code sample if applicable 
      render: C#
    validations:
      required: false


================================================
FILE: .github/ISSUE_TEMPLATE/question.yml
================================================
name: Question
description: Ask a question
labels: ["type: question", "status: triage"]
body:
  - type: textarea
    id: description
    attributes:
      label: Question
      description: Outline the question you have
    validations:
      required: true
  - type: textarea
    id: code-sample
    attributes:
      label: Code sample
      description: Provide a code sample if applicable 
      render: C#
    validations:
      required: false
  - type: input
    id: version
    attributes:
      label: Version
      description: What MvvmBlazor version are you using?
    validations:
      required: true
  - type: dropdown
    id: blazor-type
    attributes:
      label: Are you using Blazor WASM or Blazor Server?
      multiple: false
      options:
        - Blazor WASM
        - Blazor Server
    validations:
      required: true


================================================
FILE: .github/workflows/build.yaml
================================================
on:
  push:
    branches:
      - master
  pull_request:
    branches:
      - master
    paths:
      - 'src/**/*'
      - 'samples/**/*'
      - '.github/workflows/**/*'

name: Build

jobs:

  build:
    name: Build
    runs-on: ubuntu-latest
    steps:

      - uses: actions/checkout@master

      - name: Setup .NET
        uses: actions/setup-dotnet@master
        with:
          dotnet-version: '6.0.x'

      - name: Build
        run: dotnet build --configuration Release -p:ContinuousIntegrationBuild=true -p:TreatWarningsAsErrors=true src

      - name: Test
        run: dotnet test -p:ContinuousIntegrationBuild=true -p:CollectCoverage=true -p:CoverletOutputFormat=lcov -p:CoverletOutput=$GITHUB_WORKSPACE/coverage.lcov src

      - name: Upload coverage
        uses: romeovs/lcov-reporter-action@master
        if: github.event.pull_request.base.ref == 'master'
        with:
          lcov-file: coverage.lcov
          delete-old-comments: true

      - name: Pack
        run: dotnet pack --configuration Release -p:ContinuousIntegrationBuild=true -p:SymbolPackageFormat=snupkg src -o out

      - name: Upload artifacts
        uses: actions/upload-artifact@v2
        with:
          name: packages
          path: out/*


================================================
FILE: .github/workflows/release.yaml
================================================
on:
  release:
    types: [ published ]

name: Release

jobs:

  release:
    name: Release
    runs-on: ubuntu-latest
    steps:

      - uses: actions/checkout@master

      - name: Setup .NET
        uses: actions/setup-dotnet@master
        with:
          dotnet-version: '6.0.x'

      - name: Build
        run: dotnet build -p:ContinuousIntegrationBuild=true -p:TreatWarningsAsErrors=true --configuration Release src

      - name: Test
        run: dotnet test --configuration Release src

      - name: Publish
        run: >
          dotnet nuget add source --username $GITHUB_REPOSITORY_OWNER --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json";
          export VERSION=$(git describe --long --tags --match 'v*' | sed 's/v//' | sed -E 's/[-].+//g');
          echo $VERSION;
          dotnet pack --configuration Release --include-source -p:ContinuousIntegrationBuild=true -p:Version=$VERSION -o out src;
          cd out;
          rm MvvmBlazor.CodeGenerators.*.symbols.nupkg;
          dotnet nuget push *.nupkg --skip-duplicate --force-english-output -k ${{ secrets.NUGET_KEY }} -s https://api.nuget.org/v3/index.json;
          dotnet nuget push *.nupkg --skip-duplicate --force-english-output -s https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json;

      - name: Upload artifacts
        if: always()
        uses: actions/upload-artifact@v2
        with:
          name: packages
          path: out/*


================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore

# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates

# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs

# Mono auto generated files
mono_crash.*

# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/

# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/

# Visual Studio 2017 auto generated files
Generated\ Files/

# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*

# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml

# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c

# Benchmark Results
BenchmarkDotNet.Artifacts/

# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/

# Tye
.tye/

# ASP.NET Scaffolding
ScaffoldingReadMe.txt

# StyleCop
StyleCopReport.xml

# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc

# Chutzpah Test files
_Chutzpah*

# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb

# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap

# Visual Studio Trace Files
*.e2e

# TFS 2012 Local Workspace
$tf/

# Guidance Automation Toolkit
*.gpState

# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user

# TeamCity is a build add-in
_TeamCity*

# DotCover is a Code Coverage Tool
*.dotCover

# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json

# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info

# Visual Studio code coverage results
*.coverage
*.coveragexml

# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*

# MightyMoose
*.mm.*
AutoTest.Net/

# Web workbench (sass)
.sass-cache/

# Installshield output folder
[Ee]xpress/

# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html

# Click-Once directory
publish/

# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj

# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/

# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets

# Microsoft Azure Build Output
csx/
*.build.csdef

# Microsoft Azure Emulator
ecf/
rcf/

# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload

# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/

# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs

# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk

# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/

# RIA/Silverlight projects
Generated_Code/

# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak

# SQL Server files
*.mdf
*.ldf
*.ndf

# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl

# Microsoft Fakes
FakesAssemblies/

# GhostDoc plugin setting file
*.GhostDoc.xml

# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/

# Visual Studio 6 build log
*.plg

# Visual Studio 6 workspace options file
*.opt

# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw

# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions

# Paket dependency manager
.paket/paket.exe
paket-files/

# FAKE - F# Make
.fake/

# CodeRush personal settings
.cr/personal

# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc

# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config

# Tabs Studio
*.tss

# Telerik's JustMock configuration file
*.jmconfig

# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs

# OpenCover UI analysis results
OpenCover/

# Azure Stream Analytics local run output
ASALocalRun/

# MSBuild Binary and Structured Log
*.binlog

# NVidia Nsight GPU debugger configuration file
*.nvuser

# MFractors (Xamarin productivity tool) working folder
.mfractor/

# Local History for Visual Studio
.localhistory/

# BeatPulse healthcheck temp database
healthchecksdb

# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/

# Ionide (cross platform F# VS Code tools) working folder
.ionide/

# Fody - auto-generated XML schema
FodyWeavers.xsd

##
## Visual studio for Mac
##


# globs
Makefile.in
*.userprefs
*.usertasks
config.make
config.status
aclocal.m4
install-sh
autom4te.cache/
*.tar.gz
tarballs/
test-results/

# Mac bundle stuff
*.dmg
*.app

# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
# General
.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon


# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db

# Dump file
*.stackdump

# Folder config file
[Dd]esktop.ini

# Recycle Bin used on file shares
$RECYCLE.BIN/

# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp

# Windows shortcuts
*.lnk

# JetBrains Rider
.idea/
*.sln.iml

##
## Visual Studio Code
##
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2019 Christian Klemm

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
MvvmBlazor
================
[![Build Status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fchris579%2FMvvmBlazor%2Fbadge&style=flat-square)](https://github.com/chris579/MvvmBlazor/actions) [![NuGet](https://img.shields.io/nuget/v/MvvmBlazor.svg?style=flat-square)](https://www.nuget.org/packages/MvvmBlazor) [![NuGet](https://img.shields.io/nuget/dt/MvvmBlazor)](https://www.nuget.org/packages/MvvmBlazor) 


MvvmBlazor is a small framework for building Blazor WebAssembly and Blazor Server apps. With its easy-to-use MVVM pattern you
can increase your development speed while minimising the effort required to make it work.

## Getting started

MvvmBlazor is available on [NuGet](https://www.nuget.org/packages/MvvmBlazor). You will need **.NET 6** to use this
library.

The library needs to be added to the DI container in order to use it. This is done in your `Startup` class.

```c#
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvvm();
    }
}
```

## Usage

### Components

Components need to inherit the base class `MvvmBlazor.Components.MvvmComponentBase` if you want to inject your view
model manually. You can set a binding on any view model you like to.

If you want full support use `MvvmBlazor.Components.MvvmComponentBase<T>` and specify your view model type as a generic
argument.
Your view model will get auto injected into the component and set as a binding context.

#### BindingSource

The binding source is the default source object a binding will be made to. It is set automatically when using the base
class `MvvmBlazor.Components.MvvmComponentBase<T>` where `T` is your view model type. You can access it via
the `BindingContext` property in your component.

#### Bindings

Bindings are achieved via the `Bind` method in the component. If the value of the bound property has changed the
component will be told to rerender automatically. In this example we assume that the class `ClockViewModel` has a
property called `DateTime`.

```c#
@inherits MvvmComponentBase<ClockViewModel>

Current time: @Bind(x => x.DateTime)
```

Bindings can also be done by specifying the binding source explicitly:

```c#
@inherits MvvmComponentBase
@inject ClockViewModel ClockViewModel

Current time: @Bind(ClockViewModel, x => x.DateTime)
```

Bindings also handle background updating automatically. No need to invoke the main thread.

#### Collection Bindings

If you want to have a collection that automatically notifies the component when it has changed you should use one that
implements `INotifyCollectionChanged`, e.g. `ObservableCollection<T>`.

In List scenarios you often chain view models to achieve bindings for every list element on it's corresponding view
model. Given this view models

```c#
class MainViewModel
{
    public ObservableCollection<SubViewModel> Items { get; }
}

class SubViewModel
{
    private string _name;
    public string Name
    {
        get => _name;
        set => Set(ref, _name, value);
    }
}
```

you can use bindings on your sub view models like this

```c#
@foreach (var item in Bind(x => x.Items))
{
    <label>@Bind(item, x => x.Name)</label>
}
```

This way the name of every list item is bound to it's corresponding entry in the view. If you change the name on any
list item, it will be changed in the view too.

### EventHandlers

Event handles work just the way they work in blazor. When you use the non generic base class you can bind any injected
object on them.

```c#
@inherits MvvmComponentBase
@inject CounterViewModel CounterViewModel

<button @onclick="@CounterViewModel.IncrementCount">Click me</button>
```

When using the generic base class you can directly bind them to your binding context.

```c#
@inherits MvvmComponentBase<CounterViewModel>

<button @onclick="@BindingContext.IncrementCount">Click me</button>
```

### ViewModel

View models need to inherit the base class `MvvmBlazor.ViewModel.ViewModelBase`.

If you register a view model as scoped it will be tied to the lifetime of the component and disposed when the component is disposed.
This allows you to inject scoped services that should be short lived (e.g. a `DbContext`) without the need for using a factory.

Note: Some services need to be resolved from the root service provider (e.g. `NavigationManager`). To do this you can access the root service provider via the `RootServiceProvider` property.

#### Property implementation

Bindable properties need to raise the `PropertyChanged` event on the ViewModel.

The `Set`-Method is performing an equality check and is raising this event if needed. An example implementation could
look like this:

```c#
private int _currentCount;
public int CurrentCount
{
    get => _currentCount;
    set => Set(ref _currentCount, value);
}
```

As an alternative you can leverage the power of source generators to do the tedious work for you.
Just declare a private field inside of a view model and decorate if with the `Notify` attribute. The matching property will be auto generated for you.
```c#
[Notify]
private int _currentCount;
```
Note: Some third party IDEs may not recognize source generators at the current point. They could report that the property does not exist while the project builds fine.

#### Lifecycle methods

View models have access to the same lifecycle methods as a component when they are set as a binding context. They are
documented [here](https://docs.microsoft.com/en-us/aspnet/core/blazor/components?view=aspnetcore-3.0#lifecycle-methods).

#### Parameters

Parameters are automatically populated to the view model. Declare a parameter in your component

```c#
@code {
    [Parameter]
    public string Name { get; set; }
}
```

and declare the same parameter in your view model

```c#
class ViewModel: ViewModelBase
{
    [Parameter]
    public string Name { get; set; }
}
```

Cascading parameters are supported as well.
The parameter will be passed when parameters are set on the component. More information regarding the lifecycle can be
found in
the [Microsoft Documentation](https://docs.microsoft.com/en-us/aspnet/core/blazor/components/lifecycle?view=aspnetcore-6.0#lifecycle-events)
.

##### Type conversion

When binding parameters in a component Blazor restricts you to a finite list of primitive types you can use. In some
scenarios you might want to bind to a strong type and perform automatic conversions to it. A typical use case for this
would be a strongly typed identifier that you use in your application.

View model parameters support type conversion
using [`TypeConverter`](https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.typeconverter?view=net-6.0).
Your component parameters still needs to be a supported primitive type, however your view model parameter can be
strongly typed and will get auto converted. An example for this can be found in the TypedParameter sample.

#### Dispose

Since ViewModels are being injected through dependency injection in the scope of the component the
DI [takes care](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-5.0#disposal-of-services)
of disposing ViewModels.

## Advanced scenarios
Some libraries may force you to use a different base class than `MvvmComponentBase`.
To solve this issue you can create a custom mvvm component using source generators.

Create a partial class and decorate it with the `MvvmComponent` attribute.

```c#
[MvvmComponent]
public abstract partial class CustomComponentBase : UiLibraryComponentBase
{

}
```

As a reference point see [`MvvmComponentBase`](https://github.com/klemmchr/MvvmBlazor/blob/master/src/MvvmBlazor.Core/Components/MvvmComponentBase.cs) and [`MvvmComponentBase<T>`](https://github.com/klemmchr/MvvmBlazor/blob/master/src/MvvmBlazor.Core/Components/MvvmComponentBaseT.cs).
Both are generated by a source generator as well.

## Examples

Examples for Blazor and Serverside Blazor can be
found [here](https://github.com/chris579/MvvmBlazor/tree/master/samples).

You will find several projects in there

- *BlazorServersideSample*
  A server for the blazor serverside sample
- *BlazorClientsideSample.Server*
  The server for the blazor clientside sample
- *BlazorClientsideSample.Client*
  The client for the blazor clientside sample

These projects act as wrapper projects for the main functionality that is shared among these examples.

- *BlazorSample.Components*
  The components and pages for the samples
- *BlazorSample.ViewModels*
  The view models for the pages
- *BlazorSample.Domain*
  Domain logic, stuff shared between components and view models



================================================
FILE: samples/BlazorClientsideSample.Client/App.razor
================================================
<Router AppAssembly="@typeof(BlazorSample.Components._Imports).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/>
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(MainLayout)">
            <p>Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>

================================================
FILE: samples/BlazorClientsideSample.Client/BlazorClientsideSample.Client.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">

    <PropertyGroup>
        <TargetFramework>net6.0</TargetFramework>
        <BlazorWebAssemblyEnableLinking>false</BlazorWebAssemblyEnableLinking>
        <Nullable>enable</Nullable>
        <ImplicitUsings>true</ImplicitUsings>
        <IsPackable>false</IsPackable>
    </PropertyGroup>

    <ItemGroup>
        <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.2"/>
        <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="6.0.2"/>
    </ItemGroup>

    <ItemGroup>
        <ProjectReference Include="..\..\src\MvvmBlazor.Core\MvvmBlazor.Core.csproj"/>
        <ProjectReference Include="..\BlazorSample.Components\BlazorSample.Components.csproj"/>
    </ItemGroup>

</Project>


================================================
FILE: samples/BlazorClientsideSample.Client/Program.cs
================================================
using BlazorClientsideSample.Client.Services;
using BlazorSample.Components.Extensions;
using BlazorSample.Domain.Extensions;
using BlazorSample.Domain.Services;
using BlazorSample.ViewModels.Extensions;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;

namespace BlazorClientsideSample.Client;

public class Program
{
    public static async Task Main(string[] args)
    {
        var builder = WebAssemblyHostBuilder.CreateDefault(args);

        builder.RootComponents.Add<App>("#app");

        builder.Services.AddMvvm();
        builder.Services.AddDomain().AddComponents().AddViewModels();

        builder.Services.AddSingleton(new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });

        builder.Services.AddSingleton<IWeatherForecastGetter, WeatherForecastGetter>();

        var host = builder.Build();

        await host.RunAsync();
    }
}

================================================
FILE: samples/BlazorClientsideSample.Client/Properties/launchSettings.json
================================================
{
    "profiles": {
        "BlazorClientsideSample.Client": {
            "commandName": "Project",
            "environmentVariables": {
                "ASPNETCORE_ENVIRONMENT": "Development"
            },
            "applicationUrl": "http://localhost:50069/"
        }
    }
}

================================================
FILE: samples/BlazorClientsideSample.Client/Services/WeatherForecastGetter.cs
================================================
using System.Net.Http.Json;
using BlazorSample.Domain.Entities;
using BlazorSample.Domain.Services;

namespace BlazorClientsideSample.Client.Services;

public class WeatherForecastGetter : IWeatherForecastGetter
{
    private readonly HttpClient _httpClient;

    public WeatherForecastGetter(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public Task<IEnumerable<WeatherForecastEntity>> GetForecasts()
    {
        return _httpClient.GetFromJsonAsync<IEnumerable<WeatherForecastEntity>>("WeatherForecast")!;
    }
}

================================================
FILE: samples/BlazorClientsideSample.Client/_Imports.razor
================================================
@using System.Net.Http
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.JSInterop
@using MvvmBlazor.Components
@using BlazorSample.Components.Shared

================================================
FILE: samples/BlazorClientsideSample.Client/wwwroot/index.html
================================================
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <meta content="width=device-width" name="viewport"/>
    <title>BlazorSample</title>
    <base href="/"/>
    <link href="_content/BlazorSample.Components/site.css" rel="stylesheet"/>
    <script src="_content/MatBlazor/dist/matBlazor.js"></script>
    <link href="_content/MatBlazor/dist/matBlazor.css" rel="stylesheet"/>
</head>
<body>
<div id="app">Loading...</div>

<script src="_framework/blazor.webassembly.js"></script>
</body>
</html>

================================================
FILE: samples/BlazorClientsideSample.Server/BlazorClientsideSample.Server.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk.Web">

    <PropertyGroup>
        <TargetFramework>net6.0</TargetFramework>
        <Nullable>enable</Nullable>
        <ImplicitUsings>true</ImplicitUsings>
        <IsPackable>false</IsPackable>
    </PropertyGroup>

    <ItemGroup>

        <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="6.0.2"/>
        <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.2"/>
    </ItemGroup>

    <ItemGroup>
        <ProjectReference Include="..\..\src\MvvmBlazor.Core\MvvmBlazor.Core.csproj"/>
        <ProjectReference Include="..\BlazorClientsideSample.Client\BlazorClientsideSample.Client.csproj"/>
    </ItemGroup>

</Project>


================================================
FILE: samples/BlazorClientsideSample.Server/Controllers/WeatherForecastController.cs
================================================
using BlazorSample.Domain.Entities;
using Microsoft.AspNetCore.Mvc;

namespace BlazorClientsideSample.Server.Controllers;

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    private static readonly string[] Summaries =
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    [HttpGet]
    public IEnumerable<WeatherForecastEntity> Get()
    {
        var rng = new Random();
        return Enumerable.Range(1, 5)
            .Select(
                index => new WeatherForecastEntity
                {
                    Date = DateTime.Now.AddDays(index),
                    TemperatureC = rng.Next(-20, 55),
                    Summary = Summaries[rng.Next(Summaries.Length)]
                }
            )
            .ToArray();
    }
}

================================================
FILE: samples/BlazorClientsideSample.Server/Program.cs
================================================
using Microsoft.AspNetCore;

namespace BlazorClientsideSample.Server;

public class Program
{
    public static void Main(string[] args)
    {
        BuildWebHost(args).Run();
    }

    public static IWebHost BuildWebHost(string[] args)
    {
        return WebHost.CreateDefaultBuilder(args)
            .UseConfiguration(new ConfigurationBuilder().AddCommandLine(args).Build())
            .UseStartup<Startup>()
            .Build();
    }
}

================================================
FILE: samples/BlazorClientsideSample.Server/Properties/launchSettings.json
================================================
{
    "profiles": {
        "BlazorClientsideSample.Server": {
            "commandName": "Project",
            "launchBrowser": true,
            "environmentVariables": {
                "ASPNETCORE_ENVIRONMENT": "Development"
            },
            "applicationUrl": "http://localhost:52345/"
        }
    }
}

================================================
FILE: samples/BlazorClientsideSample.Server/Startup.cs
================================================
using Microsoft.AspNetCore.ResponseCompression;

namespace BlazorClientsideSample.Server;

public class Startup
{
    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc().AddNewtonsoftJson();
        services.AddResponseCompression(
            opts =>
            {
                opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[] { "application/octet-stream" });
            }
        );
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseResponseCompression();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseWebAssemblyDebugging();
        }

        app.UseBlazorFrameworkFiles();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseEndpoints(
            endpoints =>
            {
                endpoints.MapDefaultControllerRoute();
                endpoints.MapFallbackToFile("index.html");
            }
        );
    }
}

================================================
FILE: samples/BlazorSample.Components/BlazorSample.Components.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk.Razor">

    <PropertyGroup>
        <TargetFramework>net6.0</TargetFramework>
        <RazorLangVersion>3.0</RazorLangVersion>
        <Nullable>enable</Nullable>
        <ImplicitUsings>true</ImplicitUsings>
        <IsPackable>false</IsPackable>
    </PropertyGroup>


    <ItemGroup>
        <PackageReference Include="MatBlazor" Version="2.8.0" />
        <PackageReference Include="Microsoft.AspNetCore.Components" Version="6.0.2" />
        <PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="6.0.2" />
    </ItemGroup>


    <ItemGroup>
        <ProjectReference Include="..\BlazorSample.ViewModels\BlazorSample.ViewModels.csproj" />
    </ItemGroup>

    <ItemGroup>
        <ProjectReference Include="..\..\src\MvvmBlazor.CodeGenerators\MvvmBlazor.CodeGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
    </ItemGroup>

</Project>


================================================
FILE: samples/BlazorSample.Components/Extensions/ServiceCollectionExtensions.cs
================================================
using BlazorSample.Components.Pages;
using BlazorSample.Domain.Extensions;
using MatBlazor;
using Microsoft.Extensions.DependencyInjection;
using Index = BlazorSample.Components.Pages.Index;

namespace BlazorSample.Components.Extensions;

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddComponents(this IServiceCollection serviceCollection)
    {
        serviceCollection.AddNavigationItem<Index>("Index", MatIconNames.Home);
        serviceCollection.AddNavigationItem<Counter>("Counter", MatIconNames.Add);
        serviceCollection.AddNavigationItem<WeatherForecasts>("Weather forecasts", MatIconNames.Cloud);
        serviceCollection.AddNavigationItem<Clock>("Clock", MatIconNames.Alarm);
        serviceCollection.AddNavigationItem<Parameters>("Parameters", MatIconNames.List);
        serviceCollection.AddNavigationItem<TypedParameters>("Typed Parameters", MatIconNames.List);

        return serviceCollection;
    }
}

================================================
FILE: samples/BlazorSample.Components/Pages/Clock.razor
================================================
@page "/clock"
@inherits CustomBaseComponent<ClockViewModel>

<MatCard>
    <MatCardContent>
        <MatSubtitle1>
            Having values updating in the background is no hassle. No need to manually invoke
            <pre style="display: inline">StateHasChanged()</pre> on the component inside it's synchronization context.
            That's done for you! Just make sure to update your values using the <pre style="display: inline">Set</pre> method in your view model.
        </MatSubtitle1>
    </MatCardContent>
</MatCard>
<br/>

<MatBody2>
    This component demonstrates updating from a background task without user interaction.
</MatBody2>
<br/>
<MatBody2>
    Current time: @Bind(x => x.DateTime)
</MatBody2>

================================================
FILE: samples/BlazorSample.Components/Pages/Counter.razor
================================================
@page "/counter"
@inherits CustomBaseComponent<CounterViewModel>
@inject ClockViewModel ClockViewModel

<MatCard>
    <MatCardContent>
        <MatSubtitle1>
            The view model gets auto injected and can be accessed with the property <pre style="display: inline">BindingContext</pre>.
            You can use this property to bind event handlers on your controls.
        </MatSubtitle1>
    </MatCardContent>
</MatCard>
<br/>


<MatBody2>Current count: @Bind(x => x.CurrentCount)</MatBody2>

<MatButton OnClick="@BindingContext.IncrementCount" Raised="true">Click me</MatButton>

<br/>
<br/>
<br/>

<MatCard>
    <MatCardContent>
        <MatSubtitle1>
            You can also bind additional view models that you simply inject by yourself.
            The lifecycle methods on such view models will not be invoked by this component.
        </MatSubtitle1>
    </MatCardContent>
</MatCard>
<br/>

<MatBody2>Current time: @Bind(ClockViewModel, x => x.DateTime)</MatBody2>

================================================
FILE: samples/BlazorSample.Components/Pages/Index.razor
================================================
@page "/"

<MatCard>
    <MatCardContent>
        <MatSubtitle1>
            Explore the tabs on the left to see some examples and explore MvvmBlazor!
        </MatSubtitle1>
    </MatCardContent>
</MatCard>

================================================
FILE: samples/BlazorSample.Components/Pages/Parameters.razor
================================================
@page "/parameters/{name}"
@inherits CustomBaseComponent<ParametersViewModel>

<MatCard>
    <MatCardContent>
        <MatSubtitle1>
            You can also pass down parameters to your view model. If you set a binding context you can
            declare your parameters just as you would before but you also declare them in your view model.
            They will be passed down to it automatically.
            Enter a new value down below and click the button. See how the url changed and the parameter is shown.
        </MatSubtitle1>
    </MatCardContent>
</MatCard>
<br/>

<MatBody2>My Name is @Bind(x => x.Name)</MatBody2>
<br/>

<MatTextField @bind-Value="@BindingContext.NewName" Label="New name"></MatTextField>
<MatButton OnClick="@BindingContext.NavigateToNewName" Raised="true">Navigate</MatButton>

<br/>
<br/>

<MatCard>
    <MatCardContent>
        <MatSubtitle1>
            Cascading parameters are supported as well.
        </MatSubtitle1>
    </MatCardContent>
</MatCard>
<br/>

<CascadingValue Value="@("Marc Foo Bar")">
    <CascadingComponent></CascadingComponent>
</CascadingValue>

@code {

    [Parameter]
    public string Name { get; set; } = "Marc";

}

================================================
FILE: samples/BlazorSample.Components/Pages/TypedParameters.razor
================================================
@page "/typed-parameters/{Id}"
@inherits CustomBaseComponent<TypedParametersViewModel>

<MatCard>
    <MatCardContent>
        <MatSubtitle1>
            Converting parameter types is supported out of the box.
            Visit the <a href="https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.typeconverter?view=net-6.0" target="_blank">Microsoft Documentation</a> to find out more about TypeConverter.
        </MatSubtitle1>
    </MatCardContent>
</MatCard>
<br/>

<MatBody2>Id value @Bind(x => x.Id)</MatBody2>
<br/>

<MatButton OnClick="@BindingContext.NavigateToRandomId" Raised="true">Navigate to random id</MatButton>

@code {

    [Parameter]
    public string? Id { get; set; }

}

================================================
FILE: samples/BlazorSample.Components/Pages/WeatherForecasts.razor
================================================
@page "/fetchdata"

@inherits CustomBaseComponent<WeatherForecastsViewModel>

<MatCard>
    <MatCardContent>
        <MatSubtitle1>
            To use a view model with your component simply inherit from the base class
            <pre style="display: inline">MvvmComponentBase</pre> and specify your view model type for this component as a generic argument.
            Your view model will have access to all lifecycle methods you also have on your component.
            As soon as any value changes via the <pre style="display: inline">Set</pre> method on your view model the component will refresh if needed.
        </MatSubtitle1>
    </MatCardContent>
</MatCard>
<br/>

<MatCard>
    <MatCardContent>
        <MatSubtitle1>
            In list scenarios you often nest view models to achieve bindings on the list items. This is especially needed when the data of the item changes.
            You can click the randomize button and see that the list gets updated.
        </MatSubtitle1>
    </MatCardContent>
</MatCard>
<br/>

@if (Bind(x => x.Forecasts) == null)
{
    <MatProgressBar Indeterminate="true"></MatProgressBar>
}
else
{
    <MatButton Raised="true" OnClick="@BindingContext.RandomizeData">Randomize</MatButton>
    <br/>

    <MatList SingleSelection="true" TwoLine="true">
        @foreach (var forecast in Bind(x => x.Forecasts)!)
        {
            <MatListItem>
                <MatListItemText>
                    <MatListItemPrimaryText>@forecast.Date.ToShortDateString() @forecast.Summary</MatListItemPrimaryText>
                    <MatListItemSecondaryText>@Bind(forecast, x => x.TemperatureC)°C (@Bind(forecast, x => x.TemperatureF)°F)</MatListItemSecondaryText>
                </MatListItemText>
            </MatListItem>
        }
    </MatList>
}

================================================
FILE: samples/BlazorSample.Components/Shared/CascadingComponent.razor
================================================
@inherits CustomBaseComponent<CascadingViewModel>

<MatBody2>Cascading value is @Bind(x => x.Name)</MatBody2>

@code {

    [CascadingParameter]
    public string? Name { get; set; }

}

================================================
FILE: samples/BlazorSample.Components/Shared/CustomBaseComponent.cs
================================================
using Microsoft.AspNetCore.Components;
using MvvmBlazor;

namespace BlazorSample.Components.Shared;

[MvvmComponent]
public abstract partial class CustomBaseComponent<T> : CustomBaseComponent
{

}

[MvvmComponent]
public abstract partial class CustomBaseComponent : ComponentBase
{

}

================================================
FILE: samples/BlazorSample.Components/Shared/MainLayout.razor
================================================
@inherits LayoutComponentBase

<Navbar>
    @Body
</Navbar>

================================================
FILE: samples/BlazorSample.Components/Shared/Navbar.razor
================================================
@inherits MvvmComponentBase<NavbarViewModel>

<MatDrawerContainer Class="navbar">
    <MatDrawer @bind-Opened="@BindingContext.IsMenuOpen" Class="left-sidebar">
        <MatNavMenu>
            @foreach (var item in Bind(x => x.NavbarItems))
            {
                <MatNavItem Href="@item.Template" Selected="@Bind(item, x => x.IsActive)">
                    @if (!string.IsNullOrEmpty(item.Icon))
                    {
                        <MatIcon Icon="@item.Icon" class="icon"></MatIcon>
                    }

                    @item.DisplayName
                </MatNavItem>
            }
        </MatNavMenu>
    </MatDrawer>
    <MatDrawerContent>
        <MatAppBarContainer>
            <MatAppBar Fixed="true">
                <MatAppBarRow>
                    <MatAppBarSection>
                        <MatIconButton Icon="menu" OnClick="@BindingContext.ToggleMenu"></MatIconButton>
                        <MatAppBarTitle>MvvmBlazor Sample App</MatAppBarTitle>
                    </MatAppBarSection>
                </MatAppBarRow>
            </MatAppBar>
            <MatAppBarContent>
                <div class="content-container">
                    @ChildContent
                </div>
            </MatAppBarContent>
        </MatAppBarContainer>
    </MatDrawerContent>
</MatDrawerContainer>

@code
{
    [Parameter]
    public RenderFragment? ChildContent { get; set; }

}

================================================
FILE: samples/BlazorSample.Components/_Imports.razor
================================================
@using Microsoft.AspNetCore.Components.Web
@using MvvmBlazor.Components
@using BlazorSample.ViewModels
@using BlazorSample.ViewModels.Navbar
@using Microsoft.AspNetCore.Components.Routing
@using MatBlazor
@using BlazorSample.Components.Shared

================================================
FILE: samples/BlazorSample.Components/wwwroot/site.css
================================================
/* http://meyerweb.com/eric/tools/css/reset/
   v4.0 | 20180602
   License: none (public domain)
*/

html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
main, menu, nav, output, ruby, section, summary,
time, mark, audio, video {
    border: 0;
    font: inherit;
    font-size: 100%;
    margin: 0;
    padding: 0;
    vertical-align: baseline;
}

/* HTML5 display-role reset for older browsers */

article, aside, details, figcaption, figure,
footer, header, hgroup, main, menu, nav, section {
    display: block;
}

/* HTML5 hidden-attribute fix for newer browsers */

*[hidden] {
    display: none;
}

body {
    line-height: 1;
}

ol, ul {
    list-style: none;
}

blockquote, q {
    quotes: none;
}

blockquote:before, blockquote:after,
q:before, q:after {
    content: '';
    content: none;
}

table {
    border-collapse: collapse;
    border-spacing: 0;
}

html, body, app {
    height: 100%;
}

.mdc-text-field {
    width: 100% !important;
}

.mdc-card__primary-action {
    padding: 0.5rem;
}

.navbar {
    height: 100vh;
    width: 100vw;
}

.navbar .left-sidebar {
    user-select: none;
}

.navbar .icon {
    margin-right: 0.5rem;
}

.navbar .content-container {
    margin: 1rem;
}

.navbar aside:focus {
    outline: none;
}

.navbar .mdc-drawer__content:focus {
    outline: none;
}

================================================
FILE: samples/BlazorSample.Domain/BlazorSample.Domain.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <TargetFramework>net6.0</TargetFramework>
        <Nullable>enable</Nullable>
        <ImplicitUsings>true</ImplicitUsings>
        <IsPackable>false</IsPackable>
    </PropertyGroup>

    <ItemGroup>
        <PackageReference Include="Microsoft.AspNetCore.Components" Version="6.0.2"/>
        <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0"/>
    </ItemGroup>

</Project>


================================================
FILE: samples/BlazorSample.Domain/Converters/IdTypeConverter.cs
================================================
using System.ComponentModel;
using System.Globalization;
using BlazorSample.Domain.Entities;

namespace BlazorSample.Domain.Converters;

public class IdTypeConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
    {
        return sourceType == typeof(string);
    }

    public override object? ConvertTo(
        ITypeDescriptorContext? context,
        CultureInfo? culture,
        object? value,
        Type destinationType)
    {
        return value is not string s || !Guid.TryParse(s, out var guid) ? null : new IdType(guid);
    }
}

================================================
FILE: samples/BlazorSample.Domain/Entities/IdType.cs
================================================
using System.ComponentModel;
using System.Text.Json;
using BlazorSample.Domain.Converters;

namespace BlazorSample.Domain.Entities;

[TypeConverter(typeof(IdTypeConverter))]
public record IdType(Guid Value)
{
    public override string ToString()
    {
        return JsonSerializer.Serialize(this);
    }
}

================================================
FILE: samples/BlazorSample.Domain/Entities/WeatherForecastEntity.cs
================================================
namespace BlazorSample.Domain.Entities;

public class WeatherForecastEntity
{
    public DateTime Date { get; set; }

    public int TemperatureC { get; set; }

    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);

    public string? Summary { get; set; }
}

================================================
FILE: samples/BlazorSample.Domain/Extensions/ServiceCollectionExtensions.cs
================================================
using BlazorSample.Domain.Services.Navbar;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.DependencyInjection;

namespace BlazorSample.Domain.Extensions;

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddNavigationItem<TPage>(
        this IServiceCollection serviceCollection,
        string title,
        string? icon = null) where TPage : ComponentBase
    {
        serviceCollection.AddSingleton(new NavbarItem(typeof(TPage), title, icon));
        return serviceCollection;
    }

    public static IServiceCollection AddDomain(this IServiceCollection serviceCollection)
    {
        serviceCollection.AddScoped<INavbarService, NavbarService>();
        return serviceCollection;
    }
}

================================================
FILE: samples/BlazorSample.Domain/Services/IWeatherForecastGetter.cs
================================================
using BlazorSample.Domain.Entities;

namespace BlazorSample.Domain.Services;

public interface IWeatherForecastGetter
{
    Task<IEnumerable<WeatherForecastEntity>> GetForecasts();
}

================================================
FILE: samples/BlazorSample.Domain/Services/Navbar/NavbarItem.cs
================================================
namespace BlazorSample.Domain.Services.Navbar;

public class NavbarItem
{
    public string? Icon { get; }
    public Type Page { get; }
    public string DisplayName { get; }
    public string? Template { get; set; }

    public NavbarItem(Type page, string displayName, string? icon)
    {
        Page = page;
        DisplayName = displayName;
        Icon = icon;
    }
}

================================================
FILE: samples/BlazorSample.Domain/Services/Navbar/NavbarService.cs
================================================
using System.Collections.ObjectModel;
using System.Reflection;
using Microsoft.AspNetCore.Components;

namespace BlazorSample.Domain.Services.Navbar;

public interface INavbarService
{
    IReadOnlyList<NavbarItem> NavbarItems { get; }
}

public class NavbarService : INavbarService
{
    private readonly List<NavbarItem> _navbarItems = new();

    public NavbarService(IEnumerable<NavbarItem> navbarItems)
    {
        NavbarItems = new ReadOnlyCollection<NavbarItem>(_navbarItems);
        LoadComponents(navbarItems);
    }

    public IReadOnlyList<NavbarItem> NavbarItems { get; }

    private void LoadComponents(IEnumerable<NavbarItem> navbarItems)
    {
        foreach (var item in navbarItems)
        {
            if (!(item.Page.BaseType == typeof(ComponentBase) ||
                  typeof(ComponentBase).IsAssignableFrom(item.Page.BaseType)))
            {
                throw new InvalidOperationException(
                    $"NavItem {item.DisplayName}:{item.Page.FullName} is not a component"
                );
            }

            var routeAttribute = item.Page.GetCustomAttribute<RouteAttribute>();
            if (routeAttribute == null)
            {
                throw new InvalidOperationException(
                    $"NavItem {item.DisplayName}:{item.Page.FullName} has no {nameof(RouteAttribute)}"
                );
            }

            item.Template = routeAttribute.Template;
            _navbarItems.Add(item);
        }
    }
}

================================================
FILE: samples/BlazorSample.ViewModels/BlazorSample.ViewModels.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <TargetFramework>net6.0</TargetFramework>
        <Nullable>enable</Nullable>
        <ImplicitUsings>true</ImplicitUsings>
        <IsPackable>false</IsPackable>
    </PropertyGroup>

    <ItemGroup>
        <ProjectReference Include="..\..\src\MvvmBlazor.Core\MvvmBlazor.Core.csproj" />
        <ProjectReference Include="..\BlazorSample.Domain\BlazorSample.Domain.csproj" />
    </ItemGroup>

    <ItemGroup>
        <ProjectReference Include="..\..\src\MvvmBlazor.CodeGenerators\MvvmBlazor.CodeGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
    </ItemGroup>

</Project>


================================================
FILE: samples/BlazorSample.ViewModels/CascadingViewModel.cs
================================================
namespace BlazorSample.ViewModels;

public class CascadingViewModel : ViewModelBase
{
    [CascadingParameter]
    public string? Name { get; set; }
}

================================================
FILE: samples/BlazorSample.ViewModels/ClockViewModel.cs
================================================
using Timer = System.Timers.Timer;

namespace BlazorSample.ViewModels;

public partial class ClockViewModel : ViewModelBase, IDisposable
{
    private readonly Timer _timer;

    [Notify] private DateTime _dateTime = DateTime.Now;

    public ClockViewModel()
    {
        _timer = new Timer(TimeSpan.FromSeconds(1).TotalMilliseconds);
        _timer.Elapsed += TimerOnElapsed;
        _timer.Start();
    }

    public void Dispose()
    {
        GC.SuppressFinalize(this);
        Dispose(true);
    }

    private void TimerOnElapsed(object? sender, ElapsedEventArgs e)
    {
        DateTime = DateTime.Now;
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            _timer.Dispose();
        }
    }

    ~ClockViewModel()
    {
        Dispose(false);
    }
}

================================================
FILE: samples/BlazorSample.ViewModels/CounterViewModel.cs
================================================
namespace BlazorSample.ViewModels;

public partial class CounterViewModel : ViewModelBase
{
    [Notify] private int _currentCount;

    public void IncrementCount()
    {
        CurrentCount++;
    }
}

================================================
FILE: samples/BlazorSample.ViewModels/Extensions/ServiceCollectionExtensions.cs
================================================
using BlazorSample.ViewModels.Navbar;

namespace BlazorSample.ViewModels.Extensions;

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddViewModels(this IServiceCollection serviceCollection)
    {
        serviceCollection.AddTransient<WeatherForecastsViewModel>();
        serviceCollection.AddTransient<CounterViewModel>();
        serviceCollection.AddTransient<ClockViewModel>();
        serviceCollection.AddTransient<ParametersViewModel>();
        serviceCollection.AddScoped<NavbarViewModel>();
        serviceCollection.AddScoped<TypedParametersViewModel>();
        serviceCollection.AddTransient<CascadingViewModel>();

        return serviceCollection;
    }
}

================================================
FILE: samples/BlazorSample.ViewModels/GlobalUsings.cs
================================================
global using System.Collections.ObjectModel;
global using BlazorSample.Domain.Services;
global using MvvmBlazor;
global using MvvmBlazor.ViewModel;
global using System.Timers;
global using Microsoft.AspNetCore.Components;
global using Microsoft.Extensions.DependencyInjection;
global using BlazorSample.Domain.Entities;

================================================
FILE: samples/BlazorSample.ViewModels/Navbar/NavbarItemViewModel.cs
================================================
namespace BlazorSample.ViewModels.Navbar;

public class NavbarItemViewModel : ViewModelBase
{
    private bool _isActive;

    public string DisplayName { get; }

    public string Template { get; }

    public string? Icon { get; set; }

    public bool IsActive
    {
        get => _isActive;
        set => Set(ref _isActive, value, nameof(IsActive));
    }

    public NavbarItemViewModel(string displayName, string template, string? icon)
    {
        DisplayName = displayName;
        Template = template;
        Icon = icon;
    }
}

================================================
FILE: samples/BlazorSample.ViewModels/Navbar/NavbarViewModel.cs
================================================
using BlazorSample.Domain.Services.Navbar;

namespace BlazorSample.ViewModels.Navbar;

public class NavbarViewModel : ViewModelBase
{
    private bool _isMenuOpen = true;

    public ObservableCollection<NavbarItemViewModel> NavbarItems { get; }

    public bool IsMenuOpen
    {
        get => _isMenuOpen;
        set => Set(ref _isMenuOpen, value, nameof(IsMenuOpen));
    }

    public NavbarViewModel(INavbarService navbarService)
    {
        NavbarItems = new ObservableCollection<NavbarItemViewModel>(
            navbarService.NavbarItems.Select(x => new NavbarItemViewModel(x.DisplayName, x.Template!, x.Icon))
        );
    }

    public void ToggleMenu()
    {
        IsMenuOpen = !IsMenuOpen;
    }

    private void UpdateActiveItem(NavigationManager navigationManager)
    {
        var relativePath = navigationManager.ToBaseRelativePath(navigationManager.Uri);

        foreach (var navbarItem in NavbarItems)
            if (string.IsNullOrEmpty(relativePath))
            {
                navbarItem.IsActive = navbarItem.Template == "/";
            }
            else
            {
                navbarItem.IsActive = navbarItem.Template.StartsWith("/" + relativePath);
            }
    }

    public override void OnInitialized()
    {
        var navigationManager = RootServiceProvider.GetRequiredService<NavigationManager>();
        navigationManager.LocationChanged += (_, _) => UpdateActiveItem(navigationManager);

        UpdateActiveItem(navigationManager);
    }
}

================================================
FILE: samples/BlazorSample.ViewModels/ParametersViewModel.cs
================================================
namespace BlazorSample.ViewModels;

public class ParametersViewModel : ViewModelBase
{
    private NavigationManager _navigationManager = null!;

    [Parameter] public string? Name { get; set; }

    public string? NewName { get; set; }

    public void NavigateToNewName()
    {
        if (string.IsNullOrEmpty(NewName))
        {
            return;
        }

        _navigationManager.NavigateTo($"/parameters/{NewName}");
    }

    public override void OnInitialized()
    {
        _navigationManager = RootServiceProvider.GetRequiredService<NavigationManager>();
    }
}

================================================
FILE: samples/BlazorSample.ViewModels/TypedParametersViewModel.cs
================================================
namespace BlazorSample.ViewModels;

public class TypedParametersViewModel : ViewModelBase
{
    private NavigationManager _navigationManager = null!;

    [Parameter] public IdType? Id { get; set; }

    public void NavigateToRandomId()
    {
        _navigationManager.NavigateTo($"/typed-parameters/{Guid.NewGuid()}");
    }

    public override void OnInitialized()
    {
        _navigationManager = RootServiceProvider.GetRequiredService<NavigationManager>();
    }

    public override void OnParametersSet()
    {
        if (Id is null)
        {
            NavigateToRandomId();
        }
    }
}

================================================
FILE: samples/BlazorSample.ViewModels/WeatherForecastViewModel.cs
================================================
namespace BlazorSample.ViewModels;

public class WeatherForecastViewModel : ViewModelBase
{
    private readonly WeatherForecastEntity _weatherForecastEntity;

    private int _temperatureC;

    private int _temperatureF;

    public DateTime Date => _weatherForecastEntity.Date;
    public string? Summary => _weatherForecastEntity.Summary;

    public int TemperatureC
    {
        get => _temperatureC;
        set => Set(ref _temperatureC, value, nameof(TemperatureC));
    }

    public int TemperatureF
    {
        get => _temperatureF;
        set => Set(ref _temperatureF, value, nameof(TemperatureF));
    }

    public WeatherForecastViewModel(WeatherForecastEntity weatherForecastEntity)
    {
        _weatherForecastEntity = weatherForecastEntity;
        TemperatureC = _weatherForecastEntity.TemperatureC;
        TemperatureF = _weatherForecastEntity.TemperatureF;
    }
}

================================================
FILE: samples/BlazorSample.ViewModels/WeatherForecastsViewModel.cs
================================================
namespace BlazorSample.ViewModels;

public partial class WeatherForecastsViewModel : ViewModelBase
{
    private readonly IWeatherForecastGetter _weatherForecastGetter;

    [Notify] private ObservableCollection<WeatherForecastViewModel>? _forecasts;

    public WeatherForecastsViewModel(IWeatherForecastGetter weatherForecastGetter)
    {
        _weatherForecastGetter = weatherForecastGetter;
    }

    public override async Task OnInitializedAsync()
    {
        // Simulate loading time
        await Task.Delay(1500);

        var forecastData = await _weatherForecastGetter.GetForecasts();
        Forecasts = new ObservableCollection<WeatherForecastViewModel>(
            forecastData.Select(x => new WeatherForecastViewModel(x))
        );
    }

    public void RandomizeData()
    {
        var random = new Random();

        foreach (var weatherForecastEntity in _forecasts!)
        {
            weatherForecastEntity.TemperatureC = random.Next(10, 40);
            weatherForecastEntity.TemperatureF = random.Next(50, 200);
        }
    }
}

================================================
FILE: samples/BlazorServersideSample/App.razor
================================================
<Router AppAssembly="@typeof(BlazorSample.Components._Imports).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/>
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(MainLayout)">
            <p>Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>

================================================
FILE: samples/BlazorServersideSample/BlazorServersideSample.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk.Web">

    <PropertyGroup>
        <TargetFramework>net6.0</TargetFramework>
        <Nullable>enable</Nullable>
        <ImplicitUsings>true</ImplicitUsings>
        <IsPackable>false</IsPackable>
    </PropertyGroup>

    <ItemGroup>
        <ProjectReference Include="..\..\src\MvvmBlazor.Core\MvvmBlazor.Core.csproj"/>
        <ProjectReference Include="..\BlazorSample.Components\BlazorSample.Components.csproj"/>
    </ItemGroup>

</Project>


================================================
FILE: samples/BlazorServersideSample/Pages/Error.razor
================================================
@page "/error"


<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>

<h3>Development Mode</h3>
<p>
    Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
</p>
<p>
    <strong>The Development environment shouldn't be enabled for deployed applications.</strong>
    It can result in displaying sensitive information from exceptions to end users.
    For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
    and restarting the app.
</p>

================================================
FILE: samples/BlazorServersideSample/Pages/_Host.cshtml
================================================
@page "/"
@namespace BlazorServersideSample.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>BlazorServersideSample</title>
    <base href="~/"/>
    <link href="_content/BlazorSample.Components/site.css" rel="stylesheet"/>
    <script src="_content/MatBlazor/dist/matBlazor.js"></script>
    <link href="_content/MatBlazor/dist/matBlazor.css" rel="stylesheet"/>
</head>
<body>
<app>
    @(await Html.RenderComponentAsync<App>(RenderMode.ServerPrerendered))
</app>

<script src="_framework/blazor.server.js"></script>
</body>
</html>

================================================
FILE: samples/BlazorServersideSample/Pages/_Imports.razor
================================================
@inherits MvvmComponentBase

================================================
FILE: samples/BlazorServersideSample/Program.cs
================================================
namespace BlazorServersideSample;

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args)
    {
        return Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
    }
}

================================================
FILE: samples/BlazorServersideSample/Properties/launchSettings.json
================================================
{
    "profiles": {
        "BlazorServersideSample": {
            "commandName": "Project",
            "launchBrowser": true,
            "applicationUrl": "https://localhost:5001;http://localhost:5000",
            "environmentVariables": {
                "ASPNETCORE_ENVIRONMENT": "Development"
            }
        }
    }
}

================================================
FILE: samples/BlazorServersideSample/Services/WeatherForecastGetter.cs
================================================
using BlazorSample.Domain.Entities;
using BlazorSample.Domain.Services;

namespace BlazorServersideSample.Services;

public class WeatherForecastGetter : IWeatherForecastGetter
{
    private static readonly string[] Summaries =
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    public Task<IEnumerable<WeatherForecastEntity>> GetForecasts()
    {
        var rng = new Random();
        return Task.FromResult(
            Enumerable.Range(1, 5)
                .Select(
                    index => new WeatherForecastEntity
                    {
                        Date = DateTime.Now.AddDays(index),
                        TemperatureC = rng.Next(-20, 55),
                        Summary = Summaries[rng.Next(Summaries.Length)]
                    }
                )
        );
    }
}

================================================
FILE: samples/BlazorServersideSample/Startup.cs
================================================
using BlazorSample.Components.Extensions;
using BlazorSample.Domain.Extensions;
using BlazorSample.Domain.Services;
using BlazorSample.ViewModels.Extensions;
using BlazorServersideSample.Services;

namespace BlazorServersideSample;

public class Startup
{
    public IConfiguration Configuration { get; }

    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddRazorPages();
        services.AddServerSideBlazor();

        // Add mvvm to server
        services.AddMvvm();

        services.AddDomain().AddComponents().AddViewModels();
        services.AddSingleton<IWeatherForecastGetter, WeatherForecastGetter>();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseEndpoints(
            endpoints =>
            {
                endpoints.MapBlazorHub();
                endpoints.MapFallbackToPage("/_Host");
            }
        );
    }
}

================================================
FILE: samples/BlazorServersideSample/_Imports.razor
================================================
@using System.Net.Http
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components
@using Microsoft.JSInterop
@using BlazorServersideSample
@using MvvmBlazor.Components
@using BlazorSample.Components.Shared

================================================
FILE: samples/BlazorServersideSample/appsettings.Development.json
================================================
{
    "Logging": {
        "LogLevel": {
            "Default": "Debug",
            "System": "Information",
            "Microsoft": "Information"
        }
    }
}

================================================
FILE: samples/BlazorServersideSample/appsettings.json
================================================
{
    "Logging": {
        "LogLevel": {
            "Default": "Information",
            "Microsoft": "Warning",
            "Microsoft.Hosting.Lifetime": "Information"
        }
    },
    "AllowedHosts": "*"
}

================================================
FILE: src/.editorconfig
================================================
[*.{c,c++,cc,cp,cpp,cu,cuh,cxx,h,hh,hpp,hxx,inc,inl,ino,ipp,mpp,proto,tpp}]
indent_style = tab
indent_size = tab
tab_width = 4

[*.{asax,ascx,aspx,cs,cshtml,css,htm,html,js,jsx,master,razor,skin,ts,tsx,vb,xaml,xamlx,xoml}]
indent_style = space
indent_size = 4
tab_width = 4

[*.{appxmanifest,build,config,csproj,dbml,discomap,dtd,json,jsproj,lsproj,njsproj,nuspec,proj,props,resjson,resw,resx,StyleCop,targets,tasks,vbproj,xml,xsd}]
indent_style = space
indent_size = 2
tab_width = 2

[*]

# Microsoft .NET properties
csharp_new_line_before_members_in_object_initializers = false
csharp_preferred_modifier_order = public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion
csharp_style_var_elsewhere = true:hint
csharp_style_var_for_built_in_types = true:hint
csharp_style_var_when_type_is_apparent = true:hint
dotnet_style_predefined_type_for_locals_parameters_members = true:hint
dotnet_style_predefined_type_for_member_access = true:hint
dotnet_style_qualification_for_event = false:warning
dotnet_style_qualification_for_field = false:warning
dotnet_style_qualification_for_method = false:warning
dotnet_style_qualification_for_property = false:warning
dotnet_style_require_accessibility_modifiers = for_non_interface_members:hint

# ReSharper properties
resharper_csharp_empty_block_style = together_same_line


================================================
FILE: src/Directory.Build.props
================================================
<Project>
  <PropertyGroup>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <AnalysisMode>All</AnalysisMode>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="IDisposableAnalyzers" Version="4.0.2">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>
</Project>

================================================
FILE: src/MvvmBlazor/MvvmBlazor.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
    <CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GeneratedFiles</CompilerGeneratedFilesOutputPath>
  </PropertyGroup>

  <PropertyGroup Condition="'$(CI)' == 'true'">
    <Deterministic>true</Deterministic>
    <PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
    <PackageLicenseExpression>MIT</PackageLicenseExpression>
    <Authors>Christian Klemm</Authors>
    <Description>A lightweight Blazor Mvvm Library</Description>
    <PackageProjectUrl>$(GITHUB_SERVER_URL)/$(GITHUB_REPOSITORY)</PackageProjectUrl>
    <RepositoryUrl>$(GITHUB_SERVER_URL)/$(GITHUB_REPOSITORY)</RepositoryUrl>
    <RepositoryType>git</RepositoryType>
    <PackageTags>Blazor;Mvvm</PackageTags>
    <PublishRepositoryUrl>true</PublishRepositoryUrl>
    <IncludeSymbols>true</IncludeSymbols>
    <EmbedUntrackedSources>true</EmbedUntrackedSources>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\MvvmBlazor.CodeGenerators\MvvmBlazor.CodeGenerators.csproj"/>
    <ProjectReference Include="..\MvvmBlazor.Core\MvvmBlazor.Core.csproj"/>
  </ItemGroup>
  
  <ItemGroup>
    <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All"/>
  </ItemGroup>

</Project>


================================================
FILE: src/MvvmBlazor/Properties/AssemblyInfo.cs
================================================
[assembly: CLSCompliant(false)]

================================================
FILE: src/MvvmBlazor.CodeGenerators/AnalyzerReleases.Shipped.md
================================================
## Release 6.0.0
### New Rules

Rule ID | Category | Severity | Notes
--------|----------|----------|-------
MVVMBLAZOR001 | MvvmBlazorGenerator | Error | MVVMBLAZOR001_AnalyzerName
MVVMBLAZOR002 | MvvmBlazorGenerator | Error | MVVMBLAZOR002_AnalyzerName
MVVMBLAZOR003 | MvvmBlazorGenerator | Error | MVVMBLAZOR003_AnalyzerName
MVVMBLAZOR004 | MvvmBlazorGenerator | Error | MVVMBLAZOR004_AnalyzerName

## Release 6.0.2
### New Rules

Rule ID | Category | Severity | Notes
--------|----------|----------|-------
MVVMBLAZOR005 | MvvmBlazorGenerator | Error | MVVMBLAZOR005_AnalyzerName

================================================
FILE: src/MvvmBlazor.CodeGenerators/AnalyzerReleases.Unshipped.md
================================================


================================================
FILE: src/MvvmBlazor.CodeGenerators/Components/MvvmComponentClassContext.cs
================================================
namespace MvvmBlazor.CodeGenerators.Components;

internal class MvvmComponentClassContext
{
    public ClassDeclarationSyntax ComponentClass { get; }
    public INamedTypeSymbol ComponentSymbol { get; }

    public MvvmComponentClassContext(ClassDeclarationSyntax componentClass, INamedTypeSymbol componentSymbol)
    {
        ComponentClass = componentClass;
        ComponentSymbol = componentSymbol;
    }
}

================================================
FILE: src/MvvmBlazor.CodeGenerators/Components/MvvmComponentGenerator.cs
================================================
namespace MvvmBlazor.CodeGenerators.Components;

[Generator]
public class MvvmComponentGenerator : ISourceGenerator
{
    private static readonly DiagnosticDescriptor ComponentNotPartialError = new(
        "MVVMBLAZOR001",
        "Component needs to be partial",
        "Mvvm Component class '{0}' needs to be partial",
        "MvvmBlazorGenerator",
        DiagnosticSeverity.Error,
        true
    );

    private static readonly DiagnosticDescriptor ComponentWrongBaseClassError = new(
        "MVVMBLAZOR002",
        "Missing component base class",
        "Mvvm Component class '{0}' needs to be assignable to '{1}'",
        "MvvmBlazorGenerator",
        DiagnosticSeverity.Error,
        true
    );

    private static readonly DiagnosticDescriptor ComponentWrongTypeParameterError = new(
        "MVVMBLAZOR005",
        "Wrong type parameter",
        "Mvvm Component class '{0}' needs to have exactly one type parameter named '{1}'",
        "MvvmBlazorGenerator",
        DiagnosticSeverity.Error,
        true
    );


    public void Execute(GeneratorExecutionContext context)
    {
        if (context.SyntaxContextReceiver is not MvvmComponentSyntaxReceiver syntaxReceiver ||
            syntaxReceiver.ComponentClassContexts.Count == 0)
        {
            return;
        }

        foreach (var componentClassContext in syntaxReceiver.ComponentClassContexts)
            ProcessComponent(context, componentClassContext);
    }

    public void Initialize(GeneratorInitializationContext context)
    {
        context.RegisterForSyntaxNotifications(() => new MvvmComponentSyntaxReceiver());
    }

    private static void ProcessComponent(
        GeneratorExecutionContext context,
        MvvmComponentClassContext componentClassContext)
    {
        var componentClass = componentClassContext.ComponentClass;
        var isPartial = componentClass.Modifiers.Any(SyntaxKind.PartialKeyword);
        if (!isPartial)
        {
            context.ReportDiagnostic(
                Diagnostic.Create(
                    ComponentNotPartialError,
                    Location.Create(
                        componentClass.SyntaxTree,
                        TextSpan.FromBounds(componentClass.SpanStart, componentClass.SpanStart)
                    ),
                    componentClass.Identifier
                )
            );
            return;
        }

        var componentBaseType =
            context.Compilation.GetTypeByMetadataName("Microsoft.AspNetCore.Components.ComponentBase")!;
        if (!componentClassContext.ComponentSymbol.InheritsFrom(componentBaseType))
        {
            context.ReportDiagnostic(
                Diagnostic.Create(
                    ComponentWrongBaseClassError,
                    Location.Create(
                        componentClass.SyntaxTree,
                        TextSpan.FromBounds(componentClass.SpanStart, componentClass.SpanStart)
                    ),
                    componentClass.Identifier,
                    componentBaseType.GetMetadataName()
                )
            );
            return;
        }

        if (componentClass.TypeParameterList is null || componentClass.TypeParameterList.Parameters.Count == 0)
        {
            AddComponent(context, componentClassContext, componentClass);
            return;
        }

        AddGenericComponent(context, componentClassContext, componentClass);
    }

    private static void AddComponent(
        GeneratorExecutionContext context,
        MvvmComponentClassContext componentClassContext,
        BaseTypeDeclarationSyntax componentClass)
    {
        var componentSourceText = SourceText.From(GenerateComponentCode(componentClassContext), Encoding.UTF8);
        context.AddSource(componentClass.Identifier + ".g.cs", componentSourceText);
    }

    private static void AddGenericComponent(
        GeneratorExecutionContext context,
        MvvmComponentClassContext componentClassContext,
        TypeDeclarationSyntax componentClass)
    {
        const string typeParameterName = "T";
        var genericComponentSourceText = SourceText.From(
            GenerateGenericComponentCode(componentClassContext, typeParameterName),
            Encoding.UTF8
        );

        var typeParameterList = componentClass.TypeParameterList;
        if (typeParameterList is null || typeParameterList.Parameters.Count != 1 || typeParameterList.Parameters[0].Identifier.ValueText != typeParameterName)
        {
            context.ReportDiagnostic(
                Diagnostic.Create(
                    ComponentWrongTypeParameterError,
                    Location.Create(
                        componentClass.SyntaxTree,
                        TextSpan.FromBounds(componentClass.SpanStart, componentClass.SpanStart)
                    ),
                    componentClass.Identifier,
                    typeParameterName
                )
            );
        }

        context.AddSource(componentClass.Identifier + "T.g.cs", genericComponentSourceText);
    }

    private static string GenerateComponentCode(MvvmComponentClassContext componentClassContext)
    {
        var componentNamespace = componentClassContext.ComponentSymbol.ContainingNamespace;
        var componentClassName = componentClassContext.ComponentClass.Identifier;

        return $@"
#nullable enable
using System;
using System.Linq.Expressions;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.DependencyInjection;
using MvvmBlazor.Components;
using MvvmBlazor.ViewModel;

namespace {componentNamespace}
{{
    partial class {componentClassName} : IDisposable, IAsyncDisposable
    {{
        private AsyncServiceScope? _scope;

        [Inject] IServiceScopeFactory ScopeFactory {{ get; set; }} = default!;
        [Inject] protected IServiceProvider RootServiceProvider {{ get; set; }} = default!;
        protected bool IsDisposed {{ get; private set; }}

        public MvvmBlazor.Internal.Bindings.IBinder Binder {{ get; private set; }} = null!;

#pragma warning disable CS0109
        protected new IServiceProvider ScopedServices
#pragma warning restore CS0109
        {{
            get
            {{
                if (ScopeFactory == null)
                {{
                    throw new InvalidOperationException(""Services cannot be accessed before the component is initialized."");
                }}

                if (IsDisposed)
                {{
                    throw new ObjectDisposedException(GetType().Name);
                }}

                _scope ??= ScopeFactory.CreateAsyncScope();
                return _scope.Value.ServiceProvider;
            }}
        }}

#pragma warning disable CS8618
        protected internal {componentClassName}(IServiceProvider services)
#pragma warning restore CS8618
        {{
            RootServiceProvider = services;
            ScopeFactory = services.GetRequiredService<IServiceScopeFactory>();
            InitializeDependencies();
        }}

#pragma warning disable CS8618
        protected {componentClassName}()
#pragma warning restore CS8618
        {{
        }}

        private void InitializeDependencies()
        {{
            Binder = ScopedServices.GetRequiredService<MvvmBlazor.Internal.Bindings.IBinder>();
            Binder.ValueChangedCallback = BindingOnBindingValueChanged;
        }}

        protected internal TValue Bind<TViewModel, TValue>(TViewModel viewModel,
            Expression<Func<TViewModel, TValue>> property)
            where TViewModel : ViewModelBase
        {{
            return AddBinding(viewModel, property);
        }}

        public virtual TValue AddBinding<TViewModel, TValue>(TViewModel viewModel,
            Expression<Func<TViewModel, TValue>> propertyExpression) where TViewModel : ViewModelBase
        {{
            return Binder.Bind(viewModel, propertyExpression);
        }}

        protected override void OnInitialized()
        {{
            base.OnInitialized();
            InitializeDependencies();
        }}

        internal virtual void BindingOnBindingValueChanged(object sender, EventArgs e)
        {{
            InvokeAsync(StateHasChanged);
        }}

        public void Dispose()
        {{
            if (!IsDisposed)
            {{
                _scope?.Dispose();
                _scope = null;
                Dispose(true);
                GC.SuppressFinalize(this);
                IsDisposed = true;
            }}
        }}

        protected virtual void Dispose(bool disposing)
        {{
        }}

        public async ValueTask DisposeAsync()
        {{
            if (!IsDisposed)
            {{
                if (_scope is not null)
                {{
                    await _scope.Value.DisposeAsync();
                    _scope = null;
                }}

                await DisposeAsyncCore();
                Dispose(false);
                GC.SuppressFinalize(this);
                IsDisposed = true;
            }}
        }}

        protected virtual ValueTask DisposeAsyncCore()
        {{
            return ValueTask.CompletedTask;
        }}
    }}
}}
            ";
    }

    private static string GenerateGenericComponentCode(MvvmComponentClassContext componentClassContext, string typeParameterName)
    {
        var componentNamespace = componentClassContext.ComponentSymbol.ContainingNamespace;
        var componentClassName = componentClassContext.ComponentClass.Identifier;

        return $@"
#nullable enable
using System;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.DependencyInjection;
using MvvmBlazor.Internal.Parameters;
using MvvmBlazor.ViewModel;

namespace {componentNamespace}
{{
    partial class {componentClassName}<{typeParameterName}>
        where T : ViewModelBase
    {{
        private MvvmBlazor.Internal.Parameters.IViewModelParameterSetter? _viewModelParameterSetter;

#pragma warning disable CS8618
        protected internal {componentClassName}(IServiceProvider serviceProvider) : base(serviceProvider)
#pragma warning restore CS8618
        {{
            SetBindingContext();
        }}

#pragma warning disable CS8618
        protected {componentClassName}()
#pragma warning restore CS8618
        {{
        }}

        protected T BindingContext {{ get; set; }}

        private void SetBindingContext()
        {{
            BindingContext ??= ScopedServices.GetRequiredService<T>();
            BindingContext.RootServiceProvider = RootServiceProvider;
        }}

        private void SetParameters()
        {{
            if (IsDisposed)
                return;

            if (BindingContext is null)
                throw new InvalidOperationException($""{{nameof(BindingContext)}} is not set"");

            _viewModelParameterSetter ??= ScopedServices.GetRequiredService<MvvmBlazor.Internal.Parameters.IViewModelParameterSetter>();
            _viewModelParameterSetter.ResolveAndSet(this, BindingContext);
        }}

        protected internal TValue Bind<TValue>(Expression<Func<T, TValue>> property)
        {{
            if (BindingContext is null)
                throw new InvalidOperationException($""{{nameof(BindingContext)}} is not set"");

            return AddBinding(BindingContext, property);
        }}

        /// <inheritdoc />
        protected override void OnInitialized()
        {{
            SetBindingContext();
            base.OnInitialized();
            BindingContext?.OnInitialized();
        }}

        /// <inheritdoc />
        protected override async Task OnInitializedAsync()
        {{
            await base.OnInitializedAsync();
            await BindingContext!.OnInitializedAsync();
        }}

        /// <inheritdoc />
        protected override void OnParametersSet()
        {{
            SetParameters();
            base.OnParametersSet();
            BindingContext?.OnParametersSet();
        }}

        /// <inheritdoc />
        protected override async Task OnParametersSetAsync()
        {{
            await base.OnParametersSetAsync();
            await BindingContext.OnParametersSetAsync();
        }}

        /// <inheritdoc />
        protected override bool ShouldRender()
        {{
            return BindingContext!.ShouldRender();
        }}

        /// <inheritdoc />
        protected override void OnAfterRender(bool firstRender)
        {{
            base.OnAfterRender(firstRender);
            BindingContext!.OnAfterRender(firstRender);
        }}

        /// <inheritdoc />
        protected override async Task OnAfterRenderAsync(bool firstRender)
        {{
            await base.OnAfterRenderAsync(firstRender);
            await BindingContext!.OnAfterRenderAsync(firstRender);
        }}

        /// <inheritdoc />
        public override async Task SetParametersAsync(ParameterView parameters)
        {{
            await base.SetParametersAsync(parameters);

            if (BindingContext != null)
            {{
                await BindingContext.SetParametersAsync(parameters);
            }}
        }}
    }}
}}
            ";
    }
}


================================================
FILE: src/MvvmBlazor.CodeGenerators/Components/MvvmComponentSyntaxReceiver.cs
================================================
namespace MvvmBlazor.CodeGenerators.Components;

internal class MvvmComponentSyntaxReceiver : ISyntaxContextReceiver
{
    public List<MvvmComponentClassContext> ComponentClassContexts { get; } = new();

    public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
    {
        if (context.Node is not ClassDeclarationSyntax cds || cds.AttributeLists.Count == 0)
        {
            return;
        }

        var symbol = context.SemanticModel.GetDeclaredSymbol(context.Node);
        if (symbol is not INamedTypeSymbol typeSymbol)
        {
            return;
        }

        var isDecoratedWithAttribute = typeSymbol.GetAttributes()
            .Any(ad => ad.AttributeClass is { Name: "MvvmComponentAttribute" });
        if (!isDecoratedWithAttribute)
        {
            return;
        }

        ComponentClassContexts.Add(new MvvmComponentClassContext(cds, typeSymbol));
    }
}

================================================
FILE: src/MvvmBlazor.CodeGenerators/Extensions/StringBuilderExtensions.cs
================================================
using System.Globalization;

namespace MvvmBlazor.CodeGenerators.Extensions;

internal static class StringBuilderExtensions
{
    public static void AppendLineFormat(this StringBuilder stringBuilder, string format, object arg0)
    {
        stringBuilder.AppendFormat(CultureInfo.InvariantCulture, format, arg0);
        stringBuilder.AppendLine();
    }

    public static void AppendLineFormat(this StringBuilder stringBuilder, string format, object arg0, object arg1)
    {
        stringBuilder.AppendFormat(CultureInfo.InvariantCulture, format, arg0, arg1);
        stringBuilder.AppendLine();
    }
}

================================================
FILE: src/MvvmBlazor.CodeGenerators/Extensions/SymbolExtensions.cs
================================================
namespace MvvmBlazor.CodeGenerators.Extensions;

internal static class SymbolExtensions
{
    public static string GetMetadataName(this ISymbol symbol)
    {
        return string.Join(".", symbol.ContainingNamespace, symbol.MetadataName);
    }

    public static bool InheritsFrom(this ITypeSymbol symbol, ISymbol baseType)
    {
        if (symbol.BaseType is null)
        {
            return false;
        }

        if (symbol.BaseType.GetMetadataName() == baseType.GetMetadataName())
        {
            return true;
        }

        return symbol.BaseType.InheritsFrom(baseType);
    }
}

================================================
FILE: src/MvvmBlazor.CodeGenerators/GlobalUsings.cs
================================================
global using System.Text;
global using Microsoft.CodeAnalysis;
global using Microsoft.CodeAnalysis.CSharp;
global using Microsoft.CodeAnalysis.CSharp.Syntax;
global using Microsoft.CodeAnalysis.Text;
global using MvvmBlazor.CodeGenerators.Extensions;
global using System.Collections.Generic;
global using System.Linq;

================================================
FILE: src/MvvmBlazor.CodeGenerators/MvvmBlazor.CodeGenerators.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <LangVersion>10.0</LangVersion>
    <IncludeBuildOutput>false</IncludeBuildOutput>
    <IncludeSymbols>false</IncludeSymbols>
  </PropertyGroup>

  <PropertyGroup Condition="'$(CI)' == 'true'">
    <PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
    <PackageLicenseExpression>MIT</PackageLicenseExpression>
    <Authors>Christian Klemm</Authors>
    <Description>Code generators for a lightweight Blazor Mvvm Library</Description>
    <PackageProjectUrl>$(GITHUB_SERVER_URL)/$(GITHUB_REPOSITORY)</PackageProjectUrl>
    <RepositoryUrl>$(GITHUB_SERVER_URL)/$(GITHUB_REPOSITORY)</RepositoryUrl>
    <RepositoryType>git</RepositoryType>
    <PackageTags>Blazor;Mvvm</PackageTags>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1"/>
  </ItemGroup>

  <ItemGroup>
    <!-- Package the generator in the analyzer directory of the nuget package -->
    <None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false"/>
  </ItemGroup>
</Project>


================================================
FILE: src/MvvmBlazor.CodeGenerators/NotifyPropertyChanged/NotifyPropertyChangedContext.cs
================================================
namespace MvvmBlazor.CodeGenerators.NotifyPropertyChanged;

internal class NotifyPropertyChangedContext
{
    public FieldDeclarationSyntax Field { get; }
    public IFieldSymbol FieldSymbol { get; }

    public NotifyPropertyChangedContext(FieldDeclarationSyntax field, IFieldSymbol fieldSymbol)
    {
        Field = field;
        FieldSymbol = fieldSymbol;
    }
}

================================================
FILE: src/MvvmBlazor.CodeGenerators/NotifyPropertyChanged/NotifyPropertyChangedGenerator.cs
================================================
namespace MvvmBlazor.CodeGenerators.NotifyPropertyChanged;

[Generator]
public class NotifyPropertyChangedGenerator : ISourceGenerator
{
    private static readonly DiagnosticDescriptor ViewModelNotPartialError = new(
        "MVVMBLAZOR003",
        "View model needs to be partial",
        "View model class '{0}' needs to be partial",
        "MvvmBlazorGenerator",
        DiagnosticSeverity.Error,
        true
    );

    private static readonly DiagnosticDescriptor ViewModelMissingBaseClass = new(
        "MVVMBLAZOR004",
        "Missing view model base class",
        "View model class '{0}' needs to be assignable to '{1}'",
        "MvvmBlazorGenerator",
        DiagnosticSeverity.Error,
        true
    );

    public void Initialize(GeneratorInitializationContext context)
    {
        context.RegisterForSyntaxNotifications(() => new NotifyPropertyChangedSyntaxReceiver());
    }

    public void Execute(GeneratorExecutionContext context)
    {
        if (context.SyntaxContextReceiver is not NotifyPropertyChangedSyntaxReceiver syntaxReceiver ||
            syntaxReceiver.Contexts.Count == 0)
        {
            return;
        }

        foreach (var fieldContexts in syntaxReceiver.Contexts) ProcessViewModel(context, fieldContexts.Value);
    }

    private static void ProcessViewModel(
        GeneratorExecutionContext context,
        IReadOnlyCollection<NotifyPropertyChangedContext> fieldContexts)
    {
        var viewModelClass = fieldContexts.First().Field.Ancestors().OfType<ClassDeclarationSyntax>().First();
        var isPartial = viewModelClass.Modifiers.Any(SyntaxKind.PartialKeyword);
        if (!isPartial)
        {
            context.ReportDiagnostic(
                Diagnostic.Create(
                    ViewModelNotPartialError,
                    Location.Create(
                        viewModelClass.SyntaxTree,
                        TextSpan.FromBounds(viewModelClass.SpanStart, viewModelClass.SpanStart)
                    ),
                    viewModelClass.Identifier
                )
            );
            return;
        }

        var viewModelType = fieldContexts.First().FieldSymbol.ContainingType;
        var viewModelBaseType = context.Compilation.GetTypeByMetadataName("MvvmBlazor.ViewModel.ViewModelBase")!;
        if (!viewModelType.InheritsFrom(viewModelBaseType))
        {
            context.ReportDiagnostic(
                Diagnostic.Create(
                    ViewModelMissingBaseClass,
                    Location.Create(
                        viewModelClass.SyntaxTree,
                        TextSpan.FromBounds(viewModelClass.SpanStart, viewModelClass.SpanStart)
                    ),
                    viewModelClass.Identifier
                )
            );
            return;
        }

        AddViewModel(context, fieldContexts, viewModelType, viewModelClass);
    }

    private static void AddViewModel(
        GeneratorExecutionContext context,
        IReadOnlyCollection<NotifyPropertyChangedContext> fieldContexts,
        INamedTypeSymbol viewModelType,
        ClassDeclarationSyntax viewModelClass)
    {
        var viewModelSourceText = SourceText.From(
            GenerateViewModelCode(fieldContexts, viewModelType, viewModelClass),
            Encoding.UTF8
        );
        context.AddSource(viewModelClass.Identifier + ".Generated.cs", viewModelSourceText);
    }

    private static string GenerateViewModelCode(
        IReadOnlyCollection<NotifyPropertyChangedContext> fieldContexts,
        INamedTypeSymbol viewModelType,
        ClassDeclarationSyntax viewModelClass)
    {
        var viewModelNamespace = viewModelType.ContainingNamespace;
        var viewModelClassName = viewModelClass.Identifier;

        var genericArgumentString = string.Empty;
        var genericArguments = viewModelType.TypeArguments.Select(x => x.Name).ToList();
        if (genericArguments.Count > 0)
        {
            genericArgumentString = "<" + string.Join(", ", genericArguments) + ">";
        }

        var sb = new StringBuilder();

        sb.AppendLine("#nullable enable");
        sb.AppendLine();
        sb.AppendLineFormat("namespace {0}", viewModelNamespace);
        sb.AppendLine("{");
        sb.AppendLineFormat("    partial class {0}{1}", viewModelClassName, genericArgumentString);
        sb.AppendLine("    {");

        foreach (var fieldContext in fieldContexts)
        {
            var propertyType = fieldContext.FieldSymbol.Type;
            var fieldName = fieldContext.FieldSymbol.Name;
            var propertyName = GetPropertyName(fieldName);
            sb.AppendLineFormat("        public {0} {1}", propertyType, propertyName);
            sb.AppendLine("        {");
            sb.AppendLineFormat("            get => {0};", fieldName);
            sb.AppendLineFormat("            set => Set(ref {0}, value, \"{1}\");", fieldName, propertyName);
            sb.AppendLine("        }");
        }

        sb.AppendLine("    }");
        sb.AppendLine("}");

        return sb.ToString();
    }

    private static string GetPropertyName(string fieldName)
    {
        var fieldNameWithoutUnderscore = fieldName.TrimStart('_');
        var firstChar = fieldNameWithoutUnderscore.Substring(0, 1);

        return firstChar.ToUpperInvariant() + fieldNameWithoutUnderscore.Substring(1);
    }
}

================================================
FILE: src/MvvmBlazor.CodeGenerators/NotifyPropertyChanged/NotifyPropertyChangedSyntaxReceiver.cs
================================================
namespace MvvmBlazor.CodeGenerators.NotifyPropertyChanged;

internal class NotifyPropertyChangedSyntaxReceiver : ISyntaxContextReceiver
{
    public Dictionary<string, List<NotifyPropertyChangedContext>> Contexts { get; } = new();

    public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
    {
        if (context.Node is not FieldDeclarationSyntax fds || fds.AttributeLists.Count == 0)
        {
            return;
        }

        foreach (var variable in fds.Declaration.Variables)
        {
            var symbol = context.SemanticModel.GetDeclaredSymbol(variable);
            if (symbol is not IFieldSymbol fieldSymbol)
            {
                continue;
            }

            var isDecoratedWithAttribute = fieldSymbol.GetAttributes()
                .Any(ad => ad.AttributeClass is { Name: "NotifyAttribute" });
            if (!isDecoratedWithAttribute)
            {
                return;
            }

            var className = fieldSymbol.ContainingType.Name;

            if (!Contexts.TryGetValue(className, out var fields))
            {
                fields = new List<NotifyPropertyChangedContext>();
                Contexts.Add(className, fields);
            }

            fields.Add(new NotifyPropertyChangedContext(fds, fieldSymbol));
        }
    }
}

================================================
FILE: src/MvvmBlazor.CodeGenerators/Properties/AssemblyInfo.cs
================================================
[assembly: CLSCompliant(false)]

================================================
FILE: src/MvvmBlazor.Core/Components/MvvmComponentAttribute.cs
================================================
// ReSharper disable once CheckNamespace

namespace MvvmBlazor;

[AttributeUsage(AttributeTargets.Class)]
public sealed class MvvmComponentAttribute : Attribute { }

================================================
FILE: src/MvvmBlazor.Core/Components/MvvmComponentBase.cs
================================================
namespace MvvmBlazor.Components;

[MvvmComponent]
// ReSharper disable once PartialTypeWithSinglePart
public abstract partial class MvvmComponentBase : ComponentBase { }

================================================
FILE: src/MvvmBlazor.Core/Components/MvvmComponentBaseT.cs
================================================
namespace MvvmBlazor.Components;

[MvvmComponent]
// ReSharper disable once PartialTypeWithSinglePart
public abstract partial class MvvmComponentBase<T> : MvvmComponentBase where T : ViewModelBase { }

================================================
FILE: src/MvvmBlazor.Core/Extensions/ServiceCollectionExtensions.cs
================================================
using Binder = MvvmBlazor.Internal.Bindings.Binder;

// ReSharper disable once CheckNamespace
namespace Microsoft.Extensions.DependencyInjection;

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddMvvm(this IServiceCollection serviceCollection)
    {
        serviceCollection.AddSingleton<IBindingFactory, BindingFactory>();
        serviceCollection.AddSingleton<IParameterResolver, ParameterResolver>();
        serviceCollection.AddSingleton<IParameterCache, ParameterCache>();
        serviceCollection.AddSingleton<IViewModelParameterSetter, ViewModelParameterSetter>();
        serviceCollection.AddTransient<IWeakEventManager, WeakEventManager>();
        serviceCollection.AddTransient<IBinder, Binder>();

        return serviceCollection;
    }
}

================================================
FILE: src/MvvmBlazor.Core/GlobalUsings.cs
================================================
global using System.ComponentModel;
global using System.Linq.Expressions;
global using System.Reflection;
global using System.Runtime.CompilerServices;
global using Microsoft.AspNetCore.Components;
global using MvvmBlazor.Internal.Bindings;
global using System.Collections.Specialized;
global using MvvmBlazor.Internal.WeakEventListener;
global using System.Runtime.Serialization;
global using MvvmBlazor.ViewModel;
global using MvvmBlazor.Components;
global using MvvmBlazor.Internal.Parameters;

================================================
FILE: src/MvvmBlazor.Core/Internal/Bindings/Binder.cs
================================================
namespace MvvmBlazor.Internal.Bindings;

public interface IBinder
{
    Action<IBinding, EventArgs>? ValueChangedCallback { get; set; }

    TValue Bind<TViewModel, TValue>(TViewModel viewModel, Expression<Func<TViewModel, TValue>> propertyExpression)
        where TViewModel : ViewModelBase;
}

internal class Binder : IBinder, IDisposable
{
    private readonly IBindingFactory _bindingFactory;
    private readonly HashSet<IBinding> _bindings = new();
    private readonly IWeakEventManager _weakEventManager;
    private bool _isDisposed;

    public Binder(IBindingFactory bindingFactory, IWeakEventManager weakEventManager)
    {
        _bindingFactory = bindingFactory;
        _weakEventManager = weakEventManager;
    }

    public Action<IBinding, EventArgs>? ValueChangedCallback { get; set; }

    public TValue Bind<TViewModel, TValue>(
        TViewModel viewModel,
        Expression<Func<TViewModel, TValue>> propertyExpression) where TViewModel : ViewModelBase
    {
        ThrowIfDisposed();

        if (ValueChangedCallback is null)
        {
            throw new BindingException($"{nameof(ValueChangedCallback)} is null");
        }

        var propertyInfo = ValidateAndResolveBindingContext(viewModel, propertyExpression);

        var binding = _bindingFactory.Create(viewModel, propertyInfo, _weakEventManager);
        if (_bindings.Contains(binding))
        {
            return (TValue)binding.GetValue();
        }

        _weakEventManager.AddWeakEventListener(binding, nameof(Binding.BindingValueChanged), ValueChangedCallback);
        binding.Initialize();

        _bindings.Add(binding);

        return (TValue)binding.GetValue();
    }

    protected static PropertyInfo ValidateAndResolveBindingContext<TViewModel, TValue>(
        TViewModel viewModel,
        Expression<Func<TViewModel, TValue>> property) where TViewModel : ViewModelBase
    {
        if (viewModel is null)
        {
            throw new BindingException("ViewModelType is null");
        }

        if (property is null)
        {
            throw new BindingException("Property expression is null");
        }

        if (property.Body is not MemberExpression { Member: PropertyInfo p })
        {
            throw new BindingException("Binding member needs to be a property");
        }

        if (typeof(TViewModel).GetProperty(p.Name) is null)
        {
            throw new BindingException($"Cannot find property {p.Name} in type {viewModel.GetType().FullName}");
        }

        return p;
    }

    #region IDisposable

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            ThrowIfDisposed();

            _isDisposed = true;
            DisposeBindings();
        }
    }

    private void DisposeBindings()
    {
        foreach (var binding in _bindings)
        {
            _weakEventManager.RemoveWeakEventListener(binding);
            binding.Dispose();
        }
    }

    private void ThrowIfDisposed()
    {
        if (_isDisposed)
        {
            throw new ObjectDisposedException(nameof(Binder));
        }
    }

    ~Binder()
    {
        Dispose(false);
    }

    #endregion
}

================================================
FILE: src/MvvmBlazor.Core/Internal/Bindings/Binding.cs
================================================
namespace MvvmBlazor.Internal.Bindings;

public interface IBinding : IDisposable
{
    INotifyPropertyChanged Source { get; }
    PropertyInfo PropertyInfo { get; }
    event EventHandler? BindingValueChanged;
    void Initialize();
    object GetValue();
}

internal class Binding : IBinding
{
    private readonly IWeakEventManager _weakEventManager;
    private INotifyCollectionChanged? _boundCollection;
    private bool _isCollection;

    public Binding(INotifyPropertyChanged source, PropertyInfo propertyInfo, IWeakEventManager weakEventManager)
    {
        _weakEventManager = weakEventManager;
        Source = source;
        PropertyInfo = propertyInfo;
    }

    public INotifyPropertyChanged Source { get; }
    public PropertyInfo PropertyInfo { get; }

    public event EventHandler? BindingValueChanged;

    public void Initialize()
    {
        _isCollection = typeof(INotifyCollectionChanged).IsAssignableFrom(PropertyInfo.PropertyType);
        _weakEventManager.AddWeakEventListener(Source, SourceOnPropertyChanged);
        AddCollectionBindings();
    }

    public object GetValue()
    {
        return PropertyInfo.GetValue(Source, null)!;
    }

    private void AddCollectionBindings()
    {
        if (!_isCollection || GetValue() is not INotifyCollectionChanged collection)
        {
            return;
        }

        _weakEventManager.AddWeakEventListener(collection, CollectionOnCollectionChanged);
        _boundCollection = collection;
    }

    private void SourceOnPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName is null)
        {
            BindingValueChanged?.Invoke(this, EventArgs.Empty);
            return;
        }

        // This should just listen to the bindings property
        if (e.PropertyName != PropertyInfo.Name)
        {
            return;
        }

        if (_isCollection)
        {
            // If our binding is a collection binding we need to remove the event
            // and reinitialize the collection bindings
            if (_boundCollection != null)
            {
                _weakEventManager.RemoveWeakEventListener(_boundCollection);
            }

            AddCollectionBindings();
        }


        BindingValueChanged?.Invoke(this, EventArgs.Empty);
    }

    private void CollectionOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        BindingValueChanged?.Invoke(this, EventArgs.Empty);
    }

    #region IDisposable Support

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (_boundCollection != null)
            {
                _weakEventManager.RemoveWeakEventListener(_boundCollection);
            }

            _weakEventManager.RemoveWeakEventListener(Source);
        }
    }

    #endregion

    #region Base overrides

    public override string ToString()
    {
        return $"{PropertyInfo?.DeclaringType?.Name}.{PropertyInfo?.Name}";
    }

    public override bool Equals(object? obj)
    {
        return obj is Binding b && ReferenceEquals(b.Source, Source) && b.PropertyInfo.Name == PropertyInfo.Name;
    }

    public override int GetHashCode()
    {
        var hash = 13;
        hash = hash * 7 + Source.GetHashCode();
        hash = hash * 7 + PropertyInfo.Name.GetHashCode(StringComparison.InvariantCulture);

        return hash;
    }

    #endregion
}

================================================
FILE: src/MvvmBlazor.Core/Internal/Bindings/BindingException.cs
================================================
namespace MvvmBlazor.Internal.Bindings;

public class BindingException : Exception
{
    public BindingException() { }

    protected BindingException(SerializationInfo info, StreamingContext context) : base(info, context) { }

    public BindingException(string message) : base(message) { }

    public BindingException(string message, Exception innerException) : base(message, innerException) { }
}

================================================
FILE: src/MvvmBlazor.Core/Internal/Bindings/BindingFactory.cs
================================================
namespace MvvmBlazor.Internal.Bindings;

internal interface IBindingFactory
{
    IBinding Create(INotifyPropertyChanged source, PropertyInfo propertyInfo, IWeakEventManager weakEventManager);
}

internal class BindingFactory : IBindingFactory
{
    public IBinding Create(INotifyPropertyChanged source, PropertyInfo propertyInfo, IWeakEventManager weakEventManager)
    {
        return new Binding(source, propertyInfo, weakEventManager);
    }
}

================================================
FILE: src/MvvmBlazor.Core/Internal/Parameters/ParameterCache.cs
================================================
using System.Collections.Concurrent;

namespace MvvmBlazor.Internal.Parameters;

internal interface IParameterCache
{
    ParameterInfo? Get(Type type);
    void Set(Type type, ParameterInfo info);
}

internal class ParameterCache : IParameterCache
{
    private readonly ConcurrentDictionary<Type, ParameterInfo> _cache = new();

    public ParameterInfo? Get(Type type)
    {
        return _cache.TryGetValue(type, out var info) ? info : null;
    }

    public void Set(Type type, ParameterInfo info)
    {
        ArgumentNullException.ThrowIfNull(info);
        _cache[type] = info;
    }
}

================================================
FILE: src/MvvmBlazor.Core/Internal/Parameters/ParameterException.cs
================================================
namespace MvvmBlazor.Internal.Parameters;

[Serializable]
public class ParameterException : Exception
{
    public ParameterException() { }
    protected ParameterException(SerializationInfo info, StreamingContext context) : base(info, context) { }
    public ParameterException(string? message) : base(message) { }
    public ParameterException(string? message, Exception? innerException) : base(message, innerException) { }
}

================================================
FILE: src/MvvmBlazor.Core/Internal/Parameters/ParameterInfo.cs
================================================
namespace MvvmBlazor.Internal.Parameters;

internal record ParameterInfo
{
    private readonly Dictionary<PropertyInfo, PropertyInfo> _parameters = new();

    public IReadOnlyDictionary<PropertyInfo, PropertyInfo> Parameters => _parameters;

    public ParameterInfo(IEnumerable<PropertyInfo> componentProperties, IEnumerable<PropertyInfo> viewModelProperties)
    {
        var componentPropertyDict = componentProperties.ToDictionary(x => x.Name);
        foreach (var viewModelProperty in viewModelProperties.OrderBy(x => x.Name))
        {
            if (!componentPropertyDict.TryGetValue(viewModelProperty.Name, out var componentProperty))
            {
                throw new ParameterException(
                    $"Failed to find matching component parameter {viewModelProperty.Name} for view model {viewModelProperty.DeclaringType!.FullName}"
                );
            }

            _parameters.Add(componentProperty, viewModelProperty);
        }
    }
}

================================================
FILE: src/MvvmBlazor.Core/Internal/Parameters/ParameterResolver.cs
================================================
namespace MvvmBlazor.Internal.Parameters;

internal interface IParameterResolver
{
    ParameterInfo ResolveParameters(Type componentType, Type viewModelType);
}

internal class ParameterResolver : IParameterResolver
{
    private readonly IParameterCache _parameterCache;

    public ParameterResolver(IParameterCache parameterCache)
    {
        _parameterCache = parameterCache;
    }

    public ParameterInfo ResolveParameters(Type componentType, Type viewModelType)
    {
        var parameterInfo = _parameterCache.Get(componentType);
        if (parameterInfo is not null)
        {
            return parameterInfo;
        }

        var componentParameters = ResolveTypeParameters(componentType);
        var viewModelParameters = ResolveTypeParameters(viewModelType);

        parameterInfo = new ParameterInfo(componentParameters, viewModelParameters);
        _parameterCache.Set(componentType, parameterInfo);

        return parameterInfo;
    }

    private static IEnumerable<PropertyInfo> ResolveTypeParameters(Type memberType)
    {
        var componentProperties = memberType.GetProperties();

        var resolvedComponentProperties = new List<PropertyInfo>();
        foreach (var componentProperty in componentProperties)
        {
            // Skip if property has no public setter
            if (componentProperty.GetSetMethod() is null)
            {
                continue;
            }

            // If the property is marked as a parameter add it to the list
            ParameterAttribute? GetParameterAttribute() => componentProperty.GetCustomAttribute<ParameterAttribute>();
            CascadingParameterAttribute? GetCascadingParameterAttribute() => componentProperty.GetCustomAttribute<CascadingParameterAttribute>();
            if (GetParameterAttribute() != null || GetCascadingParameterAttribute() != null)
            {
                resolvedComponentProperties.Add(componentProperty);
            }
        }

        return resolvedComponentProperties;
    }
}

================================================
FILE: src/MvvmBlazor.Core/Internal/Parameters/ViewModelParameterSetter.cs
================================================
namespace MvvmBlazor.Internal.Parameters;

public interface IViewModelParameterSetter
{
    void ResolveAndSet(ComponentBase component, ViewModelBase viewModel);
}

internal class ViewModelParameterSetter : IViewModelParameterSetter
{
    private readonly IParameterResolver _parameterResolver;

    public ViewModelParameterSetter(IParameterResolver parameterResolver)
    {
        _parameterResolver = parameterResolver;
    }

    public void ResolveAndSet(ComponentBase component, ViewModelBase viewModel)
    {
        var componentType = component.GetType();
        var viewModelType = viewModel.GetType();

        var parameterInfo = _parameterResolver.ResolveParameters(componentType, viewModelType);
        foreach (var (componentProperty, viewModelProperty) in parameterInfo.Parameters)
        {
            var value = componentProperty.GetValue(component);
            var parameterTypeDiffers = componentProperty.PropertyType != viewModelProperty.PropertyType;
            if (value != null && parameterTypeDiffers)
            {
                value = ConvertValue(componentProperty.PropertyType, viewModelProperty.PropertyType, value);
            }

            viewModelProperty.SetValue(viewModel, value);
        }
    }

    private static object? ConvertValue(Type componentType, Type viewModelType, object value)
    {
        var converter = TypeDescriptor.GetConverter(viewModelType);
        return converter.CanConvertFrom(componentType) ? converter.ConvertTo(value, viewModelType) : value;
    }
}

================================================
FILE: src/MvvmBlazor.Core/Internal/WeakEventListener/WeakEventListener.cs
================================================
namespace MvvmBlazor.Internal.WeakEventListener;

internal interface IWeakEventListener
{
    bool IsAlive { get; }
    object? Source { get; }
    Delegate? Handler { get; }
    void StopListening();
}

internal abstract class WeakEventListenerBase<T, TArgs> : IWeakEventListener where T : class where TArgs : EventArgs
{
    private readonly WeakReference<Action<T, TArgs>> _handler;
    private readonly WeakReference<T> _source;

    protected WeakEventListenerBase(T source, Action<T, TArgs> handler)
    {
        ArgumentNullException.ThrowIfNull(source);
        ArgumentNullException.ThrowIfNull(handler);

        _source = new WeakReference<T>(source);
        _handler = new WeakReference<Action<T, TArgs>>(handler);
    }

    public bool IsAlive => _handler.TryGetTarget(out _) && _source.TryGetTarget(out _);

    public object? Source
    {
        get
        {
            if (_source.TryGetTarget(out var source))
            {
                return source;
            }

            return null;
        }
    }

    public Delegate? Handler
    {
        get
        {
            if (_handler.TryGetTarget(out var handler))
            {
                return handler;
            }

            return null;
        }
    }

    public void StopListening()
    {
        if (_source.TryGetTarget(out var source))
        {
            StopListening(source);
        }
    }

    protected void HandleEvent(object sender, TArgs e)
    {
        if (_handler.TryGetTarget(out var handler))
        {
            handler((T)sender, e);
        }
        else
        {
            StopListening();
        }
    }

    protected abstract void StopListening(T source);
}

internal class TypedWeakEventListener<T, TArgs> : WeakEventListenerBase<T, TArgs>
    where T : class where TArgs : EventArgs
{
    private readonly Action<T, EventHandler<TArgs>> _unregister;

    public TypedWeakEventListener(
        T source,
        Action<T, EventHandler<TArgs>> register,
        Action<T, EventHandler<TArgs>> unregister,
        Action<T, TArgs> handler) : base(source, handler)
    {
        ArgumentNullException.ThrowIfNull(register);
        ArgumentNullException.ThrowIfNull(unregister);

        _unregister = unregister;
        register(source, HandleEvent!);
    }

    protected override void StopListening(T source)
    {
        _unregister(source, HandleEvent!);
    }
}

internal class PropertyChangedWeakEventListener<T> : WeakEventListenerBase<T, PropertyChangedEventArgs>
    where T : class, INotifyPropertyChanged
{
    public PropertyChangedWeakEventListener(T source, Action<T, PropertyChangedEventArgs> handler) : base(
        source,
        handler
    )
    {
        source.PropertyChanged += HandleEvent!;
    }

    protected override void StopListening(T source)
    {
        source.PropertyChanged -= HandleEvent!;
    }
}

internal class CollectionChangedWeakEventListener<T> : WeakEventListenerBase<T, NotifyCollectionChangedEventArgs>
    where T : class, INotifyCollectionChanged
{
    public CollectionChangedWeakEventListener(T source, Action<T, NotifyCollectionChangedEventArgs> handler) : base(
        source,
        handler
    )
    {
        source.CollectionChanged += HandleEvent!;
    }

    protected override void StopListening(T source)
    {
        source.CollectionChanged -= HandleEvent!;
    }
}

internal class WeakEventListener<T, TArgs> : WeakEventListenerBase<T, TArgs> where T : class where TArgs : EventArgs
{
    private readonly EventInfo _eventInfo;

    public WeakEventListener(T source, string eventName, Action<T, TArgs> handler) : base(source, handler)
    {
        _eventInfo = source.GetType().GetEvent(eventName) ??
                     throw new ArgumentException("Unknown Event Name", nameof(eventName));
        if (_eventInfo.EventHandlerType == typeof(EventHandler<TArgs>))
        {
            _eventInfo.AddEventHandler(source, new EventHandler<TArgs>(HandleEvent!));
        }
        else //the event type isn't just an EventHandler<> so we have to create the delegate using reflection
        {
            _eventInfo.AddEventHandler(
                source,
                Delegate.CreateDelegate(_eventInfo.EventHandlerType!, this, nameof(HandleEvent))
            );
        }
    }

    protected override void StopListening(T source)
    {
        if (_eventInfo.EventHandlerType == typeof(EventHandler<TArgs>))
        {
            _eventInfo.RemoveEventHandler(source, new EventHandler<TArgs>(HandleEvent!));
        }
        else
        {
            _eventInfo.RemoveEventHandler(
                source,
                Delegate.CreateDelegate(_eventInfo.EventHandlerType!, this, nameof(HandleEvent))
            );
        }
    }
}

================================================
FILE: src/MvvmBlazor.Core/Internal/WeakEventListener/WeakEventManager.cs
================================================
namespace MvvmBlazor.Internal.WeakEventListener;

internal interface IWeakEventManager
{
    /// <summary>
    ///     Registers the given delegate as a handler for the event specified by `eventName` on the given source.
    /// </summary>
    void AddWeakEventListener<T, TArgs>(T source, string eventName, Action<T, TArgs> handler)
        where T : class where TArgs : EventArgs;

    /// <summary>
    ///     Registers the given delegate as a handler for the INotifyPropertyChanged.PropertyChanged event
    /// </summary>
    void AddWeakEventListener<T>(T source, Action<T, PropertyChangedEventArgs> handler)
        where T : class, INotifyPropertyChanged;

    /// <summary>
    ///     Registers the given delegate as a handler for the INotifyCollectionChanged.CollectionChanged event
    /// </summary>
    void AddWeakEventListener<T>(T source, Action<T, NotifyCollectionChangedEventArgs> handler)
        where T : class, INotifyCollectionChanged;

    /// <summary>
    ///     Unregisters any previously registered weak event handlers on the given source object
    /// </summary>
    void RemoveWeakEventListener<T>(T source) where T : class;

    /// <summary>
    ///     Unregisters all weak event listeners that have been registered by this weak event manager instance
    /// </summary>
    void ClearWeakEventListeners();
}

public class WeakEventManager : IWeakEventManager
{
    private readonly Dictionary<IWeakEventListener, Delegate> _listeners = new();

    /// <summary>
    ///     Registers the given delegate as a handler for the event specified by `eventName` on the given source.
    /// </summary>
    public void AddWeakEventListener<T, TArgs>(T source, string eventName, Action<T, TArgs> handler)
        where T : class where TArgs : EventArgs
    {
        ArgumentNullException.ThrowIfNull(source);

        _listeners.Add(new WeakEventListener<T, TArgs>(source, eventName, handler), handler);
    }

    /// <summary>
    ///     Registers the given delegate as a handler for the INotifyPropertyChanged.PropertyChanged event
    /// </summary>
    public void AddWeakEventListener<T>(T source, Action<T, PropertyChangedEventArgs> handler)
        where T : class, INotifyPropertyChanged
    {
        ArgumentNullException.ThrowIfNull(source);

        _listeners.Add(new PropertyChangedWeakEventListener<T>(source, handler), handler);
    }

    /// <summary>
    ///     Registers the given delegate as a handler for the INotifyCollectionChanged.CollectionChanged event
    /// </summary>
    public void AddWeakEventListener<T>(T source, Action<T, NotifyCollectionChangedEventArgs> handler)
        where T : class, INotifyCollectionChanged
    {
        ArgumentNullException.ThrowIfNull(source);

        _listeners.Add(new CollectionChangedWeakEventListener<T>(source, handler), handler);
    }

    /// <summary>
    ///     Unregisters any previously registered weak event handlers on the given source object
    /// </summary>
    public void RemoveWeakEventListener<T>(T source) where T : class
    {
        ArgumentNullException.ThrowIfNull(source);

        var toRemove = new List<IWeakEventListener>();
        foreach (var listener in _listeners.Keys)
            if (!listener.IsAlive)
            {
                toRemove.Add(listener);
            }
            else if (listener.Source == source)
            {
                listener.StopListening();
                toRemove.Add(listener);
            }

        foreach (var item in toRemove) _listeners.Remove(item);
    }

    /// <summary>
    ///     Unregisters all weak event listeners that have been registered by this weak event manager instance
    /// </summary>
    public void ClearWeakEventListeners()
    {
        foreach (var listener in _listeners.Keys)
            if (listener.IsAlive)
            {
                listener.StopListening();
            }

        _listeners.Clear();
    }
}

================================================
FILE: src/MvvmBlazor.Core/MvvmBlazor.Core.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
    <CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GeneratedFiles</CompilerGeneratedFilesOutputPath>
    <RootNamespace>MvvmBlazor</RootNamespace>
  </PropertyGroup>

  <PropertyGroup Condition="'$(CI)' == 'true'">
    <Deterministic>true</Deterministic>
    <PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
    <PackageLicenseExpression>MIT</PackageLicenseExpression>
    <Authors>Christian Klemm</Authors>
    <Description>A lightweight Blazor Mvvm Library</Description>
    <PackageProjectUrl>$(GITHUB_SERVER_URL)/$(GITHUB_REPOSITORY)</PackageProjectUrl>
    <RepositoryUrl>$(GITHUB_SERVER_URL)/$(GITHUB_REPOSITORY)</RepositoryUrl>
    <RepositoryType>git</RepositoryType>
    <PackageTags>Blazor;Mvvm</PackageTags>
    <PublishRepositoryUrl>true</PublishRepositoryUrl>
    <IncludeSymbols>true</IncludeSymbols>
    <EmbedUntrackedSources>true</EmbedUntrackedSources>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Components" Version="6.0.0" />
    <PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="6.0.0">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\MvvmBlazor.CodeGenerators\MvvmBlazor.CodeGenerators.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
  </ItemGroup>
</Project>

================================================
FILE: src/MvvmBlazor.Core/NotifyAttribute.cs
================================================
// ReSharper disable once CheckNamespace

namespace MvvmBlazor;

[AttributeUsage(AttributeTargets.Field)]
public sealed class NotifyAttribute : Attribute { }

================================================
FILE: src/MvvmBlazor.Core/Properties/AssemblyInfo.cs
================================================
[assembly: InternalsVisibleTo("MvvmBlazor.Tests")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
[assembly: CLSCompliant(false)]

================================================
FILE: src/MvvmBlazor.Core/Properties/GlobalSuppressions.cs
================================================
// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.

using System.Diagnostics.CodeAnalysis;

[assembly: SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters")]

================================================
FILE: src/MvvmBlazor.Core/ViewModel/ViewModelBase.cs
================================================
namespace MvvmBlazor.ViewModel;

public abstract class ViewModelBase : INotifyPropertyChanged
{
    private readonly Dictionary<string, List<Func<object, Task>>> _subscriptions = new();

    public IServiceProvider RootServiceProvider { get; set; } = null!;

    public event PropertyChangedEventHandler? PropertyChanged;

    protected bool Set<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
    {
        return Set(ref field, value, EqualityComparer<T>.Default, propertyName);
    }

    protected bool Set<T>(ref T field, T value, IEqualityComparer<T> equalityComparer, [CallerMemberName] string? propertyName = null)
    {
        ArgumentNullException.ThrowIfNull(equalityComparer);
        if (!equalityComparer.Equals(field, value))
        {
            field = value;
            OnPropertyChanged(propertyName!);
            if (!_subscriptions.ContainsKey(propertyName!))
            {
                return true;
            }

            foreach (var action in _subscriptions[propertyName!]) action(value!);
            return true;
        }

        return false;
    }

    public virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    {
        ArgumentNullException.ThrowIfNull(propertyName);
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    protected void Subscribe<T>(Expression<Func<T>>? expression, Action<T> action)
    {
        SubscribeAsync(
            expression,
            arg =>
            {
                action(arg);
                return Task.CompletedTask;
            }
        );
    }

    protected void SubscribeAsync<T>(Expression<Func<T>>? property, Func<T, Task> func)
    {
        if (property is null)
        {
            throw new BindingException("Property cannot be null");
        }

        if (!(property.Body is MemberExpression m))
        {
            throw new BindingException("Subscription member must be a property");
        }

        if (!(m.Member is PropertyInfo propertyInfo))
        {
            throw new BindingException("Subscription member must be a property");
        }

        var propertyName = propertyInfo.Name;
        if (!_subscriptions.ContainsKey(propertyName))
        {
            _subscriptions[propertyName] = new List<Func<object, Task>>();
        }

        _subscriptions[propertyName].Add(async value => await func((T)value).ConfigureAwait(false));
    }

    #region Lifecycle Methods

    /// <summary>
    ///     Method invoked when the component is ready to start, having received its
    ///     initial parameters from its parent in the render tree.
    /// </summary>
    public virtual void OnInitialized() { }

    /// <summary>
    ///     Method invoked when the component is ready to start, having received its
    ///     initial parameters from its parent in the render tree.
    ///     Override this method if you will perform an asynchronous operation and
    ///     want the component to refresh when that operation is completed.
    /// </summary>
    /// <returns>A <see cref="Task" /> representing any asynchronous operation.</returns>
    public virtual Task OnInitializedAsync()
    {
        return Task.CompletedTask;
    }

    /// <summary>
    ///     Method invoked when the component has received parameters from its parent in
    ///     the render tree, and the incoming values have been assigned to properties.
    /// </summary>
    public virtual void OnParametersSet() { }

    /// <summary>
    ///     Method invoked when the component has received parameters from its parent in
    ///     the render tree, and the incoming values have been assigned to properties.
    /// </summary>
    /// <returns>A <see cref="Task" /> representing any asynchronous operation.</returns>
    public virtual Task OnParametersSetAsync()
    {
        return Task.CompletedTask;
    }

    /// <summary>
    ///     Notifies the component that its state has changed. When applicable, this will
    ///     cause the component to be re-rendered.
    /// </summary>
    protected void StateHasChanged()
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
    }

    /// <summary>
    ///     Returns a flag to indicate whether the component should render.
    /// </summary>
    /// <returns></returns>
    public virtual bool ShouldRender()
    {
        return true;
    }

    /// <summary>
    ///     Method invoked after each time the component has been rendered.
    /// </summary>
    /// <param name="firstRender">
    ///     Set to <c>true</c> if this is the first time
    ///     <see cref="ComponentBase.OnAfterRender(bool)" /> has been invoked
    ///     on this component instance; otherwise <c>false</c>.
    /// </param>
    /// <remarks>
    ///     The <see cref="ComponentBase.OnAfterRender(bool)" /> and
    ///     <see cref="ComponentBase.OnAfterRenderAsync(bool)" /> lifecycle methods
    ///     are useful for performing interop, or interacting with values received from <c>@ref</c>.
    ///     Use the <paramref name="firstRender" /> parameter to ensure that initialization work is only performed
    ///     once.
    /// </remarks>
    public virtual void OnAfterRender(bool firstRender) { }

    /// <summary>
    ///     Method invoked after each time the component has been rendered. Note that the component does
    ///     not automatically re-render after the completion of any returned <see cref="Task" />,
    ///     because
    ///     that would cause an infinite render loop.
    /// </summary>
    /// <param name="firstRender">
    ///     Set to <c>true</c> if this is the first time
    ///     <see cref="ComponentBase.OnAfterRender(bool)" /> has been invoked
    ///     on this component instance; otherwise <c>false</c>.
    /// </param>
    /// <returns>A <see cref="Task" /> representing any asynchronous operation.</returns>
    /// <remarks>
    ///     The <see cref="ComponentBase.OnAfterRender(bool)" /> and
    ///     <see cref="ComponentBase.OnAfterRenderAsync(bool)" /> lifecycle methods
    ///     are useful for performing interop, or interacting with values received from <c>@ref</c>.
    ///     Use the <paramref name="firstRender" /> parameter to ensure that initialization work is only performed
    ///     once.
    /// </remarks>
    public virtual Task OnAfterRenderAsync(bool firstRender)
    {
        return Task.CompletedTask;
    }

    /// <summary>
    ///     Sets parameters supplied by the component's parent in the render tree.
    /// </summary>
    /// <param name="parameters">The parameters.</param>
    /// <returns>
    ///     A <see cref="Task" /> that completes when the component has finished updating and
    ///     rendering itself.
    /// </returns>
    /// <remarks>
    ///     <para>
    ///         The
    ///         <see
    ///             cref="ComponentBase.SetParametersAsync(ParameterView)" />
    ///         method should be passed the entire set of parameter values each
    ///         time
    ///         <see
    ///             cref="ComponentBase.SetParametersAsync(ParameterView)" />
    ///         is called. It not required that the caller supply a parameter
    ///         value for all parameters that are logically understood by the component.
    ///     </para>
    ///     <para>
    ///         The default implementation of
    ///         <see
    ///             cref="ComponentBase.SetParametersAsync(ParameterView)" />
    ///         will set the value of each property
    ///         decorated with <see cref="ParameterAttribute" /> or
    ///         <see cref="CascadingParameterAttribute" /> that has
    ///         a corresponding value in the <see cref="ParameterView" />. Parameters that do
    ///         not have a corresponding value
    ///         will be unchanged.
    ///     </para>
    /// </remarks>
    public virtual Task SetParametersAsync(ParameterView parameters)
    {
        return Task.CompletedTask;
    }

    #endregion
}

================================================
FILE: src/MvvmBlazor.Tests/.editorconfig
================================================
[*.cs]
dotnet_diagnostic.CA1707.severity = none
dotnet_diagnostic.CA1812.severity = none

================================================
FILE: src/MvvmBlazor.Tests/Abstractions/StrictMock.cs
================================================
namespace MvvmBlazor.Tests.Abstractions;

internal class StrictMock<T> : Mock<T> where T : class
{
    public StrictMock() : base(MockBehavior.Strict) { }
    public StrictMock(params object[] args) : base(MockBehavior.Strict, args) { }
    public StrictMock(Expression<Func<T>> newExpression) : base(newExpression, MockBehavior.Strict) { }
}

================================================
FILE: src/MvvmBlazor.Tests/Abstractions/UnitTest.cs
================================================
namespace MvvmBlazor.Tests.Abstractions;

public abstract class UnitTest : IDisposable
{
    private readonly ServiceProvider _services;
    protected IServiceProvider Services => _services;

    protected UnitTest(ITestOutputHelper outputHelper)
    {
        _services = BuildProvider(outputHelper);
    }

    private ServiceProvider BuildProvider(ITestOutputHelper testOutputHelper)
    {
        var serviceCollection = new ServiceCollection();
        serviceCollection.AddSingleton(testOutputHelper);

        RegisterServices(serviceCollection);

        return serviceCollection.BuildServiceProvider();
    }

    protected virtual void RegisterServices(IServiceCollection services) { }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            _services.Dispose();
        }
    }
}

================================================
FILE: src/MvvmBlazor.Tests/Components/MvvmComponentBaseTTests.cs
================================================
namespace MvvmBlazor.Tests.Components;

public class MvvmComponentBaseTTests : UnitTest
{
    public MvvmComponentBaseTTests(ITestOutputHelper outputHelper) : base(outputHelper) { }

    protected override void RegisterServices(IServiceCollection services)
    {
        var binder = services.StrictMock<IBinder>();
        services.StrictMock<ViewModelBase>();
        services.StrictMock<IViewModelParameterSetter>();
        services.AddSingleton<MockMvvmComponentBase>();
        services.AddSingleton<TestViewModel>();

        binder.SetupSet(x => x.ValueChangedCallback = It.IsAny<Action<IBinding, EventArgs>>()).Verifiable();
    }

    [Fact]
    public void AfterRender_called_On_binding_context()
    {
        var viewModel = Services.GetMock<ViewModelBase>();
        viewModel.Setup(x => x.OnAfterRender(true)).Verifiable();

        var component = Services.GetRequiredService<MockMvvmComponentBase>();
        component.AfterRender(true);

        viewModel.Verify();
    }

    [Fact]
    public async Task AfterRenderAsync_called_on_binding_context()
    {
        var task = Task.CompletedTask;
        var viewModel = Services.GetMock<ViewModelBase>();
        var component = Services.GetRequiredService<MockMvvmComponentBase>();

        viewModel.Setup(x => x.OnAfterRenderAsync(It.IsAny<bool>())).Returns(task).Verifiable();

        await component.AfterRenderAsync(true);

        viewModel.Verify();
    }

    [Fact]
    public void Bind_binds_binding_context()
    {
        var viewModel = Services.GetRequiredService<TestViewModel>();
        var binder = Services.GetMock<IBinder>();

        binder.Setup(x => x.Bind(viewModel, x => x.TestProperty));

        var component = new Mock<MvvmComponentBase<TestViewModel>>(Services.GetRequiredService<IServiceScopeFactory>());

        component.Setup(x => x.AddBinding(viewModel, x => x.TestProperty)).Verifiable();
        component.Object.Bind(x => x.TestProperty);

        component.Verify();
        component.VerifyNoOtherCalls();
    }

    [Fact]
    public void OnInitialized_called_on_binding_context()
    {
        var viewModel = Services.GetMock<ViewModelBase>();
        var viewModelParameterSetter = Services.GetMock<IViewModelParameterSetter>();
        var component = Services.GetRequiredService<MockMvvmComponentBase>();

        viewModel.Setup(x => x.OnInitialized()).Verifiable();
        viewModelParameterSetter.Setup(x => x.ResolveAndSet(component, viewModel.Object)).Verifiable();

        component.Initialized();

        viewModel.Verify();
    }

    [Fact]
    public async Task OnInitializedAsync_called_on_binding_context()
    {
        var task = Task.CompletedTask;

        var viewModel = Services.GetMock<ViewModelBase>();
        var component = Services.GetRequiredService<MockMvvmComponentBase>();

        viewModel.Setup(x => x.OnInitializedAsync()).Returns(task).Verifiable();

        await component.InitializedAsync();

        viewModel.Verify();
    }

    [Fact]
    public void OnParametersSet_sets_viewmodel_parameters()
    {
        var viewModel = Services.GetMock<ViewModelBase>();
        var viewModelParameterSetter = Services.GetMock<IViewModelParameterSetter>();
        var component = Services.GetRequiredService<MockMvvmComponentBase>();

        viewModel.Setup(x => x.OnParametersSet()).Verifiable();
        viewModelParameterSetter.Setup(x => x.ResolveAndSet(component, viewModel.Object)).Verifiable();

        component.ParametersSet();

        viewModelParameterSetter.Verify();
    }

    [Fact]
    public async Task OnParametersSetAsync_called_on_binding_context()
    {
        var task = Task.CompletedTask;

        var viewModel = Services.GetMock<ViewModelBase>();
        var component = Services.GetRequiredService<MockMvvmComponentBase>();

        viewModel.Setup(x => x.OnParametersSetAsync()).Returns(task).Verifiable();

        await component.ParametersSetAsync();

        viewModel.Verify();
    }

    [Fact]
    public void Sets_binding_context()
    {
        var viewModel = Services.GetMock<ViewModelBase>();
        var component = Services.GetRequiredService<MockMvvmComponentBase>();

        component.Context.ShouldBe(viewModel.Object);
    }

    [Fact]
    public void ShouldRender_called_on_binding_context()
    {
        var viewModel = Services.GetMock<ViewModelBase>();
        var component = Services.GetRequiredService<MockMvvmComponentBase>();

        viewModel.Setup(x => x.ShouldRender()).Returns(true).Verifiable();

        var res = component.Render();
        res.ShouldBe(true);

        viewModel.Verify();
    }

    private class MockMvvmComponentBase : MvvmComponentBase<ViewModelBase>
    {
        public ViewModelBase Context => BindingContext;
        public MockMvvmComponentBase(IServiceProvider services) : base(services) { }

        public void Initialized()
        {
            OnInitialized();
        }

        public Task InitializedAsync()
        {
            return OnInitializedAsync();
        }

        public void ParametersSet()
        {
            OnParametersSet();
        }

        public Task ParametersSetAsync()
        {
            return OnParametersSetAsync();
        }

        public bool Render()
        {
            return ShouldRender();
        }

        public void AfterRender(bool firstRender)
        {
            OnAfterRender(firstRender);
        }

        public Task AfterRenderAsync(bool firstRender)
        {
            return OnAfterRenderAsync(firstRender);
        }
    }
}

================================================
FILE: src/MvvmBlazor.Tests/Components/MvvmComponentBaseTests.cs
================================================
namespace MvvmBlazor.Tests.Components;

public class MvvmComponentBaseTests : UnitTest
{
    public MvvmComponentBaseTests(ITestOutputHelper outputHelper) : base(outputHelper) { }

    protected override void RegisterServices(IServiceCollection services)
    {
        var binder = services.StrictMock<IBinder>();
        services.AddSingleton<TestComponent>();

        binder.SetupSet(x => x.ValueChangedCallback = It.IsAny<Action<IBinding, EventArgs>>()).Verifiable();
    }

    [Fact]
    public void AddBinding_adds_Binding()
    {
        var viewModel = new TestViewModel();
        var binder = Services.GetMock<IBinder>();
        binder.Setup(x => x.Bind(viewModel, y => y.TestProperty)).Returns("Test").Verifiable();

        var component = Services.GetRequiredService<TestComponent>();
        var res = component.AddBinding(viewModel, x => x.TestProperty);
        res.ShouldBe("Test");

        binder.Verify();
    }

    [Fact]
    public void Bind_adds_Binding()
    {
        var viewModel = new TestViewModel();

        var component = new Mock<MvvmComponentBase>(Services.GetRequiredService<IServiceScopeFactory>());
        component.Setup(x => x.AddBinding(viewModel, y => y.TestProperty)).Returns("Test").Verifiable();

        var res = component.Object.Bind(viewModel, x => x.TestProperty);
        res.ShouldBe("Test");

        component.Verify();
    }

    internal class TestComponent : MvvmComponentBase
    {
        public Action? BindingChangedAction { get; set; }


        public TestComponent(IServiceProvider services) : base(services) { }

        internal override void BindingOnBindingValueChanged(object sender, EventArgs e)
        {
            BindingChangedAction?.Invoke();
        }
    }
}

================================================
FILE: src/MvvmBlazor.Tests/Components/TestViewModel.cs
================================================
namespace MvvmBlazor.Tests.Components;

public class TestViewModel : ViewModelBase
{
    private string? _testProperty;

    public string? TestProperty
    {
        get => _testProperty;
        set => Set(ref _testProperty, value);
    }
}

================================================
FILE: src/MvvmBlazor.Tests/Extensions/ServiceCollectionExtensions.cs
================================================
// ReSharper disable once CheckNamespace

namespace Microsoft.Extensions.DependencyInjection;

internal static class ServiceCollectionExtensions
{
    public static Mock<T> Mock<T>(this IServiceCollection services, params object[] args) where T : class
    {
        var mock = new Mock<T>(args);
        services.AddSingleton(mock);
        services.AddSingleton(mock.Object);
        return mock;
    }

    public static Mock<T> StrictMock<T>(this IServiceCollection services) where T : class
    {
        var mock = new StrictMock<T>();
        services.AddSingleton<Mock<T>>(mock);
        services.AddSingleton(mock.Object);
        return mock;
    }

    public static IServiceCollection Provide<T>(this IServiceCollection services) where T : class
    {
        return services.AddSingleton<T>();
    }
}

================================================
FILE: src/MvvmBlazor.Tests/Extensions/ServiceProviderExtensions.cs
================================================
// ReSharper disable once CheckNamespace

namespace Microsoft.Extensions.DependencyInjection;

internal static class ServiceProviderExtensions
{
    public static Mock<T> GetMock<T>(this IServiceProvider provider) where T : class
    {
        return provider.GetRequiredService<Mock<T>>();
    }
}

================================================
FILE: src/MvvmBlazor.Tests/Generators/MvvmComponentGeneratorTests.cs
================================================
using Binder = System.Reflection.Binder;

namespace MvvmBlazor.Tests.Generators;

public class MvvmComponentGeneratorTests
{
    [Fact]
    public void Generates_error_when_component_is_not_partial()
    {
        var inputCompilation = CreateCompilation(
            @$"
                [{nameof(MvvmComponentAttribute)}]
                public class TestComponent : Microsoft.AspNetCore.Components.ComponentBase {{}}
            "
        );

        var generator = new MvvmComponentGenerator();
        GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);

        driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out _, out var diagnostics);
        diagnostics.ShouldNotBeEmpty();
        diagnostics.First().Id.ShouldBe("MVVMBLAZOR001");
    }

    [Fact]
    public void Generates_error_when_component_is_not_inheriting()
    {
        var inputCompilation = CreateCompilation(
            @$"
                [{nameof(MvvmComponentAttribute)}]
                public partial class TestComponent {{}}
            "
        );

        var generator = new MvvmComponentGenerator();
        GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);

        driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out _, out var diagnostics);
        diagnostics.ShouldNotBeEmpty();
        diagnostics.First().Id.ShouldBe("MVVMBLAZOR002");
    }

    [Fact]
    public void Generates_error_when_generic_type_parameter_count_is_wrong()
    {
        var inputCompilation = CreateCompilation(
            @$"
                [{nameof(MvvmComponentAttribute)}]
                public partial class TestComponent<T, T1> : Microsoft.AspNetCore.Components.ComponentBase {{}}
            "
        );

        var generator = new MvvmComponentGenerator();
        GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);

        driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out _, out var diagnostics);
        diagnostics.ShouldNotBeEmpty();
        diagnostics.First().Id.ShouldBe("MVVMBLAZOR005");
    }

    [Fact]
    public void Generates_error_when_generic_type_parameter_name_is_wrong()
    {
        var inputCompilation = CreateCompilation(
            @$"
                [{nameof(MvvmComponentAttribute)}]
                public partial class TestComponent<T1> : Microsoft.AspNetCore.Components.ComponentBase {{}}
            "
        );

        var generator = new MvvmComponentGenerator();
        GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);

        driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out _, out var diagnostics);
        diagnostics.ShouldNotBeEmpty();
        diagnostics.First().Id.ShouldBe("MVVMBLAZOR005");
    }

    [Fact]
    public void Generates_component_with_component_base_class()
    {
        var inputCompilation = CreateCompilation(
            @$"
                [{nameof(MvvmComponentAttribute)}]
                public partial class TestComponent : Microsoft.AspNetCore.Components.ComponentBase {{}}
            "
        );

        var generator = new MvvmComponentGenerator();
        GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);

        driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out _, out var diagnostics);
        diagnostics.ShouldBeEmpty();
    }

    [Fact]
    public void Generates_component_with_owning_component_base_class()
    {
        var inputCompilation = CreateCompilation(
            @$"
                [{nameof(MvvmComponentAttribute)}]
                public partial class TestComponent : Microsoft.AspNetCore.Components.OwningComponentBase {{}}
            "
        );

        var generator = new MvvmComponentGenerator();
        GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);

        driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out _, out var diagnostics);
        diagnostics.ShouldBeEmpty();
    }

    [Fact]
    public void Generates_generic_component()
    {
        var inputCompilation = CreateCompilation(
            @$"
                [{nameof(MvvmComponentAttribute)}]
                public partial class TestComponent<T> : Microsoft.AspNetCore.Components.OwningComponentBase {{}}
            "
        );

        var generator = new MvvmComponentGenerator();
        GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);

        driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out _, out var diagnostics);
        diagnostics.ShouldBeEmpty();
    }

    private static Compilation CreateCompilation(string source)
    {
        return CSharpCompilation.Create(
            "compilation",
            new[] { CSharpSyntaxTree.ParseText(source) },
            new[]
            {
                MetadataReference.CreateFromFile(typeof(Binder).GetTypeInfo().Assembly.Location),
                MetadataReference.CreateFromFile(typeof(ComponentBase).GetTypeInfo().Assembly.Location)
            },
            new CSharpCompilationOptions(OutputKind.ConsoleApplication)
        );
    }
}

================================================
FILE: src/MvvmBlazor.Tests/Generators/NotifyPropertyChangedGeneratorTests.cs
================================================
namespace MvvmBlazor.Tests.Generators;

public class NotifyPropertyChangedGeneratorTests
{
    [Fact]
    public void Generates_error_when_viewmodel_is_not_partial()
    {
        var inputCompilation = CreateCompilation(
            @$"
                public class TestViewModel : MvvmBlazor.ViewModel.ViewModelBase
                {{
                    [{typeof(NotifyAttribute)}]
                    private bool _test;
                }}
            "
        );

        var generator = new NotifyPropertyChangedGenerator();
        GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);

        driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out _, out var diagnostics);
        diagnostics.ShouldNotBeEmpty();
        diagnostics.First().Id.ShouldBe("MVVMBLAZOR003");
    }

    [Fact]
    public void Generates_error_when_view_model_is_not_inheriting()
    {
        var inputCompilation = CreateCompilation(
            @$"
                public partial class TestViewModel
                {{
                    [{typeof(NotifyAttribute)}]
                    private bool _test;
                }}
            "
        );

        var generator = new NotifyPropertyChangedGenerator();
        GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);

        driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out _, out var diagnostics);
        diagnostics.ShouldNotBeEmpty();
        diagnostics.First().Id.ShouldBe("MVVMBLAZOR004");
    }

    [Fact]
    public void Generates_viewmodel()
    {
        var inputCompilation = CreateCompilation(
            @$"
                public partial class TestViewModel : MvvmBlazor.ViewModel.ViewModelBase
                {{
                    [{typeof(NotifyAttribute)}]
                    private bool _test;

                    public TestViewModel()
                    {{
                        Test = true;
                    }}
                }}
            "
        );

        var generator = new NotifyPropertyChangedGenerator();
        GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);

        driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out _, out var diagnostics);
        diagnostics.ShouldBeEmpty();
    }

    [Fact]
    public void Generates_viewmodel_for_abstract_class()
    {
        var inputCompilation = CreateCompilation(
            @$"
                public abstract partial class TestViewModel : MvvmBlazor.ViewModel.ViewModelBase
                {{
                    [{typeof(NotifyAttribute)}]
                    private bool _test;
                }}

                public TestViewModel()
                {{
                    Test = true;
                }}
            "
        );

        var generator = new NotifyPropertyChangedGenerator();
        GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);

        driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out _, out var diagnostics);
        diagnostics.ShouldBeEmpty();
    }

    [Fact]
    public void Generates_viewmodel_for_abstract_class_with_inheritance()
    {
        var inputCompilation = CreateCompilation(
            @$"
                public abstract partial class BaseViewModel : MvvmBlazor.ViewModel.ViewModelBase
                {{
                    [{typeof(NotifyAttribute)}]
                    private bool _foo;
                }}

                public abstract partial class TestViewModel : BaseViewModel
                {{
                    [{typeof(NotifyAttribute)}]
                    private bool _test;
                }}

                public TestViewModel()
                {{
                    Test = true;
                    Foo = true;
                }}
            "
        );

        var generator = new NotifyPropertyChangedGenerator();
        GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);

        driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out _, out var diagnostics);
        diagnostics.ShouldBeEmpty();
    }


    [Fact]
    public void Generates_viewmodel_for_generic_class()
    {
        var inputCompilation = CreateCompilation(
            @$"
                public abstract partial class TestViewModel<T> : MvvmBlazor.ViewModel.ViewModelBase
                {{
                    [{typeof(NotifyAttribute)}]
                    private bool _test;
                }}

                public TestViewModel()
                {{
                    Test = true;
                }}
            "
        );

        var generator = new NotifyPropertyChangedGenerator();
        GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);

        driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out _, out var diagnostics);
        diagnostics.ShouldBeEmpty();
    }

    [Fact]
    public void Generates_viewmodel_for_abstract_generic_class()
    {
        var inputCompilation = CreateCompilation(
            @$"
                public abstract partial class TestViewModel<T> : MvvmBlazor.ViewModel.ViewModelBase
                {{
                    [{typeof(NotifyAttribute)}]
                    private bool _test;
                }}

                public TestViewModel()
                {{
                    Test = true;
                }}
            "
        );

        var generator = new NotifyPropertyChangedGenerator();
        GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);

        driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out _, out var diagnostics);
        diagnostics.ShouldBeEmpty();
    }

    private static Compilation CreateCompilation(string source)
    {
        return CSharpCompilation.Create(
            "compilation",
            new[] { CSharpSyntaxTree.ParseText(source) },
            new[]
            {
                MetadataReference.CreateFromFile(typeof(MvvmComponentBase).GetTypeInfo().Assembly.Location)
            },
            new CSharpCompilationOptions(OutputKind.ConsoleApplication)
        );
    }
}

================================================
FILE: src/MvvmBlazor.Tests/GlobalUsings.cs
================================================
global using System;
global using Microsoft.Extensions.DependencyInjection;
global using Moq;
global using MvvmBlazor.Components;
global using MvvmBlazor.Internal.Bindings;
global using MvvmBlazor.Tests.Abstractions;
global using Shouldly;
global using Xunit;
global using Xunit.Abstractions;
global using System.Linq;
global using System.Reflection;
global using Microsoft.AspNetCore.Components;
global using Microsoft.CodeAnalysis;
global using Microsoft.CodeAnalysis.CSharp;
global using System.Threading.Tasks;
global using MvvmBlazor.Internal.Parameters;
global using MvvmBlazor.ViewModel;
global using System.Linq.Expressions;
global using System.Collections.Specialized;
global using System.ComponentModel;
global using MvvmBlazor.Internal.WeakEventListener;
global using MvvmBlazor.CodeGenerators.Components;
global using MvvmBlazor.CodeGenerators.NotifyPropertyChanged;

================================================
FILE: src/MvvmBlazor.Tests/Internal/Bindings/BinderTests.cs
================================================
using Binder = MvvmBlazor.Internal.Bindings.Binder;

namespace MvvmBlazor.Tests.Internal.Bindings;

public class BinderTests : UnitTest
{
    public BinderTests(ITestOutputHelper outputHelper) : base(outputHelper) { }

    protected override void RegisterServices(IServiceCollection services)
    {
        services.StrictMock<IBindingFactory>();
        services.StrictMock<IWeakEventManager>();
        services.Provide<TestViewModel>();
        services.Provide<Binder>();
    }

    [Fact]
    public void Bind_throws_when_callback_is_null()
    {
        var viewModel = Services.GetRequiredService<TestViewModel>();
        var binder = Services.GetRequiredService<Binder>();

        Should.Throw<BindingException>(() => binder.Bind(viewModel, x => x.Test));
    }

    [Fact]
    public void Bind_throws_when_view_model_is_null()
    {
        var binder = Services.GetRequiredService<Binder>();

        var callback = (IBinding b, EventArgs a) => { };
        binder.ValueChangedCallback = callback;

        Should.Throw<BindingException>(() => binder.Bind<TestViewModel, string>(null!, x => x.Test!));
    }

    [Fact]
    public void Bind_throws_when_property_expression_is_null()
    {
        var viewModel = Services.GetRequiredService<TestViewModel>();
        var binder = Services.GetRequiredService<Binder>();

        var callback = (IBinding b, EventArgs a) => { };
        binder.ValueChangedCallback = callback;

        Should.Throw<BindingException>(() => binder.Bind<TestViewModel, string>(viewModel, null!));
    }

    [Fact]
    public void Bind_throws_when_property_expression_is_not_property()
    {
        var viewModel = Services.GetRequiredService<TestViewModel>();
        var binder = Services.GetRequiredService<Binder>();

        var callback = (IBinding b, EventArgs a) => { };
        binder.ValueChangedCallback = callback;

        Should.Throw<BindingException>(() => binder.Bind(viewModel, x => x.PublicField));
    }

    [Fact]
    public void Bind_binds_property()
    {
        var bindingFactory = Services.GetMock<IBindingFactory>();
        var weakEventManager = Services.GetMock<IWeakEventManager>();
        var viewModel = Services.GetRequiredService<TestViewModel>();
        var binding = new Mock<IBinding>();

        bindingFactory.Setup(x => x.Create(viewModel, It.IsAny<PropertyInfo>(), weakEventManager.Object))
            .Returns(binding.Object)
            .Verifiable();

        binding.Setup(x => x.GetValue()).Returns("test").Verifiable();
        weakEventManager.Setup(
                x => x.AddWeakEventListener(
                    binding.Object,
                    nameof(Binding.BindingValueChanged),
                    It.IsAny<Action<IBinding, EventArgs>>()
                )
            )
            .Verifiable();
        weakEventManager.Setup(x => x.RemoveWeakEventListener(binding.Object));

        var callback = (IBinding b, EventArgs a) => { };

        var binder = Services.GetRequiredService<Binder>();
        binder.ValueChangedCallback = callback;

        var res = binder.Bind(viewModel, x => x.Test);
        res.ShouldBe("test");

        bindingFactory.Verify();
        binding.Verify();
        weakEventManager.Verify();
    }

    [Fact]
    public void Bind_binds_property_exactly_once()
    {
        var bindingFactory = Services.GetMock<IBindingFactory>();
        var weakEventManager = Services.GetMock<IWeakEventManager>();
        var viewModel = Services.GetRequiredService<TestViewModel>();
        var binding = new Mock<IBinding>();

        bindingFactory.Setup(x => x.Create(viewModel, It.IsAny<PropertyInfo>(), weakEventManager.Object))
            .Returns(binding.Object)
            .Verifiable();

        binding.Setup(x => x.GetValue()).Returns("test").Verifiable();
        weakEventManager.Setup(
                x => x.AddWeakEventListener(
                    binding.Object,
                    nameof(Binding.BindingValueChanged),
                    It.IsAny<Action<IBinding, EventArgs>>()
                )
            )
            .Verifiable();
        weakEventManager.Setup(x => x.RemoveWeakEventListener(binding.Object));

        var callback = (IBinding b, EventArgs a) => { };

        var binder = Services.GetRequiredService<Binder>();
        binder.ValueChangedCallback = callback;

        var res = binder.Bind(viewModel, x => x.Test);
        res.ShouldBe("test");

        res = binder.Bind(viewModel, x => x.Test);
        res.ShouldBe("test");

        weakEventManager.Verify(
            x => x.AddWeakEventListener(
                binding.Object,
                nameof(Binding.BindingValueChanged),
                It.IsAny<Action<IBinding, EventArgs>>()
            ),
            Times.Once
        );
        binding.Verify();
        bindingFactory.Verify();
        weakEventManager.Verify();
    }

    private class TestViewModel : ViewModelBase
    {
#pragma warning disable CA1051
#pragma warning disable CS0649
        public string? PublicField;
#pragma warning restore CS0649
#pragma warning restore CA1051
        public string? Test { get; set; }
    }
}

================================================
FILE: src/MvvmBlazor.Tests/Internal/Bindings/BindingFactoryTests.cs
================================================
namespace MvvmBlazor.Tests.Internal.Bindings;

public class BindingFactoryTests
{
    [Fact]
    public void Create_returns_binding()
    {
        var source = new Mock<INotifyPropertyChanged>();
        var propertyInfo = new Mock<PropertyInfo>();
        var wem = new Mock<IWeakEventManager>();

        var factory = new BindingFactory();
        using var res = factory.Create(source.Object, propertyInfo.Object, wem.Object);
        res.ShouldNotBeNull();
        res.PropertyInfo.ShouldBe(propertyInfo.Object);
        res.Source.ShouldBe(source.Object);
    }
}

================================================
FILE: src/MvvmBlazor.Tests/Internal/Bindings/BindingTests.cs
================================================
using System.Collections.ObjectModel;

namespace MvvmBlazor.Tests.Internal.Bindings;

public class BindingTests
{
    [Fact]
    public void Adds_collection_event_listener_when_initializing()
    {
        const string propertyName = "propertyName";

        var source = new Mock<INotifyPropertyChanged>();
        var collection = new Mock<INotifyCollectionChanged>();
        var wem = new Mock<IWeakEventManager>();
        var propertyInfo = new Mock<PropertyInfo>();
        propertyInfo.Setup(x => x.Name).Returns(propertyName);
        propertyInfo.Setup(x => x.PropertyType).Returns(typeof(INotifyCollectionChanged));
        propertyInfo.SetupSequence(x => x.GetValue(It.IsAny<INotifyPropertyChanged>(), null))
            .Returns(collection.Object);

        using var binding = new Binding(source.Object, propertyInfo.Object, wem.Object);
        binding.Initialize();

        wem.Verify(
            x => x.AddWeakEventListener(
                It.IsAny<INotifyCollectionChanged>(),
                It.IsAny<Action<INotifyCollectionChanged, NotifyCollectionChangedEventArgs>>()
            )
        );
    }

    [Fact]
    public void Adds_collection_event_listener_when_property_changes_to_not_null()
    {
        const string propertyName = "propertyName";

        var source = new Mock<INotifyPropertyChanged>();
        var collection = new Mock<INotifyCollectionChanged>();
        var wem = new Mock<IWeakEventManager>();
        var propertyInfo = new Mock<PropertyInfo>();
        propertyInfo.Setup(x => x.Name).Returns(propertyName);
        propertyInfo.Setup(x => x.PropertyType).Returns(typeof(INotifyCollectionChanged));
        wem.Setup(
                x => x.AddWeakEventListener(
                    It.IsAny<INotifyPropertyChanged>(),
                    It.IsAny<Action<INotifyPropertyChanged, PropertyChangedEventArgs>>()
                )
            )
            .Callback<INotifyPropertyChanged, Action<INotifyPropertyChanged, PropertyChangedEventArgs>>(
                (sender, action) => sender.PropertyChanged += (s, a) => action((INotifyPropertyChanged)s!, a)
            );
        propertyInfo.Setup(x => x.GetValue(It.IsAny<INotifyPropertyChanged>(), null)).Returns(collection.Object);

        using var binding = new Binding(source.Object, propertyInfo.Object, wem.Object);
        binding.Initialize();

        source.Raise(x => x.PropertyChanged += null, new PropertyChangedEventArgs(propertyName));
        wem.Verify(
            x => x.AddWeakEventListener(
                It.IsAny<INotifyCollectionChanged>(),
                It.IsAny<Action<INotifyCollectionChanged, NotifyCollectionChangedEventArgs>>()
            )
        );
    }

    [Fact]
    public void Dispose_removes_collection_listener()
    {
        const string propertyName = "propertyName";

        var source = new Mock<INotifyPropertyChanged>();
        var collection = new Mock<INotifyCollectionChanged>();
        var wem = new Mock<IWeakEventManager>();
        var propertyInfo = new Mock<PropertyInfo>();
        propertyInfo.Setup(x => x.Name).Returns(propertyName);
        propertyInfo.Setup(x => x.PropertyType).Returns(typeof(INotifyCollectionChanged));
        wem.Setup(
                x => x.AddWeakEventListener(
                    It.IsAny<INotifyPropertyChanged>(),
                    It.IsAny<Action<INotifyPropertyChanged, PropertyChangedEventArgs>>()
                )
            )
            .Callback<INotifyPropertyChanged, Action<INotifyPropertyChanged, PropertyChangedEventArgs>>(
                (sender, action) => sender.PropertyChanged += (s, a) => action((INotifyPropertyChanged)s!, a)
            );
        propertyInfo.SetupSequence(x => x.GetValue(It.IsAny<INotifyPropertyChanged>(), null))
            .Returns(collection.Object)
            .Returns((object?)null);

        using (var binding = new Binding(source.Object, propertyInfo.Object, wem.Object))
        {
            binding.Initialize();
        }

        wem.Verify(x => x.RemoveWeakEventListener(collection.Object));
    }

    [Fact]
    public void Dispose_removes_event_listener()
    {
        const string propertyName = "propertyName";

        var source = new Mock<INotifyPropertyChanged>();
        var collection = new Mock<INotifyCollectionChanged>();
        var wem = new Mock<IWeakEventManager>();
        var propertyInfo = new Mock<PropertyInfo>();
        propertyInfo.Setup(x => x.Name).Returns(propertyName);
        propertyInfo.Setup(x => x.PropertyType).Returns(typeof(INotifyCollectionChanged));
        wem.Setup(
                x => x.AddWeakEventListener(
                    It.IsAny<INotifyPropertyChanged>(),
                    It.IsAny<Action<INotifyPropertyChanged, PropertyChangedEventArgs>>()
                )
            )
            .Callback<INotifyPropertyChanged, Action<INotifyPropertyChanged, PropertyChangedEventArgs>>(
                (sender, action) => sender.PropertyChanged += (s, a) => action((INotifyPropertyChanged)s!, a)
            );
        propertyInfo.Setup(x => x.GetValue(It.IsAny<INotifyPropertyChanged>(), null)).Returns(collection.Object);

        using (var binding = new Binding(source.Object, propertyInfo.Object, wem.Object))
        {
            binding.Initialize();
        }

        wem.Verify(x => x.RemoveWeakEventListener(source.Object));
    }

    [Fact]
    public void Equals_not_different_source_and_property()
    {
        const string propertyName = "propertyName";

        var source1 = new Mock<INotifyPropertyChanged>();
        var source2 = new Mock<INotifyPropertyChanged>();

        var propertyInfo1 = new Mock<PropertyInfo>();
        propertyInfo1.Setup(x => x.Name).Returns(propertyName);
        var wem = new Mock<IWeakEventManager>();
        var propertyInfo2 = new Mock<PropertyInfo>();
        propertyInfo2.Setup(x => x.Name).Returns(propertyName + "Foo");

        using var binding1 = new Binding(source1.Object, propertyInfo1.Object, wem.Object);
        using var binding2 = new Binding(source2.Object, propertyInfo2.Object, wem.Object);

        binding1.GetHashCode().ShouldNotBe(binding2.GetHashCode());
        binding1.Equals(binding2).ShouldBeFalse();
    }

    [Fact]
    public void Equals_same_source_and_property()
    {
        const string propertyName = "propertyName";

        var source = new Mock<INotifyPropertyChanged>();
        var wem = new Mock<IWeakEventManager>();

        var propertyInfo = new Mock<PropertyInfo>();
        propertyInfo.Setup(x => x.Name).Returns(propertyName);

        using var binding1 = new Binding(source.Object, propertyInfo.Object, wem.Object);
        using var binding2 = new Binding(source.Object, propertyInfo.Object, wem.Object);

        binding1.GetHashCode().ShouldBe(binding2.GetHashCode());
        binding1.Equals(binding2).ShouldBeTrue();
    }

    [Fact]
    public void Ignores_PropertyChangedEvent_when_property_name_does_not_match()
    {
        const string propertyName = "propertyName";

        var source = new Mock<INotifyPropertyChanged>();
        var collection = new Mock<INotifyCollectionChanged>();
        var wem = new Mock<IWeakEventManager>();
        var propertyInfo = new Mock<PropertyInfo>();
        propertyInfo.Setup(x => x.Name).Returns(propertyName);
        propertyInfo.Setup(x => x.PropertyType).Returns(typeof(INotifyCollectionChanged));
        wem.Setup(
                x => x.AddWeakEventListener(
                    It.IsAny<INotifyPropertyChanged>(),
                    It.IsAny<Action<INotifyPropertyChanged, PropertyChangedEventArgs>>()
                )
            )
            .Callback<INotifyPropertyChanged, Action<INotifyPropertyChanged, PropertyChangedEventArgs>>(
                (sender, action) => sender.PropertyChanged += (s, a) => action((INotifyPropertyChanged)s!, a)
            );
        propertyInfo.Setup(x => x.GetValue(It.IsAny<INotifyPropertyChanged>(), null)).Returns(collection.Object);

        var bindingValueChangedRaised = false;
        using var binding = new Binding(source.Object, propertyInfo.Object, wem.Object);
        binding.Initialize();
        binding.BindingValueChanged += (s, e) => bindingValueChangedRaised = true;

        source.Raise(x => x.PropertyChanged += null, new PropertyChangedEventArgs("foo"));

        bindingValueChangedRaised.ShouldBeFalse();
    }

    [Fact]
    public void Does_not_raise_BindingValueChanged_when_uninitialized()
    {
        const string propertyName = "propertyName";

        var source = new Mock<INotifyPropertyChanged>();
        var wem = new Mock<IWeakEventManager>();
        var propertyInfo = new Mock<PropertyInfo>();
        propertyInfo.Setup(x => x.Name).Returns(propertyName);

        var hasChanged = false;
        using var binding = new Binding(source.Object, propertyInfo.Object, wem.Object);
        binding.BindingValueChanged += (sender, args) => { hasChanged = true; };

        source.Raise(x => x.PropertyChanged += null, new PropertyChangedEventArgs(propertyName));
        hasChanged.ShouldBeFalse();
    }

    [Fact]
    public void Raises_BindingValueChanged_when_collection_changed()
    {
        const string propertyName = "propertyName";

        var source = new Mock<INotifyPropertyChanged>();
        var collection = new Mock<ObservableCollection<object>>();
        var wem = new Mock<IWeakEventManager>();

        var propertyInfo = new Mock<PropertyInfo>();
        propertyInfo.Setup(x => x.Name).Returns(propertyName);
        propertyInfo.Setup(x => x.PropertyType).Returns(typeof(INotifyCollectionChanged));
        propertyInfo.Setup(x => x.GetValue(It.IsAny<object>(), It.IsAny<object[]>())).Returns(collection.Object);
        wem.Setup(
                x => x.AddWeakEventListener(
                    It.IsAny<INotifyCollectionChanged>(),
                    It.IsAny<Action<INotifyCollectionChanged, NotifyCollectionChangedEventArgs>>()
                )
            )
            .Callback<INotifyCollectionChanged, Action<INotifyCollectionChanged, NotifyCollectionChangedEventArgs>>(
                (sender, action) => sender.CollectionChanged += (s, a) => action((INotifyCollectionChanged)s!, a)
            );

        var hasChanged = false;
        using var binding = new Binding(source.Object, propertyInfo.Object, wem.Object);
        binding.Initialize();
        binding.BindingValueChanged += (sender, args) =>
        {
            sender.ShouldBe(binding);
            args.ShouldBe(EventArgs.Empty);
            hasChanged = true;
        };

        collection.Raise(
            x => x.CollectionChanged += null,
            new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new List<object> { new() })
        );

        hasChanged.ShouldBeTrue();
    }

    [Fact]
    public void Raises_BindingValueChanged_when_property_changed()
    {
        const string propertyName = "propertyName";

        var source = new Mock<INotifyPropertyChanged>();
        var wem = new Mock<IWeakEventManager>();
        var propertyInfo = new Mock<PropertyInfo>();
        propertyInfo.Setup(x => x.Name).Returns(propertyName);
        wem.Setup(
                x => x.AddWeakEventListener(
                    It.IsAny<INotifyPropertyChanged>(),
                    It.IsAny<Action<INotifyPropertyChanged, PropertyChangedEventArgs>>()
                )
            )
            .Callback<INotifyPropertyChanged, Action<INotifyPropertyChanged, PropertyChangedEventArgs>>(
                (sender, action) => sender.PropertyChanged += (s, a) => action((INotifyPropertyChanged)s!, a)
            );

        var hasChanged = false;
        using var binding = new Binding(source.Object, propertyInfo.Object, wem.Object);
        binding.Initialize();
        binding.BindingValueChanged += (sender, args) =>
        {
            sender.ShouldBe(binding);
            args.ShouldBe(EventArgs.Empty);
            hasChanged = true;
        };

        source.Raise(x => x.PropertyChanged += null, new PropertyChangedEventArgs(propertyName));

        hasChanged.ShouldBeTrue();
    }

    [Fact]
    public void Removes_collection_event_listener_when_property_changes_to_null()
    {
        const string propertyName = "propertyName";

        var source = new Mock<INotifyPropertyChanged>();
        var collection = new Mock<INotifyCollectionChanged>();
        var wem = new Mock<IWeakEventManager>();
        var propertyInfo = new Mock<PropertyInfo>();
        propertyInfo.Setup(x => x.Name).Returns(propertyName);
        propertyInfo.Setup(x => x.PropertyType).Returns(typeof(INotifyCollectionChanged));
        wem.Setup(
                x => x.AddWeakEventListener(
                    It.IsAny<INotifyPropertyChanged>(),
                    It.IsAny<Action<INotifyPropertyChanged, PropertyChangedEventArgs>>()
                )
            )
            .Callback<INotifyPropertyChanged, Action<INotifyPropertyChanged, PropertyChangedEventArgs>>(
                (sender, action) => sender.PropertyChanged += (s, a) => action((INotifyPropertyChanged)s!, a)
            );
        propertyInfo.SetupSequence(x => x.GetValue(It.IsAny<INotifyPropertyChanged>(), null))
            .Returns(collection.Object)
            .Returns((object?)null);

        using var binding = new Binding(source.Object, propertyInfo.Object, wem.Object);
        binding.Initialize();

        source.Raise(x => x.PropertyChanged += null, new PropertyChangedEventArgs(propertyName));
        wem.Verify(x => x.RemoveWeakEventListener(It.IsAny<INotifyCollectionChanged>()));
    }

    [Fact]
    public void Should_not_raise_BindingValueChanged_on_collection_when_uninitialized()
    {
        const string propertyName = "propertyName";

        var source = new Mock<INotifyPropertyChanged>();
        var collection = new Mock<ObservableCollection<object>>();
        var wem = new Mock<IWeakEventManager>();
        var propertyInfo = new Mock<PropertyInfo>();
        propertyInfo.Setup(x => x.Name).Returns(propertyName);
        propertyInfo.Setup(x => x.PropertyType).Returns(typeof(INotifyCollectionChanged));
        propertyInfo.Setup(x => x.GetValue(It.IsAny<object>(), It.IsAny<object[]>())).Returns(collection.Object);

        var hasChanged = false;
        using var binding = new Binding(source.Object, propertyInfo.Object, wem.Object);
        binding.BindingValueChanged += (_, __) => hasChanged = true;

        collection.Raise(
            x => x.CollectionChanged += null,
            new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new List<object> { new() })
        );

        hasChanged.ShouldBeFalse();
    }
}

================================================
FILE: src/MvvmBlazor.Tests/Internal/Parameters/ParameterCacheTests.cs
================================================
using ParameterInfo = MvvmBlazor.Internal.Parameters.ParameterInfo;

namespace MvvmBlazor.Tests.Internal.Parameters;

public class ParameterCacheTests
{
    [Fact]
    public void Get_gets_cached_entry()
    {
        var type = typeof(ParameterCacheTests);
        var parameterInfo = new ParameterInfo(new List<PropertyInfo>(), new List<PropertyInfo>());

        var cache = new ParameterCache();
        cache.Set(type, parameterInfo);

        cache.Get(type).ShouldBeSameAs(parameterInfo);
    }
}

================================================
FILE: src/MvvmBlazor.Tests/Internal/Parameters/ParameterInfoTests.cs
================================================
using ParameterInfo = MvvmBlazor.Internal.Parameters.ParameterInfo;

namespace MvvmBlazor.Tests.Internal.Parameters;

public class ParameterInfoTests
{
    private static Mock<PropertyInfo> GenerateProperty(string propertyName)
    {
        var property = new StrictMock<PropertyInfo>();
        property.SetupGet(x => x.Name).Returns(propertyName);
        property.SetupGet(x => x.DeclaringType).Returns(typeof(ParameterInfoTests));
        property.Setup(x => x.GetHashCode()).Returns(propertyName.GetHashCode(StringComparison.OrdinalIgnoreCase));
        property.Setup(x => x.Equals(It.IsAny<PropertyInfo>())).Returns<object?>((obj) => obj is PropertyInfo p && p.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase));
        return property;
    }

    [Fact]
    public void Ignores_missing_property_on_viewmodel()
    {
        var p1 = GenerateProperty("p1");
        var p2 = GenerateProperty("p2");
        var componentProperties = new List<PropertyInfo> { p1.Object, p2.Object };

        var vmp1 = GenerateProperty("p1");
        var viewModelProperties = new List<PropertyInfo> { vmp1.Object };

        var info = new ParameterInfo(componentProperties, viewModelProperties);
        info.Parameters.ShouldNotBeNull();
        info.Parameters.Count.ShouldBe(1);
        info.Parameters.ElementAt(0).Key.ShouldBe(p1.Object);
        info.Parameters.ElementAt(0).Value.ShouldBe(vmp1.Object);
    }

    [Fact]
    public void Throws_for_missing_property_on_component()
    {
        var p1 = GenerateProperty("p1");
        var p2 = GenerateProperty("p2");
        var componentProperties = new List<PropertyInfo> { p2.Object };

        var vmp1 = GenerateProperty("p1");
        var viewModelProperties = new List<PropertyInfo> { vmp1.Object };

        Should.Throw<ParameterException>(() => new ParameterInfo(componentProperties, viewModelProperties));
    }

    [Fact]
    public void Sorts_properties()
    {
        var p1 = GenerateProperty("p1");
        var p2 = GenerateProperty("p2");
        var componentProperties = new List<PropertyInfo> { p1.Object, p2.Object };

        var vmp1 = GenerateProperty("p1");
        var vmp2 = GenerateProperty("p2");
        var viewModelProperties = new List<PropertyInfo> { vmp2.Object, vmp1.Object };

        var info = new ParameterInfo(componentProperties, viewModelProperties);
        info.Parameters.ShouldNotBeNull();
        info.Parameters.Count.ShouldBe(2);
        info.Parameters.ElementAt(0).Key.ShouldBe(p1.Object);
        info.Parameters.ElementAt(0).Value.ShouldBe(vmp1.Object);
        info.Parameters.ElementAt(1).Key.ShouldBe(p2.Object);
        info.Parameters.ElementAt(1).Value.ShouldBe(vmp2.Object);
    }
}

================================================
FILE: src/MvvmBlazor.Tests/Internal/Parameters/ParameterResolverTests.cs
================================================
namespace MvvmBlazor.Tests.Internal.Parameters;

public class ParameterResolverTests : UnitTest
{
    public ParameterResolverTests(ITestOutputHelper outputHelper) : base(outputHelper) { }

    protected override void RegisterServices(IServiceCollection services)
    {
        services.Mock<IParameterCache>();
        services.Provide<ParameterResolver>();
    }

    [Fact]
    public void ResolveParameters_ignores_properties_without_attribute()
    {
        var resolver = Services.GetRequiredService<ParameterResolver>();

        var res = resolver.ResolveParameters(typeof(S3), typeof(S3));
        res.Parameters.Count.ShouldBe(1);
        res.Parameters.ElementAt(0).Key.Name.ShouldBe(nameof(S3.Test));
        res.Parameters.ElementAt(0).Key.PropertyType.ShouldBe(typeof(string));
    }

    [Fact]
    public void ResolveParameters_ignores_properties_without_public_setter()
    {
        var resolver = Services.GetRequiredService<ParameterResolver>();

        var res = resolver.ResolveParameters(typeof(S4), typeof(S4));
        res.Parameters.Count.ShouldBe(1);
        res.Parameters.ElementAt(0).Key.Name.ShouldBe(nameof(S4.Test));
        res.Parameters.ElementAt(0).Key.PropertyType.ShouldBe(typeof(string));
    }

    [Fact]
    public void ResolveParameters_resolves_multiple_parameters()
    {
        var resolver = Services.GetRequiredService<ParameterResolver>();

        var res = resolver.ResolveParameters(typeof(S2), typeof(S2));
        res.Parameters.Count.ShouldBe(2);
        res.Parameters.ElementAt(0).Key.Name.ShouldBe(nameof(S2.Foo));
        res.Parameters.ElementAt(0).Key.PropertyType.ShouldBe(typeof(int));
        res.Parameters.ElementAt(1).Key.Name.ShouldBe(nameof(S2.Test));
        res.Parameters.ElementAt(1).Key.PropertyType.ShouldBe(typeof(string));
    }

    [Fact]
    public void ResolveParameters_resolves_single_parameter()
    {
        var resolver = Services.GetRequiredService<ParameterResolver>();

        var res = resolver.ResolveParameters(typeof(S1), typeof(S1));
        res.Parameters.Count.ShouldBe(1);
        res.Parameters.ElementAt(0).Key.Name.ShouldBe(nameof(S1.Test));
        res.Parameters.ElementAt(0).Key.PropertyType.ShouldBe(typeof(string));
    }

    [Fact]
    public void ResolveParameters_resolves_cascading_parameter()
    {
        var resolver = Services.GetRequiredService<ParameterResolver>();

        var res = resolver.ResolveParameters(typeof(S5), typeof(S5));
        res.Parameters.Count.ShouldBe(1);
        res.Parameters.ElementAt(0).Key.Name.ShouldBe(nameof(S5.Test));
        res.Parameters.ElementAt(0).Key.PropertyType.ShouldBe(typeof(string));
    }

    private class S1
    {
        [Parameter] public string? Test { get; set; }
    }

    private class S2
    {
        [Parameter] public string? Test { get; set; }

        [Parameter] public int Foo { get; set; }
    }

    private class S3
    {
        [Parameter] public string? Test { get; set; }

        public int Foo { get; set; }
    }

    private class S4
    {
        [Parameter] public string? Test { get; set; }

        public int Foo { get; }
        public int Doo { get; private set; }
        public int Boo { get; internal set; }
        public int Loo { get; protected set; }
        public int Moo { get; protected internal set; }
    }

    private class S5
    {
        [CascadingParameter] public string? Test { get; set; }
    }
}

================================================
FILE: src/MvvmBlazor.Tests/Internal/Parameters/ViewModelParameterSetterTests.cs
================================================
using System.Globalization;
using ParameterInfo = MvvmBlazor.Internal.Parameters.ParameterInfo;

namespace MvvmBlazor.Tests.Internal.Parameters;

public class ViewModelParameterSetterTests : UnitTest
{
    private static readonly StronglyTypedParameter TestParameter = new();

    public ViewModelParameterSetterTests(ITestOutputHelper outputHelper) : base(outputHelper) { }

    protected override void Registe
Download .txt
gitextract_455vp84t/

├── .editorconfig
├── .gitattributes
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug.yml
│   │   ├── config.yml
│   │   ├── feature.yml
│   │   ├── other.yml
│   │   └── question.yml
│   └── workflows/
│       ├── build.yaml
│       └── release.yaml
├── .gitignore
├── LICENSE
├── README.md
├── samples/
│   ├── BlazorClientsideSample.Client/
│   │   ├── App.razor
│   │   ├── BlazorClientsideSample.Client.csproj
│   │   ├── Program.cs
│   │   ├── Properties/
│   │   │   └── launchSettings.json
│   │   ├── Services/
│   │   │   └── WeatherForecastGetter.cs
│   │   ├── _Imports.razor
│   │   └── wwwroot/
│   │       └── index.html
│   ├── BlazorClientsideSample.Server/
│   │   ├── BlazorClientsideSample.Server.csproj
│   │   ├── Controllers/
│   │   │   └── WeatherForecastController.cs
│   │   ├── Program.cs
│   │   ├── Properties/
│   │   │   └── launchSettings.json
│   │   └── Startup.cs
│   ├── BlazorSample.Components/
│   │   ├── BlazorSample.Components.csproj
│   │   ├── Extensions/
│   │   │   └── ServiceCollectionExtensions.cs
│   │   ├── Pages/
│   │   │   ├── Clock.razor
│   │   │   ├── Counter.razor
│   │   │   ├── Index.razor
│   │   │   ├── Parameters.razor
│   │   │   ├── TypedParameters.razor
│   │   │   └── WeatherForecasts.razor
│   │   ├── Shared/
│   │   │   ├── CascadingComponent.razor
│   │   │   ├── CustomBaseComponent.cs
│   │   │   ├── MainLayout.razor
│   │   │   └── Navbar.razor
│   │   ├── _Imports.razor
│   │   └── wwwroot/
│   │       └── site.css
│   ├── BlazorSample.Domain/
│   │   ├── BlazorSample.Domain.csproj
│   │   ├── Converters/
│   │   │   └── IdTypeConverter.cs
│   │   ├── Entities/
│   │   │   ├── IdType.cs
│   │   │   └── WeatherForecastEntity.cs
│   │   ├── Extensions/
│   │   │   └── ServiceCollectionExtensions.cs
│   │   └── Services/
│   │       ├── IWeatherForecastGetter.cs
│   │       └── Navbar/
│   │           ├── NavbarItem.cs
│   │           └── NavbarService.cs
│   ├── BlazorSample.ViewModels/
│   │   ├── BlazorSample.ViewModels.csproj
│   │   ├── CascadingViewModel.cs
│   │   ├── ClockViewModel.cs
│   │   ├── CounterViewModel.cs
│   │   ├── Extensions/
│   │   │   └── ServiceCollectionExtensions.cs
│   │   ├── GlobalUsings.cs
│   │   ├── Navbar/
│   │   │   ├── NavbarItemViewModel.cs
│   │   │   └── NavbarViewModel.cs
│   │   ├── ParametersViewModel.cs
│   │   ├── TypedParametersViewModel.cs
│   │   ├── WeatherForecastViewModel.cs
│   │   └── WeatherForecastsViewModel.cs
│   └── BlazorServersideSample/
│       ├── App.razor
│       ├── BlazorServersideSample.csproj
│       ├── Pages/
│       │   ├── Error.razor
│       │   ├── _Host.cshtml
│       │   └── _Imports.razor
│       ├── Program.cs
│       ├── Properties/
│       │   └── launchSettings.json
│       ├── Services/
│       │   └── WeatherForecastGetter.cs
│       ├── Startup.cs
│       ├── _Imports.razor
│       ├── appsettings.Development.json
│       └── appsettings.json
└── src/
    ├── .editorconfig
    ├── Directory.Build.props
    ├── MvvmBlazor/
    │   ├── MvvmBlazor.csproj
    │   └── Properties/
    │       └── AssemblyInfo.cs
    ├── MvvmBlazor.CodeGenerators/
    │   ├── AnalyzerReleases.Shipped.md
    │   ├── AnalyzerReleases.Unshipped.md
    │   ├── Components/
    │   │   ├── MvvmComponentClassContext.cs
    │   │   ├── MvvmComponentGenerator.cs
    │   │   └── MvvmComponentSyntaxReceiver.cs
    │   ├── Extensions/
    │   │   ├── StringBuilderExtensions.cs
    │   │   └── SymbolExtensions.cs
    │   ├── GlobalUsings.cs
    │   ├── MvvmBlazor.CodeGenerators.csproj
    │   ├── NotifyPropertyChanged/
    │   │   ├── NotifyPropertyChangedContext.cs
    │   │   ├── NotifyPropertyChangedGenerator.cs
    │   │   └── NotifyPropertyChangedSyntaxReceiver.cs
    │   └── Properties/
    │       └── AssemblyInfo.cs
    ├── MvvmBlazor.Core/
    │   ├── Components/
    │   │   ├── MvvmComponentAttribute.cs
    │   │   ├── MvvmComponentBase.cs
    │   │   └── MvvmComponentBaseT.cs
    │   ├── Extensions/
    │   │   └── ServiceCollectionExtensions.cs
    │   ├── GlobalUsings.cs
    │   ├── Internal/
    │   │   ├── Bindings/
    │   │   │   ├── Binder.cs
    │   │   │   ├── Binding.cs
    │   │   │   ├── BindingException.cs
    │   │   │   └── BindingFactory.cs
    │   │   ├── Parameters/
    │   │   │   ├── ParameterCache.cs
    │   │   │   ├── ParameterException.cs
    │   │   │   ├── ParameterInfo.cs
    │   │   │   ├── ParameterResolver.cs
    │   │   │   └── ViewModelParameterSetter.cs
    │   │   └── WeakEventListener/
    │   │       ├── WeakEventListener.cs
    │   │       └── WeakEventManager.cs
    │   ├── MvvmBlazor.Core.csproj
    │   ├── NotifyAttribute.cs
    │   ├── Properties/
    │   │   ├── AssemblyInfo.cs
    │   │   └── GlobalSuppressions.cs
    │   └── ViewModel/
    │       └── ViewModelBase.cs
    ├── MvvmBlazor.Tests/
    │   ├── .editorconfig
    │   ├── Abstractions/
    │   │   ├── StrictMock.cs
    │   │   └── UnitTest.cs
    │   ├── Components/
    │   │   ├── MvvmComponentBaseTTests.cs
    │   │   ├── MvvmComponentBaseTests.cs
    │   │   └── TestViewModel.cs
    │   ├── Extensions/
    │   │   ├── ServiceCollectionExtensions.cs
    │   │   └── ServiceProviderExtensions.cs
    │   ├── Generators/
    │   │   ├── MvvmComponentGeneratorTests.cs
    │   │   └── NotifyPropertyChangedGeneratorTests.cs
    │   ├── GlobalUsings.cs
    │   ├── Internal/
    │   │   ├── Bindings/
    │   │   │   ├── BinderTests.cs
    │   │   │   ├── BindingFactoryTests.cs
    │   │   │   └── BindingTests.cs
    │   │   ├── Parameters/
    │   │   │   ├── ParameterCacheTests.cs
    │   │   │   ├── ParameterInfoTests.cs
    │   │   │   ├── ParameterResolverTests.cs
    │   │   │   └── ViewModelParameterSetterTests.cs
    │   │   └── WeakEventListener/
    │   │       └── WeakEventListenerTests.cs
    │   ├── MvvmBlazor.Tests.csproj
    │   ├── Properties/
    │   │   └── AssemblyInfo.cs
    │   ├── ViewModel/
    │   │   └── ViewModelBaseTests.cs
    │   └── xunit.runner.json
    ├── MvvmBlazor.sln
    └── MvvmBlazor.sln.DotSettings
Download .txt
SYMBOL INDEX (346 symbols across 70 files)

FILE: samples/BlazorClientsideSample.Client/Program.cs
  class Program (line 10) | public class Program
    method Main (line 12) | public static async Task Main(string[] args)

FILE: samples/BlazorClientsideSample.Client/Services/WeatherForecastGetter.cs
  class WeatherForecastGetter (line 7) | public class WeatherForecastGetter : IWeatherForecastGetter
    method WeatherForecastGetter (line 11) | public WeatherForecastGetter(HttpClient httpClient)
    method GetForecasts (line 16) | public Task<IEnumerable<WeatherForecastEntity>> GetForecasts()

FILE: samples/BlazorClientsideSample.Server/Controllers/WeatherForecastController.cs
  class WeatherForecastController (line 6) | [ApiController]
    method Get (line 15) | [HttpGet]

FILE: samples/BlazorClientsideSample.Server/Program.cs
  class Program (line 5) | public class Program
    method Main (line 7) | public static void Main(string[] args)
    method BuildWebHost (line 12) | public static IWebHost BuildWebHost(string[] args)

FILE: samples/BlazorClientsideSample.Server/Startup.cs
  class Startup (line 5) | public class Startup
    method ConfigureServices (line 9) | public void ConfigureServices(IServiceCollection services)
    method Configure (line 21) | public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

FILE: samples/BlazorSample.Components/Extensions/ServiceCollectionExtensions.cs
  class ServiceCollectionExtensions (line 9) | public static class ServiceCollectionExtensions
    method AddComponents (line 11) | public static IServiceCollection AddComponents(this IServiceCollection...

FILE: samples/BlazorSample.Components/Shared/CustomBaseComponent.cs
  class CustomBaseComponent (line 6) | [MvvmComponent]
  class CustomBaseComponent (line 12) | [MvvmComponent]

FILE: samples/BlazorSample.Domain/Converters/IdTypeConverter.cs
  class IdTypeConverter (line 7) | public class IdTypeConverter : TypeConverter
    method CanConvertFrom (line 9) | public override bool CanConvertFrom(ITypeDescriptorContext? context, T...
    method ConvertTo (line 14) | public override object? ConvertTo(

FILE: samples/BlazorSample.Domain/Entities/IdType.cs
  type IdType (line 7) | [TypeConverter(typeof(IdTypeConverter))]

FILE: samples/BlazorSample.Domain/Entities/WeatherForecastEntity.cs
  class WeatherForecastEntity (line 3) | public class WeatherForecastEntity

FILE: samples/BlazorSample.Domain/Extensions/ServiceCollectionExtensions.cs
  class ServiceCollectionExtensions (line 7) | public static class ServiceCollectionExtensions
    method AddNavigationItem (line 9) | public static IServiceCollection AddNavigationItem<TPage>(
    method AddDomain (line 18) | public static IServiceCollection AddDomain(this IServiceCollection ser...

FILE: samples/BlazorSample.Domain/Services/IWeatherForecastGetter.cs
  type IWeatherForecastGetter (line 5) | public interface IWeatherForecastGetter
    method GetForecasts (line 7) | Task<IEnumerable<WeatherForecastEntity>> GetForecasts();

FILE: samples/BlazorSample.Domain/Services/Navbar/NavbarItem.cs
  class NavbarItem (line 3) | public class NavbarItem
    method NavbarItem (line 10) | public NavbarItem(Type page, string displayName, string? icon)

FILE: samples/BlazorSample.Domain/Services/Navbar/NavbarService.cs
  type INavbarService (line 7) | public interface INavbarService
  class NavbarService (line 12) | public class NavbarService : INavbarService
    method NavbarService (line 16) | public NavbarService(IEnumerable<NavbarItem> navbarItems)
    method LoadComponents (line 24) | private void LoadComponents(IEnumerable<NavbarItem> navbarItems)

FILE: samples/BlazorSample.ViewModels/CascadingViewModel.cs
  class CascadingViewModel (line 3) | public class CascadingViewModel : ViewModelBase

FILE: samples/BlazorSample.ViewModels/ClockViewModel.cs
  class ClockViewModel (line 5) | public partial class ClockViewModel : ViewModelBase, IDisposable
    method ClockViewModel (line 11) | public ClockViewModel()
    method Dispose (line 18) | public void Dispose()
    method TimerOnElapsed (line 24) | private void TimerOnElapsed(object? sender, ElapsedEventArgs e)
    method Dispose (line 29) | protected virtual void Dispose(bool disposing)

FILE: samples/BlazorSample.ViewModels/CounterViewModel.cs
  class CounterViewModel (line 3) | public partial class CounterViewModel : ViewModelBase
    method IncrementCount (line 7) | public void IncrementCount()

FILE: samples/BlazorSample.ViewModels/Extensions/ServiceCollectionExtensions.cs
  class ServiceCollectionExtensions (line 5) | public static class ServiceCollectionExtensions
    method AddViewModels (line 7) | public static IServiceCollection AddViewModels(this IServiceCollection...

FILE: samples/BlazorSample.ViewModels/Navbar/NavbarItemViewModel.cs
  class NavbarItemViewModel (line 3) | public class NavbarItemViewModel : ViewModelBase
    method NavbarItemViewModel (line 19) | public NavbarItemViewModel(string displayName, string template, string...

FILE: samples/BlazorSample.ViewModels/Navbar/NavbarViewModel.cs
  class NavbarViewModel (line 5) | public class NavbarViewModel : ViewModelBase
    method NavbarViewModel (line 17) | public NavbarViewModel(INavbarService navbarService)
    method ToggleMenu (line 24) | public void ToggleMenu()
    method UpdateActiveItem (line 29) | private void UpdateActiveItem(NavigationManager navigationManager)
    method OnInitialized (line 44) | public override void OnInitialized()

FILE: samples/BlazorSample.ViewModels/ParametersViewModel.cs
  class ParametersViewModel (line 3) | public class ParametersViewModel : ViewModelBase
    method NavigateToNewName (line 11) | public void NavigateToNewName()
    method OnInitialized (line 21) | public override void OnInitialized()

FILE: samples/BlazorSample.ViewModels/TypedParametersViewModel.cs
  class TypedParametersViewModel (line 3) | public class TypedParametersViewModel : ViewModelBase
    method NavigateToRandomId (line 9) | public void NavigateToRandomId()
    method OnInitialized (line 14) | public override void OnInitialized()
    method OnParametersSet (line 19) | public override void OnParametersSet()

FILE: samples/BlazorSample.ViewModels/WeatherForecastViewModel.cs
  class WeatherForecastViewModel (line 3) | public class WeatherForecastViewModel : ViewModelBase
    method WeatherForecastViewModel (line 26) | public WeatherForecastViewModel(WeatherForecastEntity weatherForecastE...

FILE: samples/BlazorSample.ViewModels/WeatherForecastsViewModel.cs
  class WeatherForecastsViewModel (line 3) | public partial class WeatherForecastsViewModel : ViewModelBase
    method WeatherForecastsViewModel (line 9) | public WeatherForecastsViewModel(IWeatherForecastGetter weatherForecas...
    method OnInitializedAsync (line 14) | public override async Task OnInitializedAsync()
    method RandomizeData (line 25) | public void RandomizeData()

FILE: samples/BlazorServersideSample/Program.cs
  class Program (line 3) | public class Program
    method Main (line 5) | public static void Main(string[] args)
    method CreateHostBuilder (line 10) | public static IHostBuilder CreateHostBuilder(string[] args)

FILE: samples/BlazorServersideSample/Services/WeatherForecastGetter.cs
  class WeatherForecastGetter (line 6) | public class WeatherForecastGetter : IWeatherForecastGetter
    method GetForecasts (line 13) | public Task<IEnumerable<WeatherForecastEntity>> GetForecasts()

FILE: samples/BlazorServersideSample/Startup.cs
  class Startup (line 9) | public class Startup
    method Startup (line 13) | public Startup(IConfiguration configuration)
    method ConfigureServices (line 20) | public void ConfigureServices(IServiceCollection services)
    method Configure (line 33) | public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

FILE: src/MvvmBlazor.CodeGenerators/Components/MvvmComponentClassContext.cs
  class MvvmComponentClassContext (line 3) | internal class MvvmComponentClassContext
    method MvvmComponentClassContext (line 8) | public MvvmComponentClassContext(ClassDeclarationSyntax componentClass...

FILE: src/MvvmBlazor.CodeGenerators/Components/MvvmComponentGenerator.cs
  class MvvmComponentGenerator (line 3) | [Generator]
    method Execute (line 34) | public void Execute(GeneratorExecutionContext context)
    method Initialize (line 46) | public void Initialize(GeneratorInitializationContext context)
    method ProcessComponent (line 51) | private static void ProcessComponent(
    method AddComponent (line 99) | private static void AddComponent(
    method AddGenericComponent (line 108) | private static void AddGenericComponent(
    method GenerateComponentCode (line 138) | private static string GenerateComponentCode(MvvmComponentClassContext ...
    method GenerateGenericComponentCode (line 272) | private static string GenerateGenericComponentCode(MvvmComponentClassC...

FILE: src/MvvmBlazor.CodeGenerators/Components/MvvmComponentSyntaxReceiver.cs
  class MvvmComponentSyntaxReceiver (line 3) | internal class MvvmComponentSyntaxReceiver : ISyntaxContextReceiver
    method OnVisitSyntaxNode (line 7) | public void OnVisitSyntaxNode(GeneratorSyntaxContext context)

FILE: src/MvvmBlazor.CodeGenerators/Extensions/StringBuilderExtensions.cs
  class StringBuilderExtensions (line 5) | internal static class StringBuilderExtensions
    method AppendLineFormat (line 7) | public static void AppendLineFormat(this StringBuilder stringBuilder, ...
    method AppendLineFormat (line 13) | public static void AppendLineFormat(this StringBuilder stringBuilder, ...

FILE: src/MvvmBlazor.CodeGenerators/Extensions/SymbolExtensions.cs
  class SymbolExtensions (line 3) | internal static class SymbolExtensions
    method GetMetadataName (line 5) | public static string GetMetadataName(this ISymbol symbol)
    method InheritsFrom (line 10) | public static bool InheritsFrom(this ITypeSymbol symbol, ISymbol baseT...

FILE: src/MvvmBlazor.CodeGenerators/NotifyPropertyChanged/NotifyPropertyChangedContext.cs
  class NotifyPropertyChangedContext (line 3) | internal class NotifyPropertyChangedContext
    method NotifyPropertyChangedContext (line 8) | public NotifyPropertyChangedContext(FieldDeclarationSyntax field, IFie...

FILE: src/MvvmBlazor.CodeGenerators/NotifyPropertyChanged/NotifyPropertyChangedGenerator.cs
  class NotifyPropertyChangedGenerator (line 3) | [Generator]
    method Initialize (line 24) | public void Initialize(GeneratorInitializationContext context)
    method Execute (line 29) | public void Execute(GeneratorExecutionContext context)
    method ProcessViewModel (line 40) | private static void ProcessViewModel(
    method AddViewModel (line 81) | private static void AddViewModel(
    method GenerateViewModelCode (line 94) | private static string GenerateViewModelCode(
    method GetPropertyName (line 136) | private static string GetPropertyName(string fieldName)

FILE: src/MvvmBlazor.CodeGenerators/NotifyPropertyChanged/NotifyPropertyChangedSyntaxReceiver.cs
  class NotifyPropertyChangedSyntaxReceiver (line 3) | internal class NotifyPropertyChangedSyntaxReceiver : ISyntaxContextReceiver
    method OnVisitSyntaxNode (line 7) | public void OnVisitSyntaxNode(GeneratorSyntaxContext context)

FILE: src/MvvmBlazor.Core/Components/MvvmComponentAttribute.cs
  class MvvmComponentAttribute (line 5) | [AttributeUsage(AttributeTargets.Class)]

FILE: src/MvvmBlazor.Core/Components/MvvmComponentBase.cs
  class MvvmComponentBase (line 3) | [MvvmComponent]

FILE: src/MvvmBlazor.Core/Components/MvvmComponentBaseT.cs
  class MvvmComponentBase (line 3) | [MvvmComponent]

FILE: src/MvvmBlazor.Core/Extensions/ServiceCollectionExtensions.cs
  class ServiceCollectionExtensions (line 6) | public static class ServiceCollectionExtensions
    method AddMvvm (line 8) | public static IServiceCollection AddMvvm(this IServiceCollection servi...

FILE: src/MvvmBlazor.Core/Internal/Bindings/Binder.cs
  type IBinder (line 3) | public interface IBinder
    method Bind (line 7) | TValue Bind<TViewModel, TValue>(TViewModel viewModel, Expression<Func<...
  class Binder (line 11) | internal class Binder : IBinder, IDisposable
    method Binder (line 18) | public Binder(IBindingFactory bindingFactory, IWeakEventManager weakEv...
    method Bind (line 26) | public TValue Bind<TViewModel, TValue>(
    method ValidateAndResolveBindingContext (line 53) | protected static PropertyInfo ValidateAndResolveBindingContext<TViewMo...
    method Dispose (line 82) | public void Dispose()
    method Dispose (line 88) | protected virtual void Dispose(bool disposing)
    method DisposeBindings (line 99) | private void DisposeBindings()
    method ThrowIfDisposed (line 108) | private void ThrowIfDisposed()

FILE: src/MvvmBlazor.Core/Internal/Bindings/Binding.cs
  type IBinding (line 3) | public interface IBinding : IDisposable
    method Initialize (line 8) | void Initialize();
    method GetValue (line 9) | object GetValue();
  class Binding (line 12) | internal class Binding : IBinding
    method Binding (line 18) | public Binding(INotifyPropertyChanged source, PropertyInfo propertyInf...
    method Initialize (line 30) | public void Initialize()
    method GetValue (line 37) | public object GetValue()
    method AddCollectionBindings (line 42) | private void AddCollectionBindings()
    method SourceOnPropertyChanged (line 53) | private void SourceOnPropertyChanged(object sender, PropertyChangedEve...
    method CollectionOnCollectionChanged (line 83) | private void CollectionOnCollectionChanged(object sender, NotifyCollec...
    method Dispose (line 90) | public void Dispose()
    method Dispose (line 96) | protected virtual void Dispose(bool disposing)
    method ToString (line 113) | public override string ToString()
    method Equals (line 118) | public override bool Equals(object? obj)
    method GetHashCode (line 123) | public override int GetHashCode()

FILE: src/MvvmBlazor.Core/Internal/Bindings/BindingException.cs
  class BindingException (line 3) | public class BindingException : Exception
    method BindingException (line 5) | public BindingException() { }
    method BindingException (line 7) | protected BindingException(SerializationInfo info, StreamingContext co...
    method BindingException (line 9) | public BindingException(string message) : base(message) { }
    method BindingException (line 11) | public BindingException(string message, Exception innerException) : ba...

FILE: src/MvvmBlazor.Core/Internal/Bindings/BindingFactory.cs
  type IBindingFactory (line 3) | internal interface IBindingFactory
    method Create (line 5) | IBinding Create(INotifyPropertyChanged source, PropertyInfo propertyIn...
  class BindingFactory (line 8) | internal class BindingFactory : IBindingFactory
    method Create (line 10) | public IBinding Create(INotifyPropertyChanged source, PropertyInfo pro...

FILE: src/MvvmBlazor.Core/Internal/Parameters/ParameterCache.cs
  type IParameterCache (line 5) | internal interface IParameterCache
    method Get (line 7) | ParameterInfo? Get(Type type);
    method Set (line 8) | void Set(Type type, ParameterInfo info);
  class ParameterCache (line 11) | internal class ParameterCache : IParameterCache
    method Get (line 15) | public ParameterInfo? Get(Type type)
    method Set (line 20) | public void Set(Type type, ParameterInfo info)

FILE: src/MvvmBlazor.Core/Internal/Parameters/ParameterException.cs
  class ParameterException (line 3) | [Serializable]
    method ParameterException (line 6) | public ParameterException() { }
    method ParameterException (line 7) | protected ParameterException(SerializationInfo info, StreamingContext ...
    method ParameterException (line 8) | public ParameterException(string? message) : base(message) { }
    method ParameterException (line 9) | public ParameterException(string? message, Exception? innerException) ...

FILE: src/MvvmBlazor.Core/Internal/Parameters/ParameterInfo.cs
  type ParameterInfo (line 3) | internal record ParameterInfo

FILE: src/MvvmBlazor.Core/Internal/Parameters/ParameterResolver.cs
  type IParameterResolver (line 3) | internal interface IParameterResolver
    method ResolveParameters (line 5) | ParameterInfo ResolveParameters(Type componentType, Type viewModelType);
  class ParameterResolver (line 8) | internal class ParameterResolver : IParameterResolver
    method ParameterResolver (line 12) | public ParameterResolver(IParameterCache parameterCache)
    method ResolveParameters (line 17) | public ParameterInfo ResolveParameters(Type componentType, Type viewMo...
    method ResolveTypeParameters (line 34) | private static IEnumerable<PropertyInfo> ResolveTypeParameters(Type me...

FILE: src/MvvmBlazor.Core/Internal/Parameters/ViewModelParameterSetter.cs
  type IViewModelParameterSetter (line 3) | public interface IViewModelParameterSetter
    method ResolveAndSet (line 5) | void ResolveAndSet(ComponentBase component, ViewModelBase viewModel);
  class ViewModelParameterSetter (line 8) | internal class ViewModelParameterSetter : IViewModelParameterSetter
    method ViewModelParameterSetter (line 12) | public ViewModelParameterSetter(IParameterResolver parameterResolver)
    method ResolveAndSet (line 17) | public void ResolveAndSet(ComponentBase component, ViewModelBase viewM...
    method ConvertValue (line 36) | private static object? ConvertValue(Type componentType, Type viewModel...

FILE: src/MvvmBlazor.Core/Internal/WeakEventListener/WeakEventListener.cs
  type IWeakEventListener (line 3) | internal interface IWeakEventListener
    method StopListening (line 8) | void StopListening();
  class WeakEventListenerBase (line 11) | internal abstract class WeakEventListenerBase<T, TArgs> : IWeakEventList...
    method WeakEventListenerBase (line 16) | protected WeakEventListenerBase(T source, Action<T, TArgs> handler)
    method StopListening (line 53) | public void StopListening()
    method HandleEvent (line 61) | protected void HandleEvent(object sender, TArgs e)
    method StopListening (line 73) | protected abstract void StopListening(T source);
  class TypedWeakEventListener (line 76) | internal class TypedWeakEventListener<T, TArgs> : WeakEventListenerBase<...
    method TypedWeakEventListener (line 81) | public TypedWeakEventListener(
    method StopListening (line 94) | protected override void StopListening(T source)
  class PropertyChangedWeakEventListener (line 100) | internal class PropertyChangedWeakEventListener<T> : WeakEventListenerBa...
    method PropertyChangedWeakEventListener (line 103) | public PropertyChangedWeakEventListener(T source, Action<T, PropertyCh...
    method StopListening (line 111) | protected override void StopListening(T source)
  class CollectionChangedWeakEventListener (line 117) | internal class CollectionChangedWeakEventListener<T> : WeakEventListener...
    method CollectionChangedWeakEventListener (line 120) | public CollectionChangedWeakEventListener(T source, Action<T, NotifyCo...
    method StopListening (line 128) | protected override void StopListening(T source)
  class WeakEventListener (line 134) | internal class WeakEventListener<T, TArgs> : WeakEventListenerBase<T, TA...
    method WeakEventListener (line 138) | public WeakEventListener(T source, string eventName, Action<T, TArgs> ...
    method StopListening (line 155) | protected override void StopListening(T source)

FILE: src/MvvmBlazor.Core/Internal/WeakEventListener/WeakEventManager.cs
  type IWeakEventManager (line 3) | internal interface IWeakEventManager
    method AddWeakEventListener (line 8) | void AddWeakEventListener<T, TArgs>(T source, string eventName, Action...
    method AddWeakEventListener (line 14) | void AddWeakEventListener<T>(T source, Action<T, PropertyChangedEventA...
    method AddWeakEventListener (line 20) | void AddWeakEventListener<T>(T source, Action<T, NotifyCollectionChang...
    method RemoveWeakEventListener (line 26) | void RemoveWeakEventListener<T>(T source) where T : class;
    method ClearWeakEventListeners (line 31) | void ClearWeakEventListeners();
  class WeakEventManager (line 34) | public class WeakEventManager : IWeakEventManager
    method AddWeakEventListener (line 41) | public void AddWeakEventListener<T, TArgs>(T source, string eventName,...
    method AddWeakEventListener (line 52) | public void AddWeakEventListener<T>(T source, Action<T, PropertyChange...
    method AddWeakEventListener (line 63) | public void AddWeakEventListener<T>(T source, Action<T, NotifyCollecti...
    method RemoveWeakEventListener (line 74) | public void RemoveWeakEventListener<T>(T source) where T : class
    method ClearWeakEventListeners (line 96) | public void ClearWeakEventListeners()

FILE: src/MvvmBlazor.Core/NotifyAttribute.cs
  class NotifyAttribute (line 5) | [AttributeUsage(AttributeTargets.Field)]

FILE: src/MvvmBlazor.Core/ViewModel/ViewModelBase.cs
  class ViewModelBase (line 3) | public abstract class ViewModelBase : INotifyPropertyChanged
    method Set (line 11) | protected bool Set<T>(ref T field, T value, [CallerMemberName] string?...
    method Set (line 16) | protected bool Set<T>(ref T field, T value, IEqualityComparer<T> equal...
    method OnPropertyChanged (line 35) | public virtual void OnPropertyChanged([CallerMemberName] string? prope...
    method Subscribe (line 41) | protected void Subscribe<T>(Expression<Func<T>>? expression, Action<T>...
    method SubscribeAsync (line 53) | protected void SubscribeAsync<T>(Expression<Func<T>>? property, Func<T...
    method OnInitialized (line 85) | public virtual void OnInitialized() { }
    method OnInitializedAsync (line 94) | public virtual Task OnInitializedAsync()
    method OnParametersSet (line 103) | public virtual void OnParametersSet() { }
    method OnParametersSetAsync (line 110) | public virtual Task OnParametersSetAsync()
    method StateHasChanged (line 119) | protected void StateHasChanged()
    method ShouldRender (line 128) | public virtual bool ShouldRender()
    method OnAfterRender (line 148) | public virtual void OnAfterRender(bool firstRender) { }
    method OnAfterRenderAsync (line 169) | public virtual Task OnAfterRenderAsync(bool firstRender)
    method SetParametersAsync (line 206) | public virtual Task SetParametersAsync(ParameterView parameters)

FILE: src/MvvmBlazor.Tests/Abstractions/StrictMock.cs
  class StrictMock (line 3) | internal class StrictMock<T> : Mock<T> where T : class
    method StrictMock (line 5) | public StrictMock() : base(MockBehavior.Strict) { }
    method StrictMock (line 6) | public StrictMock(params object[] args) : base(MockBehavior.Strict, ar...
    method StrictMock (line 7) | public StrictMock(Expression<Func<T>> newExpression) : base(newExpress...

FILE: src/MvvmBlazor.Tests/Abstractions/UnitTest.cs
  class UnitTest (line 3) | public abstract class UnitTest : IDisposable
    method UnitTest (line 8) | protected UnitTest(ITestOutputHelper outputHelper)
    method BuildProvider (line 13) | private ServiceProvider BuildProvider(ITestOutputHelper testOutputHelper)
    method RegisterServices (line 23) | protected virtual void RegisterServices(IServiceCollection services) { }
    method Dispose (line 25) | public void Dispose()
    method Dispose (line 31) | protected virtual void Dispose(bool disposing)

FILE: src/MvvmBlazor.Tests/Components/MvvmComponentBaseTTests.cs
  class MvvmComponentBaseTTests (line 3) | public class MvvmComponentBaseTTests : UnitTest
    method MvvmComponentBaseTTests (line 5) | public MvvmComponentBaseTTests(ITestOutputHelper outputHelper) : base(...
    method RegisterServices (line 7) | protected override void RegisterServices(IServiceCollection services)
    method AfterRender_called_On_binding_context (line 18) | [Fact]
    method AfterRenderAsync_called_on_binding_context (line 30) | [Fact]
    method Bind_binds_binding_context (line 44) | [Fact]
    method OnInitialized_called_on_binding_context (line 61) | [Fact]
    method OnInitializedAsync_called_on_binding_context (line 76) | [Fact]
    method OnParametersSet_sets_viewmodel_parameters (line 91) | [Fact]
    method OnParametersSetAsync_called_on_binding_context (line 106) | [Fact]
    method Sets_binding_context (line 121) | [Fact]
    method ShouldRender_called_on_binding_context (line 130) | [Fact]
    class MockMvvmComponentBase (line 144) | private class MockMvvmComponentBase : MvvmComponentBase<ViewModelBase>
      method MockMvvmComponentBase (line 147) | public MockMvvmComponentBase(IServiceProvider services) : base(servi...
      method Initialized (line 149) | public void Initialized()
      method InitializedAsync (line 154) | public Task InitializedAsync()
      method ParametersSet (line 159) | public void ParametersSet()
      method ParametersSetAsync (line 164) | public Task ParametersSetAsync()
      method Render (line 169) | public bool Render()
      method AfterRender (line 174) | public void AfterRender(bool firstRender)
      method AfterRenderAsync (line 179) | public Task AfterRenderAsync(bool firstRender)

FILE: src/MvvmBlazor.Tests/Components/MvvmComponentBaseTests.cs
  class MvvmComponentBaseTests (line 3) | public class MvvmComponentBaseTests : UnitTest
    method MvvmComponentBaseTests (line 5) | public MvvmComponentBaseTests(ITestOutputHelper outputHelper) : base(o...
    method RegisterServices (line 7) | protected override void RegisterServices(IServiceCollection services)
    method AddBinding_adds_Binding (line 15) | [Fact]
    method Bind_adds_Binding (line 29) | [Fact]
    class TestComponent (line 43) | internal class TestComponent : MvvmComponentBase
      method TestComponent (line 48) | public TestComponent(IServiceProvider services) : base(services) { }
      method BindingOnBindingValueChanged (line 50) | internal override void BindingOnBindingValueChanged(object sender, E...

FILE: src/MvvmBlazor.Tests/Components/TestViewModel.cs
  class TestViewModel (line 3) | public class TestViewModel : ViewModelBase

FILE: src/MvvmBlazor.Tests/Extensions/ServiceCollectionExtensions.cs
  class ServiceCollectionExtensions (line 5) | internal static class ServiceCollectionExtensions
    method Mock (line 7) | public static Mock<T> Mock<T>(this IServiceCollection services, params...
    method StrictMock (line 15) | public static Mock<T> StrictMock<T>(this IServiceCollection services) ...
    method Provide (line 23) | public static IServiceCollection Provide<T>(this IServiceCollection se...

FILE: src/MvvmBlazor.Tests/Extensions/ServiceProviderExtensions.cs
  class ServiceProviderExtensions (line 5) | internal static class ServiceProviderExtensions
    method GetMock (line 7) | public static Mock<T> GetMock<T>(this IServiceProvider provider) where...

FILE: src/MvvmBlazor.Tests/Generators/MvvmComponentGeneratorTests.cs
  class MvvmComponentGeneratorTests (line 5) | public class MvvmComponentGeneratorTests
    method Generates_error_when_component_is_not_partial (line 7) | [Fact]
    method Generates_error_when_component_is_not_inheriting (line 25) | [Fact]
    method Generates_error_when_generic_type_parameter_count_is_wrong (line 43) | [Fact]
    method Generates_error_when_generic_type_parameter_name_is_wrong (line 61) | [Fact]
    method Generates_component_with_component_base_class (line 79) | [Fact]
    method Generates_component_with_owning_component_base_class (line 96) | [Fact]
    method Generates_generic_component (line 113) | [Fact]
    method CreateCompilation (line 130) | private static Compilation CreateCompilation(string source)

FILE: src/MvvmBlazor.Tests/Generators/NotifyPropertyChangedGeneratorTests.cs
  class NotifyPropertyChangedGeneratorTests (line 3) | public class NotifyPropertyChangedGeneratorTests
    method Generates_error_when_viewmodel_is_not_partial (line 5) | [Fact]
    method Generates_error_when_view_model_is_not_inheriting (line 26) | [Fact]
    method Generates_viewmodel (line 47) | [Fact]
    method Generates_viewmodel_for_abstract_class (line 72) | [Fact]
    method Generates_viewmodel_for_abstract_class_with_inheritance (line 97) | [Fact]
    method Generates_viewmodel_for_generic_class (line 130) | [Fact]
    method Generates_viewmodel_for_abstract_generic_class (line 155) | [Fact]
    method CreateCompilation (line 180) | private static Compilation CreateCompilation(string source)

FILE: src/MvvmBlazor.Tests/Internal/Bindings/BinderTests.cs
  class BinderTests (line 5) | public class BinderTests : UnitTest
    method BinderTests (line 7) | public BinderTests(ITestOutputHelper outputHelper) : base(outputHelper...
    method RegisterServices (line 9) | protected override void RegisterServices(IServiceCollection services)
    method Bind_throws_when_callback_is_null (line 17) | [Fact]
    method Bind_throws_when_view_model_is_null (line 26) | [Fact]
    method Bind_throws_when_property_expression_is_null (line 37) | [Fact]
    method Bind_throws_when_property_expression_is_not_property (line 49) | [Fact]
    method Bind_binds_property (line 61) | [Fact]
    method Bind_binds_property_exactly_once (line 97) | [Fact]
    class TestViewModel (line 144) | private class TestViewModel : ViewModelBase

FILE: src/MvvmBlazor.Tests/Internal/Bindings/BindingFactoryTests.cs
  class BindingFactoryTests (line 3) | public class BindingFactoryTests
    method Create_returns_binding (line 5) | [Fact]

FILE: src/MvvmBlazor.Tests/Internal/Bindings/BindingTests.cs
  class BindingTests (line 5) | public class BindingTests
    method Adds_collection_event_listener_when_initializing (line 7) | [Fact]
    method Adds_collection_event_listener_when_property_changes_to_not_null (line 32) | [Fact]
    method Dispose_removes_collection_listener (line 66) | [Fact]
    method Dispose_removes_event_listener (line 98) | [Fact]
    method Equals_not_different_source_and_property (line 128) | [Fact]
    method Equals_same_source_and_property (line 149) | [Fact]
    method Ignores_PropertyChangedEvent_when_property_name_does_not_match (line 167) | [Fact]
    method Does_not_raise_BindingValueChanged_when_uninitialized (line 199) | [Fact]
    method Raises_BindingValueChanged_when_collection_changed (line 217) | [Fact]
    method Raises_BindingValueChanged_when_property_changed (line 258) | [Fact]
    method Removes_collection_event_listener_when_property_changes_to_null (line 292) | [Fact]
    method Should_not_raise_BindingValueChanged_on_collection_when_uninitialized (line 323) | [Fact]

FILE: src/MvvmBlazor.Tests/Internal/Parameters/ParameterCacheTests.cs
  class ParameterCacheTests (line 5) | public class ParameterCacheTests
    method Get_gets_cached_entry (line 7) | [Fact]

FILE: src/MvvmBlazor.Tests/Internal/Parameters/ParameterInfoTests.cs
  class ParameterInfoTests (line 5) | public class ParameterInfoTests
    method GenerateProperty (line 7) | private static Mock<PropertyInfo> GenerateProperty(string propertyName)
    method Ignores_missing_property_on_viewmodel (line 17) | [Fact]
    method Throws_for_missing_property_on_component (line 34) | [Fact]
    method Sorts_properties (line 47) | [Fact]

FILE: src/MvvmBlazor.Tests/Internal/Parameters/ParameterResolverTests.cs
  class ParameterResolverTests (line 3) | public class ParameterResolverTests : UnitTest
    method ParameterResolverTests (line 5) | public ParameterResolverTests(ITestOutputHelper outputHelper) : base(o...
    method RegisterServices (line 7) | protected override void RegisterServices(IServiceCollection services)
    method ResolveParameters_ignores_properties_without_attribute (line 13) | [Fact]
    method ResolveParameters_ignores_properties_without_public_setter (line 24) | [Fact]
    method ResolveParameters_resolves_multiple_parameters (line 35) | [Fact]
    method ResolveParameters_resolves_single_parameter (line 48) | [Fact]
    method ResolveParameters_resolves_cascading_parameter (line 59) | [Fact]
    class S1 (line 70) | private class S1
    class S2 (line 75) | private class S2
    class S3 (line 82) | private class S3
    class S4 (line 89) | private class S4
    class S5 (line 100) | private class S5

FILE: src/MvvmBlazor.Tests/Internal/Parameters/ViewModelParameterSetterTests.cs
  class ViewModelParameterSetterTests (line 6) | public class ViewModelParameterSetterTests : UnitTest
    method ViewModelParameterSetterTests (line 10) | public ViewModelParameterSetterTests(ITestOutputHelper outputHelper) :...
    method RegisterServices (line 12) | protected override void RegisterServices(IServiceCollection services)
    method GenerateProperty (line 20) | private static Mock<PropertyInfo> GenerateProperty(string propertyName...
    method ResolveAndSet_resolves_parameters_of_same_type (line 28) | [Fact]
    method ResolveAndSet_resolves_parameters_of_convertible_types (line 58) | [Fact]
    class StronglyTypedParameter (line 88) | [TypeConverter(typeof(StronglyTypedParameterConverter))]
    class StronglyTypedParameterConverter (line 91) | private class StronglyTypedParameterConverter : TypeConverter
      method CanConvertFrom (line 93) | public override bool CanConvertFrom(ITypeDescriptorContext? context,...
      method ConvertTo (line 98) | public override object ConvertTo(

FILE: src/MvvmBlazor.Tests/Internal/WeakEventListener/WeakEventListenerTests.cs
  class WeakEventManagerTests (line 3) | public class WeakEventManagerTests
    method AddWeakEventListener_collection_fires_event (line 5) | [Fact]
    method AddWeakEventListener_custom_fires_event (line 36) | [Fact]
    method AddWeakEventListener_fires_event_after_GC (line 60) | [Fact]
    method AddWeakEventListener_property_fires_event (line 86) | [Fact]
    method ClearWeakEventListeners_event_no_longer_fires (line 112) | [Fact]
    method RemoveWeakEventListener_event_no_longer_fires (line 138) | [Fact]

FILE: src/MvvmBlazor.Tests/ViewModel/ViewModelBaseTests.cs
  class ViewModelBaseTests (line 3) | public class ViewModelBaseTests
    method Set_returns_false_on_reference_equal (line 5) | [Fact]
    method Set_returns_false_on_value_equal (line 20) | [Fact]
    method Set_returns_true_on_value_not_equal (line 46) | [Fact]
    method Set_returns_False_with_custom_equality_comparer (line 79) | [Fact]
    method Set_returns_true_with_custom_equality_comparer (line 95) | [Fact]
    class TestViewModel (line 111) | private class TestViewModel : ViewModelBase
      method SetProperty (line 113) | public bool SetProperty<T>(ref T field, T value, string? propertyNam...
      method SetProperty (line 118) | public bool SetProperty<T>(ref T field, T value, IEqualityComparer<T...
Condensed preview — 133 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (226K chars).
[
  {
    "path": ".editorconfig",
    "chars": 4275,
    "preview": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ntrim_trailing_whitespace = true\ninsert_final_newline = false\nindent_st"
  },
  {
    "path": ".gitattributes",
    "chars": 2518,
    "preview": "###############################################################################\n# Set default behavior to automatically "
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug.yml",
    "chars": 1399,
    "preview": "name: Bug Report\ndescription: File a bug report\nlabels: [\"type: bug\", \"status: triage\"]\nbody:\n  - type: markdown\n    att"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 253,
    "preview": "blank_issues_enabled: false\ncontact_links:\n  - name: Security vulnerabilities\n    url: https://github.com/klemmchr\n    a"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature.yml",
    "chars": 488,
    "preview": "name: Feature request\ndescription: Propose a new feature\nlabels: [\"type: feature\", \"status: triage\"]\nbody:\n  - type: tex"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/other.yml",
    "chars": 385,
    "preview": "name: Other\ndescription: All other issues\nlabels: [\"status: triage\"]\nbody:\n  - type: textarea\n    id: description\n    at"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/question.yml",
    "chars": 848,
    "preview": "name: Question\ndescription: Ask a question\nlabels: [\"type: question\", \"status: triage\"]\nbody:\n  - type: textarea\n    id:"
  },
  {
    "path": ".github/workflows/build.yaml",
    "chars": 1242,
    "preview": "on:\n  push:\n    branches:\n      - master\n  pull_request:\n    branches:\n      - master\n    paths:\n      - 'src/**/*'\n    "
  },
  {
    "path": ".github/workflows/release.yaml",
    "chars": 1548,
    "preview": "on:\n  release:\n    types: [ published ]\n\nname: Release\n\njobs:\n\n  release:\n    name: Release\n    runs-on: ubuntu-latest\n "
  },
  {
    "path": ".gitignore",
    "chars": 7459,
    "preview": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## G"
  },
  {
    "path": "LICENSE",
    "chars": 1072,
    "preview": "MIT License\n\nCopyright (c) 2019 Christian Klemm\n\nPermission is hereby granted, free of charge, to any person obtaining a"
  },
  {
    "path": "README.md",
    "chars": 8641,
    "preview": "MvvmBlazor\n================\n[![Build Status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.d"
  },
  {
    "path": "samples/BlazorClientsideSample.Client/App.razor",
    "chars": 371,
    "preview": "<Router AppAssembly=\"@typeof(BlazorSample.Components._Imports).Assembly\">\n    <Found Context=\"routeData\">\n        <Rout"
  },
  {
    "path": "samples/BlazorClientsideSample.Client/BlazorClientsideSample.Client.csproj",
    "chars": 814,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk.BlazorWebAssembly\">\n\n    <PropertyGroup>\n        <TargetFramework>net6.0</TargetFramewo"
  },
  {
    "path": "samples/BlazorClientsideSample.Client/Program.cs",
    "chars": 892,
    "preview": "using BlazorClientsideSample.Client.Services;\nusing BlazorSample.Components.Extensions;\nusing BlazorSample.Domain.Exten"
  },
  {
    "path": "samples/BlazorClientsideSample.Client/Properties/launchSettings.json",
    "chars": 283,
    "preview": "{\n    \"profiles\": {\n        \"BlazorClientsideSample.Client\": {\n            \"commandName\": \"Project\",\n            \"enviro"
  },
  {
    "path": "samples/BlazorClientsideSample.Client/Services/WeatherForecastGetter.cs",
    "chars": 545,
    "preview": "using System.Net.Http.Json;\nusing BlazorSample.Domain.Entities;\nusing BlazorSample.Domain.Services;\n\nnamespace BlazorCl"
  },
  {
    "path": "samples/BlazorClientsideSample.Client/_Imports.razor",
    "chars": 252,
    "preview": "@using System.Net.Http\n@using Microsoft.AspNetCore.Components.Forms\n@using Microsoft.AspNetCore.Components.Routing\n@usi"
  },
  {
    "path": "samples/BlazorClientsideSample.Client/wwwroot/index.html",
    "chars": 505,
    "preview": "<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"utf-8\"/>\n    <meta content=\"width=device-width\" name=\"viewport\"/>\n    "
  },
  {
    "path": "samples/BlazorClientsideSample.Server/BlazorClientsideSample.Server.csproj",
    "chars": 727,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk.Web\">\n\n    <PropertyGroup>\n        <TargetFramework>net6.0</TargetFramework>\n        <N"
  },
  {
    "path": "samples/BlazorClientsideSample.Server/Controllers/WeatherForecastController.cs",
    "chars": 865,
    "preview": "using BlazorSample.Domain.Entities;\nusing Microsoft.AspNetCore.Mvc;\n\nnamespace BlazorClientsideSample.Server.Controller"
  },
  {
    "path": "samples/BlazorClientsideSample.Server/Program.cs",
    "chars": 447,
    "preview": "using Microsoft.AspNetCore;\n\nnamespace BlazorClientsideSample.Server;\n\npublic class Program\n{\n    public static void Ma"
  },
  {
    "path": "samples/BlazorClientsideSample.Server/Properties/launchSettings.json",
    "chars": 318,
    "preview": "{\n    \"profiles\": {\n        \"BlazorClientsideSample.Server\": {\n            \"commandName\": \"Project\",\n            \"launch"
  },
  {
    "path": "samples/BlazorClientsideSample.Server/Startup.cs",
    "chars": 1344,
    "preview": "using Microsoft.AspNetCore.ResponseCompression;\n\nnamespace BlazorClientsideSample.Server;\n\npublic class Startup\n{\n    //"
  },
  {
    "path": "samples/BlazorSample.Components/BlazorSample.Components.csproj",
    "chars": 925,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk.Razor\">\n\n    <PropertyGroup>\n        <TargetFramework>net6.0</TargetFramework>\n        "
  },
  {
    "path": "samples/BlazorSample.Components/Extensions/ServiceCollectionExtensions.cs",
    "chars": 971,
    "preview": "using BlazorSample.Components.Pages;\nusing BlazorSample.Domain.Extensions;\nusing MatBlazor;\nusing Microsoft.Extensions."
  },
  {
    "path": "samples/BlazorSample.Components/Pages/Clock.razor",
    "chars": 722,
    "preview": "@page \"/clock\"\n@inherits CustomBaseComponent<ClockViewModel>\n\n<MatCard>\n    <MatCardContent>\n        <MatSubtitle1>\n   "
  },
  {
    "path": "samples/BlazorSample.Components/Pages/Counter.razor",
    "chars": 982,
    "preview": "@page \"/counter\"\n@inherits CustomBaseComponent<CounterViewModel>\n@inject ClockViewModel ClockViewModel\n\n<MatCard>\n    <"
  },
  {
    "path": "samples/BlazorSample.Components/Pages/Index.razor",
    "chars": 208,
    "preview": "@page \"/\"\n\n<MatCard>\n    <MatCardContent>\n        <MatSubtitle1>\n            Explore the tabs on the left to see some e"
  },
  {
    "path": "samples/BlazorSample.Components/Pages/Parameters.razor",
    "chars": 1184,
    "preview": "@page \"/parameters/{name}\"\n@inherits CustomBaseComponent<ParametersViewModel>\n\n<MatCard>\n    <MatCardContent>\n        <"
  },
  {
    "path": "samples/BlazorSample.Components/Pages/TypedParameters.razor",
    "chars": 704,
    "preview": "@page \"/typed-parameters/{Id}\"\n@inherits CustomBaseComponent<TypedParametersViewModel>\n\n<MatCard>\n    <MatCardContent>\n"
  },
  {
    "path": "samples/BlazorSample.Components/Pages/WeatherForecasts.razor",
    "chars": 1790,
    "preview": "@page \"/fetchdata\"\n\n@inherits CustomBaseComponent<WeatherForecastsViewModel>\n\n<MatCard>\n    <MatCardContent>\n        <M"
  },
  {
    "path": "samples/BlazorSample.Components/Shared/CascadingComponent.razor",
    "chars": 186,
    "preview": "@inherits CustomBaseComponent<CascadingViewModel>\n\n<MatBody2>Cascading value is @Bind(x => x.Name)</MatBody2>\n\n@code {\n"
  },
  {
    "path": "samples/BlazorSample.Components/Shared/CustomBaseComponent.cs",
    "chars": 285,
    "preview": "using Microsoft.AspNetCore.Components;\nusing MvvmBlazor;\n\nnamespace BlazorSample.Components.Shared;\n\n[MvvmComponent]\npu"
  },
  {
    "path": "samples/BlazorSample.Components/Shared/MainLayout.razor",
    "chars": 60,
    "preview": "@inherits LayoutComponentBase\n\n<Navbar>\n    @Body\n</Navbar>"
  },
  {
    "path": "samples/BlazorSample.Components/Shared/Navbar.razor",
    "chars": 1413,
    "preview": "@inherits MvvmComponentBase<NavbarViewModel>\n\n<MatDrawerContainer Class=\"navbar\">\n    <MatDrawer @bind-Opened=\"@Binding"
  },
  {
    "path": "samples/BlazorSample.Components/_Imports.razor",
    "chars": 243,
    "preview": "@using Microsoft.AspNetCore.Components.Web\n@using MvvmBlazor.Components\n@using BlazorSample.ViewModels\n@using BlazorSam"
  },
  {
    "path": "samples/BlazorSample.Components/wwwroot/site.css",
    "chars": 1675,
    "preview": "/* http://meyerweb.com/eric/tools/css/reset/\n   v4.0 | 20180602\n   License: none (public domain)\n*/\n\nhtml, body, div, s"
  },
  {
    "path": "samples/BlazorSample.Domain/BlazorSample.Domain.csproj",
    "chars": 488,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n    <PropertyGroup>\n        <TargetFramework>net6.0</TargetFramework>\n        <Nulla"
  },
  {
    "path": "samples/BlazorSample.Domain/Converters/IdTypeConverter.cs",
    "chars": 608,
    "preview": "using System.ComponentModel;\nusing System.Globalization;\nusing BlazorSample.Domain.Entities;\n\nnamespace BlazorSample.Do"
  },
  {
    "path": "samples/BlazorSample.Domain/Entities/IdType.cs",
    "chars": 308,
    "preview": "using System.ComponentModel;\nusing System.Text.Json;\nusing BlazorSample.Domain.Converters;\n\nnamespace BlazorSample.Doma"
  },
  {
    "path": "samples/BlazorSample.Domain/Entities/WeatherForecastEntity.cs",
    "chars": 270,
    "preview": "namespace BlazorSample.Domain.Entities;\n\npublic class WeatherForecastEntity\n{\n    public DateTime Date { get; set; }\n\n  "
  },
  {
    "path": "samples/BlazorSample.Domain/Extensions/ServiceCollectionExtensions.cs",
    "chars": 756,
    "preview": "using BlazorSample.Domain.Services.Navbar;\nusing Microsoft.AspNetCore.Components;\nusing Microsoft.Extensions.Dependency"
  },
  {
    "path": "samples/BlazorSample.Domain/Services/IWeatherForecastGetter.cs",
    "chars": 183,
    "preview": "using BlazorSample.Domain.Entities;\n\nnamespace BlazorSample.Domain.Services;\n\npublic interface IWeatherForecastGetter\n{"
  },
  {
    "path": "samples/BlazorSample.Domain/Services/Navbar/NavbarItem.cs",
    "chars": 377,
    "preview": "namespace BlazorSample.Domain.Services.Navbar;\n\npublic class NavbarItem\n{\n    public string? Icon { get; }\n    public T"
  },
  {
    "path": "samples/BlazorSample.Domain/Services/Navbar/NavbarService.cs",
    "chars": 1483,
    "preview": "using System.Collections.ObjectModel;\nusing System.Reflection;\nusing Microsoft.AspNetCore.Components;\n\nnamespace Blazor"
  },
  {
    "path": "samples/BlazorSample.ViewModels/BlazorSample.ViewModels.csproj",
    "chars": 671,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n    <PropertyGroup>\n        <TargetFramework>net6.0</TargetFramework>\n        <Nulla"
  },
  {
    "path": "samples/BlazorSample.ViewModels/CascadingViewModel.cs",
    "chars": 151,
    "preview": "namespace BlazorSample.ViewModels;\n\npublic class CascadingViewModel : ViewModelBase\n{\n    [CascadingParameter]\n    publ"
  },
  {
    "path": "samples/BlazorSample.ViewModels/ClockViewModel.cs",
    "chars": 818,
    "preview": "using Timer = System.Timers.Timer;\n\nnamespace BlazorSample.ViewModels;\n\npublic partial class ClockViewModel : ViewModel"
  },
  {
    "path": "samples/BlazorSample.ViewModels/CounterViewModel.cs",
    "chars": 204,
    "preview": "namespace BlazorSample.ViewModels;\n\npublic partial class CounterViewModel : ViewModelBase\n{\n    [Notify] private int _c"
  },
  {
    "path": "samples/BlazorSample.ViewModels/Extensions/ServiceCollectionExtensions.cs",
    "chars": 712,
    "preview": "using BlazorSample.ViewModels.Navbar;\n\nnamespace BlazorSample.ViewModels.Extensions;\n\npublic static class ServiceCollec"
  },
  {
    "path": "samples/BlazorSample.ViewModels/GlobalUsings.cs",
    "chars": 320,
    "preview": "global using System.Collections.ObjectModel;\nglobal using BlazorSample.Domain.Services;\nglobal using MvvmBlazor;\nglobal"
  },
  {
    "path": "samples/BlazorSample.ViewModels/Navbar/NavbarItemViewModel.cs",
    "chars": 544,
    "preview": "namespace BlazorSample.ViewModels.Navbar;\n\npublic class NavbarItemViewModel : ViewModelBase\n{\n    private bool _isActiv"
  },
  {
    "path": "samples/BlazorSample.ViewModels/Navbar/NavbarViewModel.cs",
    "chars": 1504,
    "preview": "using BlazorSample.Domain.Services.Navbar;\n\nnamespace BlazorSample.ViewModels.Navbar;\n\npublic class NavbarViewModel : V"
  },
  {
    "path": "samples/BlazorSample.ViewModels/ParametersViewModel.cs",
    "chars": 582,
    "preview": "namespace BlazorSample.ViewModels;\n\npublic class ParametersViewModel : ViewModelBase\n{\n    private NavigationManager _n"
  },
  {
    "path": "samples/BlazorSample.ViewModels/TypedParametersViewModel.cs",
    "chars": 607,
    "preview": "namespace BlazorSample.ViewModels;\n\npublic class TypedParametersViewModel : ViewModelBase\n{\n    private NavigationManag"
  },
  {
    "path": "samples/BlazorSample.ViewModels/WeatherForecastViewModel.cs",
    "chars": 893,
    "preview": "namespace BlazorSample.ViewModels;\n\npublic class WeatherForecastViewModel : ViewModelBase\n{\n    private readonly Weathe"
  },
  {
    "path": "samples/BlazorSample.ViewModels/WeatherForecastsViewModel.cs",
    "chars": 1062,
    "preview": "namespace BlazorSample.ViewModels;\n\npublic partial class WeatherForecastsViewModel : ViewModelBase\n{\n    private readon"
  },
  {
    "path": "samples/BlazorServersideSample/App.razor",
    "chars": 371,
    "preview": "<Router AppAssembly=\"@typeof(BlazorSample.Components._Imports).Assembly\">\n    <Found Context=\"routeData\">\n        <Rout"
  },
  {
    "path": "samples/BlazorServersideSample/BlazorServersideSample.csproj",
    "chars": 480,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk.Web\">\n\n    <PropertyGroup>\n        <TargetFramework>net6.0</TargetFramework>\n        <Nu"
  },
  {
    "path": "samples/BlazorServersideSample/Pages/Error.razor",
    "chars": 697,
    "preview": "@page \"/error\"\n\n\n<h1 class=\"text-danger\">Error.</h1>\n<h2 class=\"text-danger\">An error occurred while processing your re"
  },
  {
    "path": "samples/BlazorServersideSample/Pages/_Host.cshtml",
    "chars": 700,
    "preview": "@page \"/\"\n@namespace BlazorServersideSample.Pages\n@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers\n\n<!DOCTYPE html>"
  },
  {
    "path": "samples/BlazorServersideSample/Pages/_Imports.razor",
    "chars": 28,
    "preview": "@inherits MvvmComponentBase"
  },
  {
    "path": "samples/BlazorServersideSample/Program.cs",
    "chars": 377,
    "preview": "namespace BlazorServersideSample;\n\npublic class Program\n{\n    public static void Main(string[] args)\n    {\n        Creat"
  },
  {
    "path": "samples/BlazorServersideSample/Properties/launchSettings.json",
    "chars": 332,
    "preview": "{\n    \"profiles\": {\n        \"BlazorServersideSample\": {\n            \"commandName\": \"Project\",\n            \"launchBrowser"
  },
  {
    "path": "samples/BlazorServersideSample/Services/WeatherForecastGetter.cs",
    "chars": 871,
    "preview": "using BlazorSample.Domain.Entities;\nusing BlazorSample.Domain.Services;\n\nnamespace BlazorServersideSample.Services;\n\npub"
  },
  {
    "path": "samples/BlazorServersideSample/Startup.cs",
    "chars": 1793,
    "preview": "using BlazorSample.Components.Extensions;\nusing BlazorSample.Domain.Extensions;\nusing BlazorSample.Domain.Services;\nusin"
  },
  {
    "path": "samples/BlazorServersideSample/_Imports.razor",
    "chars": 416,
    "preview": "@using System.Net.Http\n@using Microsoft.AspNetCore.Authorization\n@using Microsoft.AspNetCore.Components.Authorization\n@"
  },
  {
    "path": "samples/BlazorServersideSample/appsettings.Development.json",
    "chars": 166,
    "preview": "{\n    \"Logging\": {\n        \"LogLevel\": {\n            \"Default\": \"Debug\",\n            \"System\": \"Information\",\n          "
  },
  {
    "path": "samples/BlazorServersideSample/appsettings.json",
    "chars": 213,
    "preview": "{\n    \"Logging\": {\n        \"LogLevel\": {\n            \"Default\": \"Information\",\n            \"Microsoft\": \"Warning\",\n     "
  },
  {
    "path": "src/.editorconfig",
    "chars": 1397,
    "preview": "[*.{c,c++,cc,cp,cpp,cu,cuh,cxx,h,hh,hpp,hxx,inc,inl,ino,ipp,mpp,proto,tpp}]\nindent_style = tab\nindent_size = tab\ntab_wid"
  },
  {
    "path": "src/Directory.Build.props",
    "chars": 438,
    "preview": "<Project>\n  <PropertyGroup>\n    <Nullable>enable</Nullable>\n    <ImplicitUsings>enable</ImplicitUsings>\n    <AnalysisMo"
  },
  {
    "path": "src/MvvmBlazor/MvvmBlazor.csproj",
    "chars": 1394,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net6.0</TargetFramework>\n    <EmitCompilerGene"
  },
  {
    "path": "src/MvvmBlazor/Properties/AssemblyInfo.cs",
    "chars": 32,
    "preview": "[assembly: CLSCompliant(false)]"
  },
  {
    "path": "src/MvvmBlazor.CodeGenerators/AnalyzerReleases.Shipped.md",
    "chars": 584,
    "preview": "## Release 6.0.0\n### New Rules\n\nRule ID | Category | Severity | Notes\n--------|----------|----------|-------\nMVVMBLAZOR"
  },
  {
    "path": "src/MvvmBlazor.CodeGenerators/AnalyzerReleases.Unshipped.md",
    "chars": 1,
    "preview": ""
  },
  {
    "path": "src/MvvmBlazor.CodeGenerators/Components/MvvmComponentClassContext.cs",
    "chars": 412,
    "preview": "namespace MvvmBlazor.CodeGenerators.Components;\n\ninternal class MvvmComponentClassContext\n{\n    public ClassDeclaration"
  },
  {
    "path": "src/MvvmBlazor.CodeGenerators/Components/MvvmComponentGenerator.cs",
    "chars": 13158,
    "preview": "namespace MvvmBlazor.CodeGenerators.Components;\n\n[Generator]\npublic class MvvmComponentGenerator : ISourceGenerator\n{\n "
  },
  {
    "path": "src/MvvmBlazor.CodeGenerators/Components/MvvmComponentSyntaxReceiver.cs",
    "chars": 901,
    "preview": "namespace MvvmBlazor.CodeGenerators.Components;\n\ninternal class MvvmComponentSyntaxReceiver : ISyntaxContextReceiver\n{\n"
  },
  {
    "path": "src/MvvmBlazor.CodeGenerators/Extensions/StringBuilderExtensions.cs",
    "chars": 608,
    "preview": "using System.Globalization;\n\nnamespace MvvmBlazor.CodeGenerators.Extensions;\n\ninternal static class StringBuilderExtens"
  },
  {
    "path": "src/MvvmBlazor.CodeGenerators/Extensions/SymbolExtensions.cs",
    "chars": 602,
    "preview": "namespace MvvmBlazor.CodeGenerators.Extensions;\n\ninternal static class SymbolExtensions\n{\n    public static string GetM"
  },
  {
    "path": "src/MvvmBlazor.CodeGenerators/GlobalUsings.cs",
    "chars": 318,
    "preview": "global using System.Text;\nglobal using Microsoft.CodeAnalysis;\nglobal using Microsoft.CodeAnalysis.CSharp;\nglobal using"
  },
  {
    "path": "src/MvvmBlazor.CodeGenerators/MvvmBlazor.CodeGenerators.csproj",
    "chars": 1433,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>netstandard2.0</TargetFramework>\n    <LangVer"
  },
  {
    "path": "src/MvvmBlazor.CodeGenerators/NotifyPropertyChanged/NotifyPropertyChangedContext.cs",
    "chars": 369,
    "preview": "namespace MvvmBlazor.CodeGenerators.NotifyPropertyChanged;\n\ninternal class NotifyPropertyChangedContext\n{\n    public Fi"
  },
  {
    "path": "src/MvvmBlazor.CodeGenerators/NotifyPropertyChanged/NotifyPropertyChangedGenerator.cs",
    "chars": 5361,
    "preview": "namespace MvvmBlazor.CodeGenerators.NotifyPropertyChanged;\n\n[Generator]\npublic class NotifyPropertyChangedGenerator : I"
  },
  {
    "path": "src/MvvmBlazor.CodeGenerators/NotifyPropertyChanged/NotifyPropertyChangedSyntaxReceiver.cs",
    "chars": 1307,
    "preview": "namespace MvvmBlazor.CodeGenerators.NotifyPropertyChanged;\n\ninternal class NotifyPropertyChangedSyntaxReceiver : ISynta"
  },
  {
    "path": "src/MvvmBlazor.CodeGenerators/Properties/AssemblyInfo.cs",
    "chars": 32,
    "preview": "[assembly: CLSCompliant(false)]"
  },
  {
    "path": "src/MvvmBlazor.Core/Components/MvvmComponentAttribute.cs",
    "chars": 165,
    "preview": "// ReSharper disable once CheckNamespace\n\nnamespace MvvmBlazor;\n\n[AttributeUsage(AttributeTargets.Class)]\npublic sealed"
  },
  {
    "path": "src/MvvmBlazor.Core/Components/MvvmComponentBase.cs",
    "chars": 170,
    "preview": "namespace MvvmBlazor.Components;\n\n[MvvmComponent]\n// ReSharper disable once PartialTypeWithSinglePart\npublic abstract p"
  },
  {
    "path": "src/MvvmBlazor.Core/Components/MvvmComponentBaseT.cs",
    "chars": 201,
    "preview": "namespace MvvmBlazor.Components;\n\n[MvvmComponent]\n// ReSharper disable once PartialTypeWithSinglePart\npublic abstract p"
  },
  {
    "path": "src/MvvmBlazor.Core/Extensions/ServiceCollectionExtensions.cs",
    "chars": 798,
    "preview": "using Binder = MvvmBlazor.Internal.Bindings.Binder;\n\n// ReSharper disable once CheckNamespace\nnamespace Microsoft.Exten"
  },
  {
    "path": "src/MvvmBlazor.Core/GlobalUsings.cs",
    "chars": 497,
    "preview": "global using System.ComponentModel;\nglobal using System.Linq.Expressions;\nglobal using System.Reflection;\nglobal using "
  },
  {
    "path": "src/MvvmBlazor.Core/Internal/Bindings/Binder.cs",
    "chars": 3286,
    "preview": "namespace MvvmBlazor.Internal.Bindings;\n\npublic interface IBinder\n{\n    Action<IBinding, EventArgs>? ValueChangedCallba"
  },
  {
    "path": "src/MvvmBlazor.Core/Internal/Bindings/Binding.cs",
    "chars": 3524,
    "preview": "namespace MvvmBlazor.Internal.Bindings;\n\npublic interface IBinding : IDisposable\n{\n    INotifyPropertyChanged Source { "
  },
  {
    "path": "src/MvvmBlazor.Core/Internal/Bindings/BindingException.cs",
    "chars": 401,
    "preview": "namespace MvvmBlazor.Internal.Bindings;\n\npublic class BindingException : Exception\n{\n    public BindingException() { }\n"
  },
  {
    "path": "src/MvvmBlazor.Core/Internal/Bindings/BindingFactory.cs",
    "chars": 449,
    "preview": "namespace MvvmBlazor.Internal.Bindings;\n\ninternal interface IBindingFactory\n{\n    IBinding Create(INotifyPropertyChange"
  },
  {
    "path": "src/MvvmBlazor.Core/Internal/Parameters/ParameterCache.cs",
    "chars": 597,
    "preview": "using System.Collections.Concurrent;\n\nnamespace MvvmBlazor.Internal.Parameters;\n\ninternal interface IParameterCache\n{\n "
  },
  {
    "path": "src/MvvmBlazor.Core/Internal/Parameters/ParameterException.cs",
    "chars": 428,
    "preview": "namespace MvvmBlazor.Internal.Parameters;\n\n[Serializable]\npublic class ParameterException : Exception\n{\n    public Para"
  },
  {
    "path": "src/MvvmBlazor.Core/Internal/Parameters/ParameterInfo.cs",
    "chars": 979,
    "preview": "namespace MvvmBlazor.Internal.Parameters;\n\ninternal record ParameterInfo\n{\n    private readonly Dictionary<PropertyInfo"
  },
  {
    "path": "src/MvvmBlazor.Core/Internal/Parameters/ParameterResolver.cs",
    "chars": 2016,
    "preview": "namespace MvvmBlazor.Internal.Parameters;\n\ninternal interface IParameterResolver\n{\n    ParameterInfo ResolveParameters("
  },
  {
    "path": "src/MvvmBlazor.Core/Internal/Parameters/ViewModelParameterSetter.cs",
    "chars": 1531,
    "preview": "namespace MvvmBlazor.Internal.Parameters;\n\npublic interface IViewModelParameterSetter\n{\n    void ResolveAndSet(Componen"
  },
  {
    "path": "src/MvvmBlazor.Core/Internal/WeakEventListener/WeakEventListener.cs",
    "chars": 4753,
    "preview": "namespace MvvmBlazor.Internal.WeakEventListener;\n\ninternal interface IWeakEventListener\n{\n    bool IsAlive { get; }\n   "
  },
  {
    "path": "src/MvvmBlazor.Core/Internal/WeakEventListener/WeakEventManager.cs",
    "chars": 3929,
    "preview": "namespace MvvmBlazor.Internal.WeakEventListener;\n\ninternal interface IWeakEventManager\n{\n    /// <summary>\n    ///     "
  },
  {
    "path": "src/MvvmBlazor.Core/MvvmBlazor.Core.csproj",
    "chars": 1739,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net6.0</TargetFramework>\n    <EmitCompilerGen"
  },
  {
    "path": "src/MvvmBlazor.Core/NotifyAttribute.cs",
    "chars": 158,
    "preview": "// ReSharper disable once CheckNamespace\n\nnamespace MvvmBlazor;\n\n[AttributeUsage(AttributeTargets.Field)]\npublic sealed"
  },
  {
    "path": "src/MvvmBlazor.Core/Properties/AssemblyInfo.cs",
    "chars": 142,
    "preview": "[assembly: InternalsVisibleTo(\"MvvmBlazor.Tests\")]\n[assembly: InternalsVisibleTo(\"DynamicProxyGenAssembly2\")]\n[assembly"
  },
  {
    "path": "src/MvvmBlazor.Core/Properties/GlobalSuppressions.cs",
    "chars": 387,
    "preview": "// This file is used by Code Analysis to maintain SuppressMessage\n// attributes that are applied to this project.\n// Pr"
  },
  {
    "path": "src/MvvmBlazor.Core/ViewModel/ViewModelBase.cs",
    "chars": 8050,
    "preview": "namespace MvvmBlazor.ViewModel;\n\npublic abstract class ViewModelBase : INotifyPropertyChanged\n{\n    private readonly Dic"
  },
  {
    "path": "src/MvvmBlazor.Tests/.editorconfig",
    "chars": 89,
    "preview": "[*.cs]\ndotnet_diagnostic.CA1707.severity = none\ndotnet_diagnostic.CA1812.severity = none"
  },
  {
    "path": "src/MvvmBlazor.Tests/Abstractions/StrictMock.cs",
    "chars": 343,
    "preview": "namespace MvvmBlazor.Tests.Abstractions;\n\ninternal class StrictMock<T> : Mock<T> where T : class\n{\n    public StrictMoc"
  },
  {
    "path": "src/MvvmBlazor.Tests/Abstractions/UnitTest.cs",
    "chars": 935,
    "preview": "namespace MvvmBlazor.Tests.Abstractions;\n\npublic abstract class UnitTest : IDisposable\n{\n    private readonly ServicePr"
  },
  {
    "path": "src/MvvmBlazor.Tests/Components/MvvmComponentBaseTTests.cs",
    "chars": 5550,
    "preview": "namespace MvvmBlazor.Tests.Components;\n\npublic class MvvmComponentBaseTTests : UnitTest\n{\n    public MvvmComponentBaseT"
  },
  {
    "path": "src/MvvmBlazor.Tests/Components/MvvmComponentBaseTests.cs",
    "chars": 1742,
    "preview": "namespace MvvmBlazor.Tests.Components;\n\npublic class MvvmComponentBaseTests : UnitTest\n{\n    public MvvmComponentBaseTe"
  },
  {
    "path": "src/MvvmBlazor.Tests/Components/TestViewModel.cs",
    "chars": 243,
    "preview": "namespace MvvmBlazor.Tests.Components;\n\npublic class TestViewModel : ViewModelBase\n{\n    private string? _testProperty;"
  },
  {
    "path": "src/MvvmBlazor.Tests/Extensions/ServiceCollectionExtensions.cs",
    "chars": 815,
    "preview": "// ReSharper disable once CheckNamespace\n\nnamespace Microsoft.Extensions.DependencyInjection;\n\ninternal static class Se"
  },
  {
    "path": "src/MvvmBlazor.Tests/Extensions/ServiceProviderExtensions.cs",
    "chars": 299,
    "preview": "// ReSharper disable once CheckNamespace\n\nnamespace Microsoft.Extensions.DependencyInjection;\n\ninternal static class Se"
  },
  {
    "path": "src/MvvmBlazor.Tests/Generators/MvvmComponentGeneratorTests.cs",
    "chars": 5071,
    "preview": "using Binder = System.Reflection.Binder;\n\nnamespace MvvmBlazor.Tests.Generators;\n\npublic class MvvmComponentGeneratorTe"
  },
  {
    "path": "src/MvvmBlazor.Tests/Generators/NotifyPropertyChangedGeneratorTests.cs",
    "chars": 6080,
    "preview": "namespace MvvmBlazor.Tests.Generators;\n\npublic class NotifyPropertyChangedGeneratorTests\n{\n    [Fact]\n    public void G"
  },
  {
    "path": "src/MvvmBlazor.Tests/GlobalUsings.cs",
    "chars": 879,
    "preview": "global using System;\nglobal using Microsoft.Extensions.DependencyInjection;\nglobal using Moq;\nglobal using MvvmBlazor.C"
  },
  {
    "path": "src/MvvmBlazor.Tests/Internal/Bindings/BinderTests.cs",
    "chars": 5134,
    "preview": "using Binder = MvvmBlazor.Internal.Bindings.Binder;\n\nnamespace MvvmBlazor.Tests.Internal.Bindings;\n\npublic class Binder"
  },
  {
    "path": "src/MvvmBlazor.Tests/Internal/Bindings/BindingFactoryTests.cs",
    "chars": 571,
    "preview": "namespace MvvmBlazor.Tests.Internal.Bindings;\n\npublic class BindingFactoryTests\n{\n    [Fact]\n    public void Create_ret"
  },
  {
    "path": "src/MvvmBlazor.Tests/Internal/Bindings/BindingTests.cs",
    "chars": 14686,
    "preview": "using System.Collections.ObjectModel;\n\nnamespace MvvmBlazor.Tests.Internal.Bindings;\n\npublic class BindingTests\n{\n    ["
  },
  {
    "path": "src/MvvmBlazor.Tests/Internal/Parameters/ParameterCacheTests.cs",
    "chars": 504,
    "preview": "using ParameterInfo = MvvmBlazor.Internal.Parameters.ParameterInfo;\n\nnamespace MvvmBlazor.Tests.Internal.Parameters;\n\np"
  },
  {
    "path": "src/MvvmBlazor.Tests/Internal/Parameters/ParameterInfoTests.cs",
    "chars": 2710,
    "preview": "using ParameterInfo = MvvmBlazor.Internal.Parameters.ParameterInfo;\n\nnamespace MvvmBlazor.Tests.Internal.Parameters;\n\np"
  },
  {
    "path": "src/MvvmBlazor.Tests/Internal/Parameters/ParameterResolverTests.cs",
    "chars": 3425,
    "preview": "namespace MvvmBlazor.Tests.Internal.Parameters;\n\npublic class ParameterResolverTests : UnitTest\n{\n    public ParameterR"
  },
  {
    "path": "src/MvvmBlazor.Tests/Internal/Parameters/ViewModelParameterSetterTests.cs",
    "chars": 4208,
    "preview": "using System.Globalization;\nusing ParameterInfo = MvvmBlazor.Internal.Parameters.ParameterInfo;\n\nnamespace MvvmBlazor.T"
  },
  {
    "path": "src/MvvmBlazor.Tests/Internal/WeakEventListener/WeakEventListenerTests.cs",
    "chars": 4361,
    "preview": "namespace MvvmBlazor.Tests.Internal.WeakEventListener;\n\npublic class WeakEventManagerTests\n{\n    [Fact]\n    public void"
  },
  {
    "path": "src/MvvmBlazor.Tests/MvvmBlazor.Tests.csproj",
    "chars": 1353,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net6.0</TargetFramework>\n    <IsPackable>false"
  },
  {
    "path": "src/MvvmBlazor.Tests/Properties/AssemblyInfo.cs",
    "chars": 32,
    "preview": "[assembly: CLSCompliant(false)]"
  },
  {
    "path": "src/MvvmBlazor.Tests/ViewModel/ViewModelBaseTests.cs",
    "chars": 3157,
    "preview": "namespace MvvmBlazor.Tests.ViewModel;\n\npublic class ViewModelBaseTests\n{\n    [Fact]\n    public void Set_returns_false_on"
  },
  {
    "path": "src/MvvmBlazor.Tests/xunit.runner.json",
    "chars": 153,
    "preview": "{\n  \"$schema\": \"https://xunit.net/schema/current/xunit.runner.schema.json\",\n  \"methodDisplayOptions\": \"replaceUnderscor"
  },
  {
    "path": "src/MvvmBlazor.sln",
    "chars": 13550,
    "preview": "\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 16\nVisualStudioVersion = 16.0.2880"
  },
  {
    "path": "src/MvvmBlazor.sln.DotSettings",
    "chars": 685,
    "preview": "<wpf:ResourceDictionary xml:space=\"preserve\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:s=\"clr-namesp"
  }
]

About this extraction

This page contains the full source code of the klemmchr/MvvmBlazor GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 133 files (202.1 KB), approximately 53.2k tokens, and a symbol index with 346 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!