This commit is contained in:
Thomas Peterson 2024-11-08 18:50:58 +01:00
parent 7e1dc0e283
commit dbeb01a9f6
20 changed files with 287 additions and 163 deletions

View File

@ -12,9 +12,19 @@
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="95257dc5-08bd-4c50-8726-85956b3c2c92" name="Changes" comment=""> <list default="true" id="95257dc5-08bd-4c50-8726-85956b3c2c92" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/.idea/.idea.PSCHelpdesk/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.PSCHelpdesk/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/.idea.PSCHelpdesk/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.PSCHelpdesk/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/HetznerServer/HetznerServer.cs" beforeDir="false" afterPath="$PROJECT_DIR$/HetznerServer/HetznerServer.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/HetznerServer/HetznerServerBootstrap.cs" beforeDir="false" afterPath="$PROJECT_DIR$/HetznerServer/HetznerServerBootstrap.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/HetznerServer/Models/Server.cs" beforeDir="false" afterPath="$PROJECT_DIR$/HetznerServer/Models/Server.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/HetznerServer/ViewModels/ServerViewModel.cs" beforeDir="false" afterPath="$PROJECT_DIR$/HetznerServer/ViewModels/ServerViewModel.cs" afterDir="false" /> <change beforePath="$PROJECT_DIR$/HetznerServer/ViewModels/ServerViewModel.cs" beforeDir="false" afterPath="$PROJECT_DIR$/HetznerServer/ViewModels/ServerViewModel.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/HetznerServer/Views/ServerView.axaml" beforeDir="false" afterPath="$PROJECT_DIR$/HetznerServer/Views/ServerView.axaml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/HetznerServer/Views/ServerView.axaml" beforeDir="false" afterPath="$PROJECT_DIR$/HetznerServer/Views/ServerView.axaml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/HetznerServer/Views/ServerView.axaml.cs" beforeDir="false" afterPath="$PROJECT_DIR$/HetznerServer/Views/ServerView.axaml.cs" afterDir="false" /> <change beforePath="$PROJECT_DIR$/PSCHelpdesk.sln.DotSettings.user" beforeDir="false" afterPath="$PROJECT_DIR$/PSCHelpdesk.sln.DotSettings.user" afterDir="false" />
<change beforePath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/Services/MenuService.cs" beforeDir="false" afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/Services/MenuService.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/Startup.cs" beforeDir="false" afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/Startup.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/ViewModels/MainWindowViewModel.cs" beforeDir="false" afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/ViewModels/MainWindowViewModel.cs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/ViewModels/TestViewModel.cs" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/Views/MainWindow.axaml" beforeDir="false" afterPath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/Views/MainWindow.axaml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/Views/TestView.axaml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/PSCHelpdesk/PSCHelpdesk/Views/TestView.axaml.cs" beforeDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -32,6 +42,7 @@
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/27be5d106a789638721745a2a46f9cb0f7a39905117d644638b6e0d56261be/ReplaySubject.cs" root0="FORCE_HIGHLIGHTING" /> <setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/27be5d106a789638721745a2a46f9cb0f7a39905117d644638b6e0d56261be/ReplaySubject.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/2e6883f773fb7c69a15db509adac9a0c068e4ca54fa119e835fd2324311c3b/Ioc.cs" root0="FORCE_HIGHLIGHTING" /> <setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/2e6883f773fb7c69a15db509adac9a0c068e4ca54fa119e835fd2324311c3b/Ioc.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/3c92637ae2e83da0a63791071c41eae291d594156062866d8621b7ed7245c/CastHelpers.cs" root0="FORCE_HIGHLIGHTING" /> <setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/3c92637ae2e83da0a63791071c41eae291d594156062866d8621b7ed7245c/CastHelpers.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/548232ed526ec850f8e542b7449b4145e5c2c61a6576c9328c36e56a27e4/ThrowHelper.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/5cde391207de75962d7bacb899ca2bd3985c86911b152d185b58999a422bf0/Type.CoreCLR.cs" root0="FORCE_HIGHLIGHTING" /> <setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/5cde391207de75962d7bacb899ca2bd3985c86911b152d185b58999a422bf0/Type.CoreCLR.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/9d4f4ac7db6d2c5d183ab2d92602280ed4349fd6e6a1b6313546b3d01fdab5/ServiceProvider.cs" root0="FORCE_HIGHLIGHTING" /> <setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/9d4f4ac7db6d2c5d183ab2d92602280ed4349fd6e6a1b6313546b3d01fdab5/ServiceProvider.cs" root0="FORCE_HIGHLIGHTING" />
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/c7102cd0ffb8973777e61b1942c3fffac7e14016a511d055c3adf73ff91748/ThrowHelper.cs" root0="FORCE_HIGHLIGHTING" /> <setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/c7102cd0ffb8973777e61b1942c3fffac7e14016a511d055c3adf73ff91748/ThrowHelper.cs" root0="FORCE_HIGHLIGHTING" />

