Initial commit.
This commit is contained in:
8
OnPremiseApp/App.xaml
Normal file
8
OnPremiseApp/App.xaml
Normal file
@@ -0,0 +1,8 @@
|
||||
<Application x:Class="ClientApiPoC.OnPremiseApp.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:ClientApiPoC.OnPremiseApp">
|
||||
<Application.Resources>
|
||||
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
36
OnPremiseApp/App.xaml.cs
Normal file
36
OnPremiseApp/App.xaml.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System.Windows;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using ClientApiPoC.OnPremiseApp.Services;
|
||||
|
||||
namespace ClientApiPoC.OnPremiseApp {
|
||||
public partial class App : Application {
|
||||
private IHost _host;
|
||||
|
||||
public App() {
|
||||
_host = Host.CreateDefaultBuilder().ConfigureServices(services => {
|
||||
// Services:
|
||||
services.AddSingleton<ClientDataService>();
|
||||
// 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
OnPremiseApp/AssemblyInfo.cs
Normal file
10
OnPremiseApp/AssemblyInfo.cs
Normal 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)
|
||||
)]
|
||||
76
OnPremiseApp/MainWindow.xaml
Normal file
76
OnPremiseApp/MainWindow.xaml
Normal file
@@ -0,0 +1,76 @@
|
||||
<wpf:MvvmWindow x:TypeArguments="local:MainWindowViewModel"
|
||||
x:Class="ClientApiPoC.OnPremiseApp.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.OnPremiseApp"
|
||||
xmlns:wpf="clr-namespace:ClientApiPoC.Shared.Wpf;assembly=Shared.Wpf"
|
||||
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
|
||||
mc:Ignorable="d"
|
||||
Title="On-Premise Gateway"
|
||||
Width="384"
|
||||
Height="AUTO"
|
||||
MaxHeight="144"
|
||||
ResizeMode="CanMinimize"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
<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 UrlTextFieldEnabled}"
|
||||
Text="{Binding ApiServiceUrl}" />
|
||||
|
||||
</Grid>
|
||||
|
||||
<Button Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Stretch"
|
||||
Padding="5"
|
||||
Margin="5, 0, 0, 0"
|
||||
Content="{Binding ConnectButtonText}"
|
||||
IsEnabled="{Binding ConnectButtonEnabled}"
|
||||
Command="{Binding ToggleConnectionCommand}" />
|
||||
|
||||
<Grid Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
RowDefinitions="AUTO, AUTO"
|
||||
ColumnDefinitions="*"
|
||||
Margin="0, 12, 0, 0">
|
||||
|
||||
<TextBlock Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Text="Local client data:" />
|
||||
|
||||
<TextBox Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding LocalClientData, UpdateSourceTrigger=PropertyChanged}">
|
||||
</TextBox>
|
||||
|
||||
</Grid>
|
||||
|
||||
<Button Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Stretch"
|
||||
Padding="5"
|
||||
Margin="5, 12, 0, 0"
|
||||
IsDefault="True"
|
||||
Content="Set data"
|
||||
IsEnabled="{Binding SetLocalClientDataButtonEnabled}"
|
||||
Command="{Binding SetLocalClientDataCommand}" />
|
||||
|
||||
</Grid>
|
||||
</wpf:MvvmWindow>
|
||||
9
OnPremiseApp/MainWindow.xaml.cs
Normal file
9
OnPremiseApp/MainWindow.xaml.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using ClientApiPoC.Shared.Wpf;
|
||||
|
||||
namespace ClientApiPoC.OnPremiseApp {
|
||||
public partial class MainWindow : MvvmWindow<MainWindowViewModel> {
|
||||
public MainWindow(MainWindowViewModel viewModel) : base(viewModel) {
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
114
OnPremiseApp/MainWindowViewModel.cs
Normal file
114
OnPremiseApp/MainWindowViewModel.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
using System.ComponentModel;
|
||||
using System.Windows.Input;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using ClientApiPoC.OnPremiseApp.Services;
|
||||
using ClientApiPoC.Shared;
|
||||
using ClientApiPoC.Shared.Wpf;
|
||||
using ClientApiPoC.Shared.SignalR;
|
||||
|
||||
namespace ClientApiPoC.OnPremiseApp {
|
||||
public class MainWindowViewModel : BaseViewModel {
|
||||
private ClientDataService _clientDataService;
|
||||
private TunnelClient _tunnelClient;
|
||||
|
||||
#region Properties
|
||||
private string _apiServiceUrl = "http://localhost:5228/";
|
||||
public string ApiServiceUrl {
|
||||
get => _apiServiceUrl;
|
||||
set {
|
||||
_apiServiceUrl = value;
|
||||
OnPropertyChanged(nameof(ApiServiceUrl));
|
||||
}
|
||||
}
|
||||
|
||||
private bool _tunnelClientIsConnecting = false;
|
||||
public bool TunnelClientIsConnecting {
|
||||
get => _tunnelClientIsConnecting;
|
||||
set {
|
||||
_tunnelClientIsConnecting = value;
|
||||
OnPropertyChanged(nameof(TunnelClientIsConnecting));
|
||||
OnPropertyChanged(nameof(ConnectButtonEnabled));
|
||||
OnPropertyChanged(nameof(UrlTextFieldEnabled));
|
||||
}
|
||||
}
|
||||
|
||||
public bool TunnelClientConnected => (_tunnelClient.IsConnected || _tunnelClient.IsConnecting);
|
||||
|
||||
public string ConnectButtonText => (this.TunnelClientConnected ? "Disconnect" : "Connect");
|
||||
|
||||
public bool ConnectButtonEnabled => !this.TunnelClientIsConnecting;
|
||||
|
||||
public bool UrlTextFieldEnabled => !this.TunnelClientIsConnecting && !this.TunnelClientConnected;
|
||||
|
||||
private string _localClientData = "";
|
||||
public string LocalClientData {
|
||||
get => _localClientData;
|
||||
set {
|
||||
_localClientData = value;
|
||||
OnPropertyChanged(nameof(LocalClientData));
|
||||
OnPropertyChanged(nameof(SetLocalClientDataButtonEnabled));
|
||||
}
|
||||
}
|
||||
|
||||
public bool SetLocalClientDataButtonEnabled {
|
||||
get {
|
||||
if (string.IsNullOrEmpty(this.LocalClientData) && string.IsNullOrEmpty(_clientDataService.ClientData)) return false;
|
||||
return !string.Equals(this.LocalClientData, _clientDataService.ClientData);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Commands
|
||||
public ICommand ToggleConnectionCommand { get; }
|
||||
|
||||
public ICommand SetLocalClientDataCommand { get; }
|
||||
#endregion
|
||||
|
||||
public MainWindowViewModel(ClientDataService clientDataService) {
|
||||
if (clientDataService == null) throw new ArgumentNullException(nameof(clientDataService));
|
||||
_clientDataService = clientDataService;
|
||||
_tunnelClient = new TunnelClient();
|
||||
_tunnelClient.PropertyChanged += TunnelClient_PropertyChanged;
|
||||
this.ToggleConnectionCommand = new AsyncRelayCommand(ToggleConnectionAsync);
|
||||
this.SetLocalClientDataCommand = new AsyncRelayCommand(SetLocalClientDataAsync);
|
||||
}
|
||||
|
||||
private void TunnelClient_PropertyChanged(object? sender, PropertyChangedEventArgs e) {
|
||||
OnPropertyChanged(nameof(TunnelClientConnected));
|
||||
OnPropertyChanged(nameof(ConnectButtonText));
|
||||
OnPropertyChanged(nameof(UrlTextFieldEnabled));
|
||||
}
|
||||
|
||||
private async Task ToggleConnectionAsync() {
|
||||
if (this.TunnelClientIsConnecting) return;
|
||||
try {
|
||||
this.TunnelClientIsConnecting = true;
|
||||
if (this.TunnelClientConnected) {
|
||||
await _tunnelClient.DisconnectAsync();
|
||||
} else {
|
||||
var url = this.ApiServiceUrl;
|
||||
if (string.IsNullOrWhiteSpace(url)) throw new Exception("Please provide a base url.");
|
||||
while (url.EndsWith('/')) url = url[..^1];
|
||||
url += Routes.TUNNEL_PATH;
|
||||
await _tunnelClient.ConnectAsync(url, _clientDataService.ConfigureTunnelActions);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
await HandleErrorAsync(ex);
|
||||
} finally {
|
||||
this.TunnelClientIsConnecting = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SetLocalClientDataAsync() {
|
||||
try {
|
||||
string? newClientData = this.LocalClientData;
|
||||
if (string.IsNullOrEmpty(newClientData)) newClientData = null;
|
||||
_clientDataService.ClientData = newClientData;
|
||||
} catch (Exception ex) {
|
||||
await HandleErrorAsync(ex);
|
||||
} finally {
|
||||
OnPropertyChanged(nameof(SetLocalClientDataButtonEnabled));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
45
OnPremiseApp/OnPremiseApp.csproj
Normal file
45
OnPremiseApp/OnPremiseApp.csproj
Normal 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.OnPremiseApp</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>
|
||||
15
OnPremiseApp/Properties/PublishProfiles/FolderProfile.pubxml
Normal file
15
OnPremiseApp/Properties/PublishProfiles/FolderProfile.pubxml
Normal 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>
|
||||
21
OnPremiseApp/Services/ClientDataService.cs
Normal file
21
OnPremiseApp/Services/ClientDataService.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using ClientApiPoC.Shared.Models;
|
||||
|
||||
namespace ClientApiPoC.OnPremiseApp.Services {
|
||||
public class ClientDataService {
|
||||
public string? ClientData { get; set; } = null;
|
||||
|
||||
public async Task<ClientDataModel> GetClientDataAsync(DateTime timestampServer) {
|
||||
return new ClientDataModel() {
|
||||
Data = this.ClientData,
|
||||
TimestampServerUtc = timestampServer,
|
||||
TimestampClientUtc = DateTime.UtcNow
|
||||
};
|
||||
}
|
||||
|
||||
public void ConfigureTunnelActions(HubConnection tunnelConnection) {
|
||||
if (tunnelConnection == null) throw new ArgumentNullException(nameof(tunnelConnection));
|
||||
tunnelConnection.On<DateTime, ClientDataModel>("GetClientData", GetClientDataAsync);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user