Initial commit.

This commit is contained in:
2026-03-13 10:23:47 +01:00
parent fd81c63020
commit 39c8b861c0
44 changed files with 1144 additions and 4 deletions

View File

@@ -0,0 +1,45 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net10.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF>
<RootNamespace>ClientApiPoC.ApiClient</RootNamespace>
<PublishSingleFile>true</PublishSingleFile>
<SelfContained>true</SelfContained>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
<PublishTrimmed>false</PublishTrimmed>
<Company>Mike Schumann</Company>
<Copyright>Copyright © 2026 $(Company)</Copyright>
<AssemblyVersion>0.1.0</AssemblyVersion>
<FileVersion>$(AssemblyVersion)</FileVersion>
<NeutralLanguage>en-US</NeutralLanguage>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<DebugType>portable</DebugType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<DebugType>none</DebugType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.5" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.5" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Shared.Wpf\Shared.Wpf.csproj" />
<ProjectReference Include="..\Shared\Shared.csproj" />
</ItemGroup>
</Project>

8
ApiClient/App.xaml Normal file
View File

@@ -0,0 +1,8 @@
<Application x:Class="ClientApiPoC.ApiClient.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ClientApiPoC.ApiClient">
<Application.Resources>
</Application.Resources>
</Application>

33
ApiClient/App.xaml.cs Normal file
View File

@@ -0,0 +1,33 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Windows;
namespace ClientApiPoC.ApiClient {
public partial class App : Application {
private IHost _host;
public App() {
_host = Host.CreateDefaultBuilder().ConfigureServices(services => {
// ViewModels:
services.AddTransient<MainWindowViewModel>();
// Views:
services.AddSingleton<MainWindow>();
}).Build();
}
protected override async void OnStartup(StartupEventArgs e) {
await _host.StartAsync();
var window = _host.Services.GetRequiredService<MainWindow>();
window.Show();
base.OnStartup(e);
}
protected override async void OnExit(ExitEventArgs e) {
await _host.StopAsync();
_host.Dispose();
base.OnExit(e);
}
}
}

10
ApiClient/AssemblyInfo.cs Normal file
View File

@@ -0,0 +1,10 @@
using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]

62
ApiClient/MainWindow.xaml Normal file
View File

@@ -0,0 +1,62 @@
<wpf:MvvmWindow x:TypeArguments="local:MainWindowViewModel"
x:Class="ClientApiPoC.ApiClient.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
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:local="clr-namespace:ClientApiPoC.ApiClient"
xmlns:wpf="clr-namespace:ClientApiPoC.Shared.Wpf;assembly=Shared.Wpf"
mc:Ignorable="d"
Title="API-Client"
Width="512"
Height="384"
ResizeMode="CanMinimize">
<Grid RowDefinitions="AUTO, AUTO, *"
ColumnDefinitions="*, AUTO"
Margin="12">
<Grid Grid.Row="0"
Grid.Column="0"
RowDefinitions="AUTO, AUTO"
ColumnDefinitions="*">
<TextBlock Grid.Row="0"
Grid.Column="0"
Text="Base-URL:" />
<TextBox Grid.Row="1"
Grid.Column="0"
VerticalAlignment="Center"
IsEnabled="{Binding ControlsEnabled}"
Text="{Binding ApiServiceUrl}" />
</Grid>
<Button Grid.Row="0"
Grid.Column="1"
VerticalAlignment="Stretch"
Padding="5"
Margin="5, 0, 0, 0"
IsDefault="True"
Content="HTTP-GET"
IsEnabled="{Binding ControlsEnabled}"
Command="{Binding HttpGetButtonCommand}" />
<TextBlock Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="0, 5, 0, 0"
Text="Result:" />
<TextBox Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="2"
Padding="5"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"
IsReadOnly="True"
FontFamily="Consolas"
Text="{Binding RequestResultDataString, Mode=OneWay}" />
</Grid>
</wpf:MvvmWindow>

View File

@@ -0,0 +1,10 @@
using ClientApiPoC.Shared.Wpf;
using System.Windows;
namespace ClientApiPoC.ApiClient {
public partial class MainWindow : MvvmWindow<MainWindowViewModel> {
public MainWindow(MainWindowViewModel viewModel) : base(viewModel) {
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,92 @@
using ClientApiPoC.Shared;
using ClientApiPoC.Shared.Models;
using ClientApiPoC.Shared.Wpf;
using CommunityToolkit.Mvvm.Input;
using System.Net.Http;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Windows.Input;
namespace ClientApiPoC.ApiClient {
public class MainWindowViewModel : BaseViewModel {
#region Properties
private string _apiServiceUrl = "http://localhost:5228/";
public string ApiServiceUrl {
get => _apiServiceUrl;
set {
_apiServiceUrl = value;
OnPropertyChanged(nameof(ApiServiceUrl));
}
}
private bool _isFetchingData = false;
public bool IsFetchingData {
get => _isFetchingData;
set {
_isFetchingData = value;
OnPropertyChanged(nameof(IsFetchingData));
OnPropertyChanged(nameof(ControlsEnabled));
}
}
public bool ControlsEnabled => !this.IsFetchingData;
private ExampleRequestResultModel? _requestResultData = null;
public ExampleRequestResultModel? RequestResultData {
get => _requestResultData;
set {
_requestResultData = value;
OnPropertyChanged(nameof(RequestResultData));
OnPropertyChanged(nameof(RequestResultDataString));
}
}
public string RequestResultDataString {
get {
if (this.RequestResultData == null) return "NULL";
var options = new JsonSerializerOptions() {
IndentCharacter = ' ',
IndentSize = 2,
NewLine = "\n",
WriteIndented = true
};
return JsonSerializer.Serialize(this.RequestResultData, options);
}
}
#endregion
#region Commands
public ICommand HttpGetButtonCommand { get; }
#endregion
public MainWindowViewModel() {
this.HttpGetButtonCommand = new AsyncRelayCommand(HttpGetButtonActionAsync);
}
private async Task HttpGetButtonActionAsync() {
if (this.IsFetchingData) return;
try {
this.IsFetchingData = true;
var url = this.ApiServiceUrl;
if (string.IsNullOrWhiteSpace(url)) throw new InvalidOperationException("No base url specified.");
while (url.EndsWith('/')) url = url[..^1];
url += Routes.EXAMPLE_REQUEST;
using (var httpClient = Tools.CreateHttpClient()) {
var request = new HttpRequestMessage(HttpMethod.Get, url);
var response = await httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
var options = new JsonSerializerOptions() {
PropertyNameCaseInsensitive = true
};
var result = JsonSerializer.Deserialize<ExampleRequestResultModel>(responseString, options);
this.RequestResultData = result;
}
} catch (Exception ex) {
await HandleErrorAsync(ex);
} finally {
this.IsFetchingData = false;
}
}
}
}

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
<Project>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>..\Publish\</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId>
<TargetFramework>net10.0-windows</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishReadyToRun>false</PublishReadyToRun>
</PropertyGroup>
</Project>