View File

@ -0,0 +1,28 @@
using System.Globalization;
using Avalonia.Data.Converters;
using Avalonia.Media;
namespace PSCHelpdesk.Plugins.HetznerServer.Converter;
public class DateTimeConverter: IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
var item = (DateTime)value;
var brush = new SolidColorBrush();
brush.Color = Colors.Red;
if ((DateTime.Now - item).TotalDays > 2) return brush;
brush.Color = Colors.Green;
return brush;
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value.Equals("RUNNING")) return "green";
return "red";
}
}

View File

@ -0,0 +1,27 @@
using System.Globalization;
using Avalonia.Data.Converters;
using Avalonia.Media;
namespace PSCHelpdesk.Plugins.HetznerServer.Converter;
public class StatusConverter: IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
var brush = new SolidColorBrush();
brush.Color = Colors.Green;
if (value.Equals("running")) return brush;
brush.Color = Colors.Red;
return brush;
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value.Equals("RUNNING")) return "green";
return "red";
}
}

View File

@ -2,6 +2,7 @@ using Avalonia.Controls;
using CommunityToolkit.Mvvm.DependencyInjection; using CommunityToolkit.Mvvm.DependencyInjection;
using Prise.Plugin; using Prise.Plugin;
using PSCHelpdesk.Plugins.HetznerServer.Menu; using PSCHelpdesk.Plugins.HetznerServer.Menu;
using PSCHelpdesk.Plugins.HetznerServer.Service;
using PSCHelpdesk.Plugins.HetznerServer.ViewModels; using PSCHelpdesk.Plugins.HetznerServer.ViewModels;
using PSCHelpdesk.Plugins.HetznerServer.Views; using PSCHelpdesk.Plugins.HetznerServer.Views;
using PSCHelpdesk.Shared.Menu; using PSCHelpdesk.Shared.Menu;
@ -24,7 +25,7 @@ public class HetznerServer : Contract
var serverTab = new Item() var serverTab = new Item()
{ {
Header = "Server", Header = "Server",
CommandParameter = new ServerViewModel() CommandParameter = new ServerViewModel(new ServerService())
}; };
menuService.AddMenuItem(serverTab); menuService.AddMenuItem(serverTab);

View File

@ -1,19 +1,17 @@
using CommunityToolkit.Mvvm.DependencyInjection;
using PSCHelpdesk.Plugins.HetznerServer.ViewModels;
using PSCHelpdesk.Shared.Service;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Prise.Plugin; using Prise.Plugin;
using PSCHelpdesk.Plugins.HetznerServer.Service;
using PSCHelpdesk.Plugins.HetznerServer.ViewModels;
namespace PSCHelpdesk.Plugins.HetznerServer; namespace PSCHelpdesk.Plugins.HetznerServer;
[PluginBootstrapper(PluginType = typeof(HetznerServer))]
public class HetznerServerBootstrap : IPluginBootstrapper public class HetznerServerBootstrap : IPluginBootstrapper
{ {
private readonly IMenuService menuService;
public IServiceCollection Bootstrap(IServiceCollection services) public IServiceCollection Bootstrap(IServiceCollection services)
{ {
services.AddSingleton<IMenuService>(this.menuService); services.AddScoped<IServerService, ServerService>();
services.AddTransient<ServerViewModel>(); services.AddTransient<ServerViewModel>();
return services; return services;
} }

View File

@ -4,7 +4,7 @@ using System.Runtime.CompilerServices;
namespace PSCHelpdesk.Plugins.HetznerServer.Models; namespace PSCHelpdesk.Plugins.HetznerServer.Models;
public class Server: INotifyPropertyChanged public class Server
{ {
public long Id { get; set; } public long Id { get; set; }
public string Name { get; set; } public string Name { get; set; }
@ -24,11 +24,4 @@ public class Server: INotifyPropertyChanged
Ipv4 = ipv4; Ipv4 = ipv4;
Ipv6 = ipv6; Ipv6 = ipv6;
} }
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
} }

