Skip to content

Commit

Permalink
Improved Avalonia examples
Browse files Browse the repository at this point in the history
  • Loading branch information
NikolayPianikov committed Sep 17, 2024
1 parent e19fc38 commit 1f379a5
Show file tree
Hide file tree
Showing 43 changed files with 420 additions and 40 deletions.
7 changes: 7 additions & 0 deletions Pure.DI.sln
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pure.DI.Abstractions", "src
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pure.DI.Example", "tests\Pure.DI.Example\Pure.DI.Example.csproj", "{9D043E3D-878F-4D64-A689-7BE16A25842A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AvaloniaSimpleApp", "samples\AvaloniaSimpleApp\AvaloniaSimpleApp.csproj", "{29F9986B-4B32-4C30-AECA-D68EAB4EA9CB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -219,6 +221,10 @@ Global
{9D043E3D-878F-4D64-A689-7BE16A25842A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9D043E3D-878F-4D64-A689-7BE16A25842A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9D043E3D-878F-4D64-A689-7BE16A25842A}.Release|Any CPU.Build.0 = Release|Any CPU
{29F9986B-4B32-4C30-AECA-D68EAB4EA9CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{29F9986B-4B32-4C30-AECA-D68EAB4EA9CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{29F9986B-4B32-4C30-AECA-D68EAB4EA9CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{29F9986B-4B32-4C30-AECA-D68EAB4EA9CB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{7C9E056B-CBA9-4548-9CDB-C5CE03C491B0} = {8163CDD7-7018-4301-A984-803C3807A6A6}
Expand Down Expand Up @@ -247,5 +253,6 @@ Global
{7FCFD4F6-AFB8-479C-A103-AEBC2FC282A8} = {FA80D231-C641-4A49-99C6-0C065D818B07}
{136A2850-FDA0-4CDB-A798-21C4B4A01974} = {8163CDD7-7018-4301-A984-803C3807A6A6}
{9D043E3D-878F-4D64-A689-7BE16A25842A} = {9DF1D09E-863F-4800-9E28-954440EE1601}
{29F9986B-4B32-4C30-AECA-D68EAB4EA9CB} = {FA80D231-C641-4A49-99C6-0C065D818B07}
EndGlobalSection
EndGlobal
76 changes: 65 additions & 11 deletions readme/Avalonia.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ This example demonstrates the creation of a [Avalonia](https://avaloniaui.net/)
> [!NOTE]
> [Another example](samples/SingleRootAvaloniaApp) with Avalonia shows how to create an application with a single composition root.
The definition of the composition is in [Composition.cs](/samples/AvaloniaApp/Composition.cs). You must not forget to define any necessary composition roots, for example, these can be view models such as _ClockViewModel_:
The definition of the composition is in [Composition.cs](/samples/AvaloniaApp/Composition.cs). This class setups how the composition of objects will be created for the application. You must not forget to define any necessary composition roots, for example, these can be view models such as _ClockViewModel_:

```csharp
using Pure.DI;
using static Pure.DI.Lifetime;

internal partial class Composition
{
void Setup() => DI.Setup()
Expand All @@ -32,24 +35,46 @@ internal partial class Composition
}
```

Advantages over classical DI container libraries:
- No performance impact or side effects when creating composition of objects.
- All logic for analyzing the graph of objects, constructors and methods takes place at compile time. Pure.DI notifies the developer at compile time of missing or cyclic dependencies, cases when some dependencies are not suitable for injection, etc.
- Does not add dependencies to any additional assembly.
- Since the generated code uses primitive language constructs to create object compositions and does not use any libraries, you can easily debug the object composition code as regular code in your application.

A single instance of the _Composition_ class is defined as a static resource in [App.xaml](/samples/AvaloniaApp/App.axaml) for later use within the _xaml_ markup everywhere:

```xaml
```xml
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="AvaloniaApp.App"
xmlns:local="using:AvaloniaApp"
RequestedThemeVariant="Default">
<Application.Resources>
<local:Composition x:Key="Composition" />
</Application.Resources>
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->

<!-- "Default" ThemeVariant follows system theme variant.
"Dark" or "Light" are other available options. -->
<Application.Styles>
<FluentTheme />
</Application.Styles>

<!--Creates a shared resource of type `Composition` and with key _‘Composition’_,
which will be further used as a data context in the views.-->
<Application.Resources>
<local:Composition x:Key="Composition" />
</Application.Resources>

</Application>
```

This markup fragment

```xml
<Application.Resources>
<local:Composition x:Key="Composition" />
</Application.Resources>
```

creates a shared resource of type `Composition` and with key _‘Composition’_, which will be further used as a data context in the views.

The associated application [App.axaml.cs](/samples/AvaloniaApp/App.axaml.cs) class is looking like:

```c#
Expand Down Expand Up @@ -85,20 +110,27 @@ public class App : Application
}
```

All previously defined composition roots are now accessible from [markup](/samples/AvaloniaApp/Views/MainWindow.xaml) without any effort, such as _ClockViewModel_:
Advantages over classical DI container libraries:
- No explicit initialisation of data contexts is required. Data contexts are configured directly in `.axaml` files according to the MVVM approach.
- The code is simpler, more compact, and requires less maintenance effort.
- The main window is created in a pure DI paradigm, and it can be easily supplied with all necessary dependencies via DI as regular types.

You can now use bindings to model views without even editing the views `.cs` code files. All previously defined composition roots are now accessible from [markup](/samples/AvaloniaApp/Views/MainWindow.xaml) without any effort, such as _ClockViewModel_:

```xaml
```xml
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:avaloniaApp="clr-namespace:AvaloniaApp"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="AvaloniaApp.Views.MainWindow"
x:DataType="avaloniaApp:Composition"
DataContext="{StaticResource Composition}"
xmlns:app="clr-namespace:AvaloniaApp"
x:DataType="app:Composition"
Title="{Binding ClockViewModel.Time}"
Icon="/Assets/avalonia-logo.ico">
Icon="/Assets/avalonia-logo.ico"
FontFamily="Consolas"
FontWeight="Bold">

<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" DataContext="{Binding ClockViewModel}">
<TextBlock Text="{Binding Date}" FontSize="64" HorizontalAlignment="Center" />
Expand All @@ -108,6 +140,28 @@ All previously defined composition roots are now accessible from [markup](/sampl
</Window>
```

To use bindings in views:

- You can set a shared resource as a data context

`DataContext="{StaticResource Composition}"`

- Specify the data type in the context:

`xmlns:app="clr-namespace:AvaloniaApp"`

`x:DataType="app:Composition"`

- Use the bindings as usual:

`Title="{Binding ClockViewModel.Time}"`

Advantages over classical DI container libraries:
- The code-behind `.cs` files for views are free of any logic.
- This approach works just as well during design time.
- You can easily use different view models in a single view.
- Bindings depend on properties through abstractions, which additionally ensures weak coupling of types in application. This is in line with the basic principles of DI.

The [project file](/samples/AvaloniaApp/AvaloniaApp.csproj) looks like this:

```xml
Expand Down
76 changes: 65 additions & 11 deletions readme/AvaloniaPageTemplate.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ This example demonstrates the creation of a [Avalonia](https://avaloniaui.net/)
> [!NOTE]
> [Another example](samples/SingleRootAvaloniaApp) with Avalonia shows how to create an application with a single composition root.
The definition of the composition is in [Composition.cs](/samples/AvaloniaApp/Composition.cs). You must not forget to define any necessary composition roots, for example, these can be view models such as _ClockViewModel_:
The definition of the composition is in [Composition.cs](/samples/AvaloniaApp/Composition.cs). This class setups how the composition of objects will be created for the application. You must not forget to define any necessary composition roots, for example, these can be view models such as _ClockViewModel_:

```csharp
using Pure.DI;
using static Pure.DI.Lifetime;

internal partial class Composition
{
void Setup() => DI.Setup()
Expand All @@ -32,24 +35,46 @@ internal partial class Composition
}
```

Advantages over classical DI container libraries:
- No performance impact or side effects when creating composition of objects.
- All logic for analyzing the graph of objects, constructors and methods takes place at compile time. Pure.DI notifies the developer at compile time of missing or cyclic dependencies, cases when some dependencies are not suitable for injection, etc.
- Does not add dependencies to any additional assembly.
- Since the generated code uses primitive language constructs to create object compositions and does not use any libraries, you can easily debug the object composition code as regular code in your application.

A single instance of the _Composition_ class is defined as a static resource in [App.xaml](/samples/AvaloniaApp/App.axaml) for later use within the _xaml_ markup everywhere:

```xaml
```xml
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="AvaloniaApp.App"
xmlns:local="using:AvaloniaApp"
RequestedThemeVariant="Default">
<Application.Resources>
<local:Composition x:Key="Composition" />
</Application.Resources>
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->

<!-- "Default" ThemeVariant follows system theme variant.
"Dark" or "Light" are other available options. -->
<Application.Styles>
<FluentTheme />
</Application.Styles>

<!--Creates a shared resource of type `Composition` and with key _‘Composition’_,
which will be further used as a data context in the views.-->
<Application.Resources>
<local:Composition x:Key="Composition" />
</Application.Resources>

</Application>
```

This markup fragment

```xml
<Application.Resources>
<local:Composition x:Key="Composition" />
</Application.Resources>
```

creates a shared resource of type `Composition` and with key _‘Composition’_, which will be further used as a data context in the views.

The associated application [App.axaml.cs](/samples/AvaloniaApp/App.axaml.cs) class is looking like:

```c#
Expand Down Expand Up @@ -85,20 +110,27 @@ public class App : Application
}
```

All previously defined composition roots are now accessible from [markup](/samples/AvaloniaApp/Views/MainWindow.xaml) without any effort, such as _ClockViewModel_:
Advantages over classical DI container libraries:
- No explicit initialisation of data contexts is required. Data contexts are configured directly in `.axaml` files according to the MVVM approach.
- The code is simpler, more compact, and requires less maintenance effort.
- The main window is created in a pure DI paradigm, and it can be easily supplied with all necessary dependencies via DI as regular types.

You can now use bindings to model views without even editing the views `.cs` code files. All previously defined composition roots are now accessible from [markup](/samples/AvaloniaApp/Views/MainWindow.xaml) without any effort, such as _ClockViewModel_:

```xaml
```xml
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:avaloniaApp="clr-namespace:AvaloniaApp"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="AvaloniaApp.Views.MainWindow"
x:DataType="avaloniaApp:Composition"
DataContext="{StaticResource Composition}"
xmlns:app="clr-namespace:AvaloniaApp"
x:DataType="app:Composition"
Title="{Binding ClockViewModel.Time}"
Icon="/Assets/avalonia-logo.ico">
Icon="/Assets/avalonia-logo.ico"
FontFamily="Consolas"
FontWeight="Bold">

<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" DataContext="{Binding ClockViewModel}">
<TextBlock Text="{Binding Date}" FontSize="64" HorizontalAlignment="Center" />
Expand All @@ -108,6 +140,28 @@ All previously defined composition roots are now accessible from [markup](/sampl
</Window>
```

To use bindings in views:

- You can set a shared resource as a data context

`DataContext="{StaticResource Composition}"`

- Specify the data type in the context:

`xmlns:app="clr-namespace:AvaloniaApp"`

`x:DataType="app:Composition"`

- Use the bindings as usual:

`Title="{Binding ClockViewModel.Time}"`

Advantages over classical DI container libraries:
- The code-behind `.cs` files for views are free of any logic.
- This approach works just as well during design time.
- You can easily use different view models in a single view.
- Bindings depend on properties through abstractions, which additionally ensures weak coupling of types in application. This is in line with the basic principles of DI.

The [project file](/samples/AvaloniaApp/AvaloniaApp.csproj) looks like this:

```xml
Expand Down
5 changes: 4 additions & 1 deletion readme/BlazorServerApp.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ This example demonstrates the creation of a [Blazor server](https://learn.micros
Composition setup file is [Composition.cs](/samples/BlazorServerApp/Composition.cs):

```c#
using Pure.DI;
using static Pure.DI.Lifetime;

internal partial class Composition: ServiceProviderFactory<Composition>
{
void Setup() => DI.Setup()
Expand Down Expand Up @@ -71,7 +74,7 @@ The [project file](/samples/BlazorServerApp/BlazorServerApp.csproj) looks like t
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Pure.DI.MS" Version="2.1.35" />
<PackageReference Include="Pure.DI.MS" Version="2.1.36" />
</ItemGroup>

</Project>
Expand Down
3 changes: 3 additions & 0 deletions readme/BlazorServerAppPageTemplate.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ This example demonstrates the creation of a [Blazor server](https://learn.micros
Composition setup file is [Composition.cs](/samples/BlazorServerApp/Composition.cs):

```c#
using Pure.DI;
using static Pure.DI.Lifetime;

internal partial class Composition: ServiceProviderFactory<Composition>
{
void Setup() => DI.Setup()
Expand Down
5 changes: 4 additions & 1 deletion readme/BlazorWebAssemblyApp.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ This example demonstrates the creation of a [Blazor WebAssembly](https://learn.m
Composition setup file is [Composition.cs](/samples/BlazorWebAssemblyApp/Composition.cs):

```c#
using Pure.DI;
using static Pure.DI.Lifetime;

internal partial class Composition: ServiceProviderFactory<Composition>
{
void Setup() => DI.Setup()
Expand Down Expand Up @@ -70,7 +73,7 @@ The [project file](/samples/BlazorWebAssemblyApp/BlazorWebAssemblyApp.csproj) lo
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Pure.DI.MS" Version="2.1.35" />
<PackageReference Include="Pure.DI.MS" Version="2.1.36" />
</ItemGroup>

</Project>
Expand Down
3 changes: 3 additions & 0 deletions readme/BlazorWebAssemblyAppPageTemplate.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ This example demonstrates the creation of a [Blazor WebAssembly](https://learn.m
Composition setup file is [Composition.cs](/samples/BlazorWebAssemblyApp/Composition.cs):

```c#
using Pure.DI;
using static Pure.DI.Lifetime;

internal partial class Composition: ServiceProviderFactory<Composition>
{
void Setup() => DI.Setup()
Expand Down
4 changes: 4 additions & 0 deletions readme/Console.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
This example demonstrates the creation of a simple console application in the pure DI paradigm using the _Pure.DI_ code generator. All code is in [one file](/samples/ShroedingersCat/Program.cs) for easy perception:

```c#
using Pure.DI;
using static Pure.DI.Lifetime;

namespace Sample;

// Let's create an abstraction
public interface IBox<out T>
Expand Down
4 changes: 4 additions & 0 deletions readme/ConsolePageTemplate.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
This example demonstrates the creation of a simple console application in the pure DI paradigm using the _Pure.DI_ code generator. All code is in [one file](/samples/ShroedingersCat/Program.cs) for easy perception:

```c#
using Pure.DI;
using static Pure.DI.Lifetime;

namespace Sample;

// Let's create an abstraction
public interface IBox<out T>
Expand Down
5 changes: 4 additions & 1 deletion readme/GrpcService.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ This example demonstrates the creation of a Web API application in the pure DI p
Composition setup file is [Composition.cs](/samples/GrpcService/Composition.cs):

```c#
using Pure.DI;
using static Pure.DI.Lifetime;

internal partial class Composition: ServiceProviderFactory<Composition>
{
void Setup() => DI.Setup()
Expand Down Expand Up @@ -56,7 +59,7 @@ The [project file](/samples/GrpcService/GrpcService.csproj) looks like this:
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Pure.DI.MS" Version="2.1.35" />
<PackageReference Include="Pure.DI.MS" Version="2.1.36" />
</ItemGroup>

</Project>
Expand Down
3 changes: 3 additions & 0 deletions readme/GrpcServicePageTemplate.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ This example demonstrates the creation of a Web API application in the pure DI p
Composition setup file is [Composition.cs](/samples/GrpcService/Composition.cs):

```c#
using Pure.DI;
using static Pure.DI.Lifetime;

internal partial class Composition: ServiceProviderFactory<Composition>
{
void Setup() => DI.Setup()
Expand Down
Loading

0 comments on commit 1f379a5

Please sign in to comment.