View File

@ -0,0 +1,6 @@
namespace PSCHelpdesk.Plugins.HetznerServer.Service;
public interface IServerService
{
}

View File

@ -0,0 +1,57 @@
using System.Collections.ObjectModel;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.DependencyInjection;
using DynamicData;
using DynamicData.Binding;
using HetznerCloudApi;
using PSCHelpdesk.Plugins.HetznerServer.Models;
using PSCHelpdesk.Shared.Service;
using PSCHelpdesk.Shared.Setting;
using ReactiveUI;
namespace PSCHelpdesk.Plugins.HetznerServer.Service;
public class ServerService: IServerService
{
public SourceCache<Server, long> SourceCache = new (x => x.Id);
private SettingsManager _settingsManager;
private readonly DispatcherTimer _reloadTimer = new DispatcherTimer();
private string _searchText = string.Empty;
public ServerService()
{
_settingsManager = (SettingsManager)Ioc.Default.GetService<ISettingsManager>();
this._reloadTimer.Tick += (sender, args) =>
{
this.reloadServer();
};
_reloadTimer.Interval = TimeSpan.FromMinutes(10);
_reloadTimer.Start();
Task.Run(async () => await this.reloadServer());
}
private async Task reloadServer()
{
var settings = new Settings();
_settingsManager.LoadPluginSettings("HetznerSettings", settings);
if (settings.HetznerApiKey != "")
{
HetznerCloudClient hetznerCloudClient = new HetznerCloudClient(settings.HetznerApiKey);
List<HetznerCloudApi.Object.Server.Server> list = await hetznerCloudClient.Server.Get();
foreach (var server in list)
{
var uiDispatcher = Ioc.Default.GetService<IUserInterfaceDispatchService>();
await uiDispatcher.InvokeAsync(() =>
{
Console.WriteLine($"Server: {server.Name}");
SourceCache.AddOrUpdate(new Server(server.Id, server.Name, server.ServerType.Name, server.Status,
server.PublicNet.Ipv4.Ip, server.PublicNet.Ipv6.Ip));
});
}
}
}
}

View File

@ -5,8 +5,11 @@ using Avalonia.Controls;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using CommunityToolkit.Mvvm.DependencyInjection; using CommunityToolkit.Mvvm.DependencyInjection;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using DynamicData;
using DynamicData.Binding;
using HetznerCloudApi; using HetznerCloudApi;
using PSCHelpdesk.Plugins.HetznerServer.Models; using PSCHelpdesk.Plugins.HetznerServer.Models;
using PSCHelpdesk.Plugins.HetznerServer.Service;
using PSCHelpdesk.Plugins.HetznerServer.Views; using PSCHelpdesk.Plugins.HetznerServer.Views;
using PSCHelpdesk.Shared.Service; using PSCHelpdesk.Shared.Service;
using PSCHelpdesk.Shared.Setting; using PSCHelpdesk.Shared.Setting;
@ -17,17 +20,27 @@ namespace PSCHelpdesk.Plugins.HetznerServer.ViewModels;
public partial class ServerViewModel : ViewModelBase, IViewModelBase public partial class ServerViewModel : ViewModelBase, IViewModelBase
{ {
public readonly ServerService ServerService;
public ReactiveCommand<SelectionChangedEventArgs, Unit> SelectionChanged { get; } public ReactiveCommand<SelectionChangedEventArgs, Unit> SelectionChanged { get; }
private SettingsManager _settingsManager; protected readonly ReadOnlyObservableCollection<Server> _server;
public ServerViewModel() public ReadOnlyObservableCollection<Server> Server => _server;
{
SelectionChanged = ReactiveCommand.Create<SelectionChangedEventArgs>(selectionChanged);
Server = new ObservableCollection<Server>();
SelectedServer = new Server(1,"","","","","");
_settingsManager = (SettingsManager)Ioc.Default.GetService<ISettingsManager>();
loadServers();
private SettingsManager _settingsManager;
public ServerViewModel(ServerService _serverService)
{
SelectedServer = new Server(1,"","","","","");
SelectionChanged = ReactiveCommand.Create<SelectionChangedEventArgs>(selectionChanged);
_settingsManager = (SettingsManager)Ioc.Default.GetService<ISettingsManager>();
ServerService = _serverService;
ServerService.SourceCache.Connect()
// Sort Ascending on the OrderIndex property
.Sort(SortExpressionComparer<Server>.Ascending(t => t.Id))
.Filter(x => x.Name.Contains(_searchText, StringComparison.OrdinalIgnoreCase))
// Bind to our ReadOnlyObservableCollection<T>
.Bind(out _server)
// Subscribe for changes
.Subscribe();
} }
private bool _isPagePaneOpen = true; private bool _isPagePaneOpen = true;
@ -37,6 +50,17 @@ public partial class ServerViewModel : ViewModelBase, IViewModelBase
set => SetAndRaisePropertyChanged(ref _isPagePaneOpen, value); set => SetAndRaisePropertyChanged(ref _isPagePaneOpen, value);
} }
private string _searchText = "";
public string SearchText
{
get => _searchText;
set
{
SetAndRaisePropertyChanged(ref _searchText, value);
ServerService.SourceCache.Refresh();
}
}
private Server _selectedServer; private Server _selectedServer;
public Server SelectedServer public Server SelectedServer
{ {
@ -44,31 +68,7 @@ public partial class ServerViewModel : ViewModelBase, IViewModelBase
set => SetAndRaisePropertyChanged(ref _selectedServer, value); set => SetAndRaisePropertyChanged(ref _selectedServer, value);
} }
private ObservableCollection<Server> _server;
public ObservableCollection<Server> Server
{
get => _server;
set => SetAndRaisePropertyChanged(ref _server, value);
}
private async void loadServers()
{
var settings = new Settings();
_settingsManager.LoadPluginSettings("HetznerSettings", settings);
if (settings.HetznerApiKey != "")
{
HetznerCloudClient hetznerCloudClient = new HetznerCloudClient(settings.HetznerApiKey);
List<HetznerCloudApi.Object.Server.Server> list = await hetznerCloudClient.Server.Get();
foreach (var server in list)
{
Server.Add(new Server(server.Id, server.Name, server.ServerType.Name, server.Status,
server.PublicNet.Ipv4.Ip, server.PublicNet.Ipv6.Ip));
}
SelectedServer = Server[0];
}
}
void selectionChanged(SelectionChangedEventArgs args) void selectionChanged(SelectionChangedEventArgs args)
{ {
@ -78,13 +78,13 @@ public partial class ServerViewModel : ViewModelBase, IViewModelBase
public void SSH(Server server) public void SSH(Server server)
{ {
Process.Start(_settingsManager.CoreSettings.SSHClientExecutable,string.Format(_settingsManager.CoreSettings.SSHClientExecutableArgs, server.Ipv4, _settingsManager.CoreSettings.PrivateSSHKeyPath)); Process.Start(_settingsManager.CoreSettings.SSHClientExecutable,string.Format(_settingsManager.CoreSettings.SSHClientExecutableArgs, server.Ipv4, _settingsManager.CoreSettings.PrivateSSHKeyPath, server.Name));
} }
public void SCP(Server server) public void SCP(Server server)
{ {
Process.Start(_settingsManager.CoreSettings.SCPClientExecutable,string.Format(_settingsManager.CoreSettings.SCPClientExecutableArgs, server.Ipv4, _settingsManager.CoreSettings.PrivateSSHKeyPath)); Process.Start(_settingsManager.CoreSettings.SCPClientExecutable,string.Format(_settingsManager.CoreSettings.SCPClientExecutableArgs, server.Ipv4, _settingsManager.CoreSettings.PrivateSSHKeyPath, server.Name));
} }

View File

@ -3,6 +3,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:PSCHelpdesk.Plugins.HetznerServer.ViewModels" xmlns:vm="clr-namespace:PSCHelpdesk.Plugins.HetznerServer.ViewModels"
xmlns:converter="clr-namespace:PSCHelpdesk.Plugins.HetznerServer.Converter"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="PSCHelpdesk.Plugins.HetznerServer.Views.ServerView" x:Class="PSCHelpdesk.Plugins.HetznerServer.Views.ServerView"
x:DataType="vm:ServerViewModel" x:DataType="vm:ServerViewModel"
@ -10,68 +11,86 @@
<Design.DataContext> <Design.DataContext>
<vm:ServerViewModel></vm:ServerViewModel> <vm:ServerViewModel></vm:ServerViewModel>
</Design.DataContext> </Design.DataContext>
<UserControl.Resources>
<converter:StatusConverter x:Key="StatusConverter"></converter:StatusConverter>
<converter:DateTimeConverter x:Key="DateTimeConverter"></converter:DateTimeConverter>
</UserControl.Resources>
<SplitView IsPaneOpen="{Binding IsPagePaneOpen }" <SplitView IsPaneOpen="{Binding IsPagePaneOpen }"
DisplayMode="Inline" DisplayMode="Inline"
OpenPaneLength="300" PanePlacement="Right"> PanePlacement="Right">
<DataGrid Margin="20" ItemsSource="{Binding Server}" <Grid Margin="20" RowDefinitions="Auto,*">
AutoGenerateColumns="False" IsReadOnly="True" <StackPanel Grid.Row="0">
GridLinesVisibility="All" <TextBlock Margin="0 5" >Suchtext</TextBlock>
BorderThickness="1" BorderBrush="Gray" <TextBox Text="{Binding SearchText}"></TextBox>
x:Name="ServerTable"> </StackPanel>
<Interaction.Behaviors> <DataGrid Grid.Row="1" ItemsSource="{Binding Server}"
<EventTriggerBehavior EventName="SelectionChanged"> AutoGenerateColumns="False" IsReadOnly="True"
<InvokeCommandAction Command="{Binding SelectionChanged}" PassEventArgsToCommand="true"></InvokeCommandAction> GridLinesVisibility="All"
</EventTriggerBehavior> BorderThickness="1" BorderBrush="Gray"
</Interaction.Behaviors> x:Name="ServerTable">
<DataGrid.Columns> <DataGrid.Styles>
<DataGridTextColumn Header="Id" Binding="{Binding Id}"/> <Style Selector="DataGridCell.status">
<DataGridTextColumn Header="Name" Binding="{Binding Name}" /> <Setter Property="Foreground" Value="{Binding Status, Converter={StaticResource StatusConverter}}" />
<DataGridTextColumn Header="Status" Binding="{Binding Status}" /> </Style>
<DataGridTextColumn Header="Type" Binding="{Binding Type}" /> <Style Selector="DataGridCell.lastBackup">
<DataGridTextColumn Header="IpV4" Binding="{Binding Ipv4}" /> <Setter Property="Foreground" Value="{Binding BackupLastModified, Converter={StaticResource DateTimeConverter}}" />
<DataGridTextColumn Header="IpV6" Binding="{Binding Ipv6}" /> </Style>
<DataGridTemplateColumn Width="*" Header=""> </DataGrid.Styles>
<DataGridTemplateColumn.CellTemplate> <Interaction.Behaviors>
<DataTemplate> <EventTriggerBehavior EventName="SelectionChanged">
<Button <InvokeCommandAction Command="{Binding SelectionChanged}" PassEventArgsToCommand="true"></InvokeCommandAction>
HorizontalAlignment="Center" </EventTriggerBehavior>
HorizontalContentAlignment="Center" </Interaction.Behaviors>
Background="#232323" <DataGrid.Columns>
Command="{Binding ShowServerDetails}" <DataGridTextColumn Header="Id" Binding="{Binding Id}"/>
CommandParameter="{Binding $self}" <DataGridTextColumn Header="Name" Binding="{Binding Name}" />
Content="View" <DataGridTextColumn Header="Status" Binding="{Binding Status}" CellStyleClasses="status" />
Foreground="White" /> <DataGridTextColumn Header="Type" Binding="{Binding Type}" />
</DataTemplate> <DataGridTextColumn IsReadOnly="False" Header="IpV4" Binding="{Binding Ipv4}" />
</DataGridTemplateColumn.CellTemplate> <DataGridTextColumn Header="IpV6" Binding="{Binding Ipv6}" />
</DataGridTemplateColumn> <DataGridTemplateColumn Width="*" Header="">
<DataGridTemplateColumn Width="*" Header=""> <DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellTemplate> <DataTemplate>
<DataTemplate> <Button
<Button HorizontalAlignment="Center"
HorizontalAlignment="Center" HorizontalContentAlignment="Center"
HorizontalContentAlignment="Center" Background="#232323"
Background="#232323" Command="{Binding ShowServerDetails}"
Click="openSSH" CommandParameter="{Binding $self}"
Content="Open SSH" Content="View"
Foreground="White" /> Foreground="White" />
</DataTemplate> </DataTemplate>
</DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn> </DataGridTemplateColumn>
<DataGridTemplateColumn Width="*" Header=""> <DataGridTemplateColumn Width="*" Header="">
<DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellTemplate>
<DataTemplate> <DataTemplate>
<Button <Button
HorizontalAlignment="Center" HorizontalAlignment="Center"
HorizontalContentAlignment="Center" HorizontalContentAlignment="Center"
Background="#232323" Background="#232323"
Click="openSCP" Click="openSSH"
Content="Open SCP" Content="Open SSH"
Foreground="White" /> Foreground="White" />
</DataTemplate> </DataTemplate>
</DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn> </DataGridTemplateColumn>
</DataGrid.Columns> <DataGridTemplateColumn Width="*" Header="">
</DataGrid> <DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button
HorizontalAlignment="Center"
HorizontalContentAlignment="Center"
Background="#232323"
Click="openSCP"
Content="Open SCP"
Foreground="White" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
<SplitView.Pane> <SplitView.Pane>
<TextBlock Text="{Binding SelectedServer.Name}" <TextBlock Text="{Binding SelectedServer.Name}"
FontSize="24" FontSize="24"

View File

@ -49,6 +49,7 @@
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AString_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fdad3f0ebff0dd1f8e1d244c3c44c649be8228d5e25fb37ef1de7f3c0e261c_003FString_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AString_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fdad3f0ebff0dd1f8e1d244c3c44c649be8228d5e25fb37ef1de7f3c0e261c_003FString_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AStyledElement_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fb06c2ce0981bf6e5989cda2e3e737e53c2d54ee6ae7e7ce318d378f52e5f66_003FStyledElement_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AStyledElement_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fb06c2ce0981bf6e5989cda2e3e737e53c2d54ee6ae7e7ce318d378f52e5f66_003FStyledElement_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASystemDialog_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F8f5178316d8f8271b8f333bd87cd9e9f7da6764081a31277e776c6cdcb3a25a8_003FSystemDialog_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASystemDialog_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F8f5178316d8f8271b8f333bd87cd9e9f7da6764081a31277e776c6cdcb3a25a8_003FSystemDialog_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F548232ed526ec850f8e542b7449b4145e5c2c61a6576c9328c36e56a27e4_003FThrowHelper_002Ecs_002Fz_003A2_002D1/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fc7102cd0ffb8973777e61b1942c3fffac7e14016a511d055c3adf73ff91748_003FThrowHelper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fc7102cd0ffb8973777e61b1942c3fffac7e14016a511d055c3adf73ff91748_003FThrowHelper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F4743f513c7ddc8411223a46f0ca426ed929391acebcff993721dff2f0c6b34_003FThrowHelper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F4743f513c7ddc8411223a46f0ca426ed929391acebcff993721dff2f0c6b34_003FThrowHelper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F8874fa6fbc50b05ba8332188d36141eb4bba81fb1f92189ba9d7a25f545_003FThrowHelper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F8874fa6fbc50b05ba8332188d36141eb4bba81fb1f92189ba9d7a25f545_003FThrowHelper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>

View File

@ -0,0 +1,18 @@
using System;
using System.Threading.Tasks;
using PSCHelpdesk.Shared.Service;
namespace PSCHelpdesk.Services;
public class AvaloniaDispatcherService: IUserInterfaceDispatchService
{
public void Invoke(Action action)
{
Avalonia.Threading.Dispatcher.UIThread.Post(action);
}
public Task InvokeAsync(Action action)
{
return Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(action).GetTask();
}
}

View File

@ -41,23 +41,10 @@ public class MenuService: ReactiveObject, IMenuService
var pluginTab = new Item() var pluginTab = new Item()
{ {
Header = "Plugins B", Header = "Plugins",
CommandParameter = Ioc.Default.GetService<PluginListViewModel>(), CommandParameter = Ioc.Default.GetService<PluginListViewModel>(),
}; };
var testTab = new Item()
{
Header = "Plugins T",
CommandParameter = new TestViewModel(),
};
/*var settingsTab = new MenuItem()
{
Name = "Settings",
Tag = Ioc.Default.GetService<SettingsViewModel>(),
};*/
this.MenuItems.Add(pluginTab); this.MenuItems.Add(pluginTab);
this.MenuItems.Add(testTab);
//this.MenuOptionItems.Add(settingsTab);
OnMenuChanged(EventArgs.Empty); OnMenuChanged(EventArgs.Empty);
} }

View File

@ -19,6 +19,7 @@ class Startup
var settingsService = new SettingsService(); var settingsService = new SettingsService();
var te = new ServiceCollection() var te = new ServiceCollection()
.AddSingleton<IUserInterfaceDispatchService, AvaloniaDispatcherService>()
.AddPrise() .AddPrise()
.AddFactory<IResultConverter>(()=> new AvaloniaPluginResultConverter()) .AddFactory<IResultConverter>(()=> new AvaloniaPluginResultConverter())
.AddSingleton<AppService>() .AddSingleton<AppService>()

View File

@ -50,6 +50,10 @@ public class MainWindowViewModel : ViewModelBase, IViewModelBase
this.MenuItems.Add(menuServiceMenuItem); this.MenuItems.Add(menuServiceMenuItem);
} }
if (TabItems.Count == 0)
{
TabItems.Add(new TabItemViewModel(MenuItems.Last().Header, MenuItems.Last().CommandParameter));
}
} }
foreach (var menuServiceMenuItem in MenuService.MenuOptionItems) foreach (var menuServiceMenuItem in MenuService.MenuOptionItems)
@ -63,9 +67,6 @@ public class MainWindowViewModel : ViewModelBase, IViewModelBase
}; };
this.InitializeClient(); this.InitializeClient();
TabItems.Add(new TabItemViewModel("test", new TestViewModel()));
} }
private void InitializeClient() private void InitializeClient()
@ -79,10 +80,10 @@ public class MainWindowViewModel : ViewModelBase, IViewModelBase
var obj = this.MenuService.MenuItems.FirstOrDefault(menuItem => menuItem.Header == parameter.ToString(), null); var obj = this.MenuService.MenuItems.FirstOrDefault(menuItem => menuItem.Header == parameter.ToString(), null);
if (obj != null) if (obj != null)
{ {
TabItems.Add(new TabItemViewModel(obj.Header, obj.CommandParameter)); if (TabItems.Count(x => x.Header == obj.Header) == 0)
//SetAndRaisePropertyChanged(ref _tabItems, TabItems); {
//this.SelectedItem = obj; TabItems.Add(new TabItemViewModel(obj.Header, obj.CommandParameter));
//this.ContentDisplay = obj.CommandParameter; }
} }
} }

View File

@ -1,11 +0,0 @@
using PSCHelpdesk.Shared.ViewModels;
namespace PSCHelpdesk.ViewModels;
public partial class TestViewModel: ViewModelBase, IViewModelBase
{
public TestViewModel()
{
}
}

View File

@ -37,7 +37,7 @@
</TabControl.ItemTemplate> </TabControl.ItemTemplate>
<TabControl.ContentTemplate> <TabControl.ContentTemplate>
<DataTemplate DataType="vm:TabItemViewModel"> <DataTemplate DataType="vm:TabItemViewModel">
<DockPanel LastChildFill="True"> <DockPanel>
<ContentControl Content="{Binding Content}" /> <ContentControl Content="{Binding Content}" />
</DockPanel> </DockPanel>
</DataTemplate> </DataTemplate>

View File

@ -1,8 +0,0 @@
<UserControl 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"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="PSCHelpdesk.Views.TestView">
Welcome to Avalonia!
</UserControl>

View File

@ -1,13 +0,0 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace PSCHelpdesk.Views;
public partial class TestView : UserControl
{
public TestView()
{
InitializeComponent();
}
}

View File

@ -0,0 +1,8 @@
namespace PSCHelpdesk.Shared.Service;
public interface IUserInterfaceDispatchService
{
void Invoke(Action action);
Task InvokeAsync(Action action);
}