Server configuration list in DataGrid.

Sorting already works out of the box.
This commit is contained in:
eelke 2025-10-26 17:13:31 +01:00
parent 26259543b3
commit 4b8a346cfb
9 changed files with 87 additions and 75 deletions

View file

@ -23,7 +23,7 @@ public sealed class ConnectionStringService : IConnectionStringService
public static ConnectionStringService CreateDefault() public static ConnectionStringService CreateDefault()
=> new(new IConnectionStringCodec[] { new LibpqCodec(), new NpgsqlCodec(), new UrlCodec(), new JdbcCodec() }); => new(new IConnectionStringCodec[] { new LibpqCodec(), new NpgsqlCodec(), new UrlCodec(), new JdbcCodec() });
public Result<ConnStringFormat> DetectFormat(string input) public Result<ConnStringFormat> DetectFormat(string? input)
{ {
if (string.IsNullOrWhiteSpace(input)) if (string.IsNullOrWhiteSpace(input))
return Result.Fail<ConnStringFormat>("Empty input"); return Result.Fail<ConnStringFormat>("Empty input");

View file

@ -8,7 +8,7 @@ namespace pgLabII.PgUtils.ConnectionStrings;
/// </summary> /// </summary>
public interface IConnectionStringService public interface IConnectionStringService
{ {
Result<ConnStringFormat> DetectFormat(string input); Result<ConnStringFormat> DetectFormat(string? input);
Result<ConnectionDescriptor> ParseToDescriptor(string input); Result<ConnectionDescriptor> ParseToDescriptor(string input);

View file

@ -39,7 +39,7 @@ namespace pgLabII.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("Documents"); b.ToTable("Documents", (string)null);
}); });
modelBuilder.Entity("pgLabII.Model.EditHistoryEntry", b => modelBuilder.Entity("pgLabII.Model.EditHistoryEntry", b =>
@ -69,7 +69,7 @@ namespace pgLabII.Migrations
b.HasIndex("DocumentId", "Timestamp"); b.HasIndex("DocumentId", "Timestamp");
b.ToTable("EditHistory"); b.ToTable("EditHistory", (string)null);
}); });
modelBuilder.Entity("pgLabII.Model.ServerConfigurationEntity", b => modelBuilder.Entity("pgLabII.Model.ServerConfigurationEntity", b =>
@ -112,7 +112,7 @@ namespace pgLabII.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("ServerConfigurations"); b.ToTable("ServerConfigurations", (string)null);
}); });
modelBuilder.Entity("pgLabII.Model.EditHistoryEntry", b => modelBuilder.Entity("pgLabII.Model.EditHistoryEntry", b =>

View file

@ -14,6 +14,5 @@ public class ServerConfigurationEntity
public bool ColorEnabled { get; set; } = true; public bool ColorEnabled { get; set; } = true;
public int ColorArgb { get; set; } = unchecked((int)0xFF_33_33_33); // default dark gray public int ColorArgb { get; set; } = unchecked((int)0xFF_33_33_33); // default dark gray
public string UserName { get; set; } = ""; public string UserName { get; set; } = "";
public string Password { get; set; } = ""; public string Password { get; set; } = "";
} }

View file

@ -28,14 +28,14 @@ public partial class EditServerConfigurationViewModel : ViewModelBase
public ServerConfigurationViewModel Configuration { get; set; } public ServerConfigurationViewModel Configuration { get; set; }
// Connection string IO // Connection string IO
[Reactive] public partial string InputConnectionString { get; set; } [Reactive] public partial string? InputConnectionString { get; set; }
[Reactive] public partial ForcedFormatOption ForcedFormat { get; set; } [Reactive] public partial ForcedFormatOption ForcedFormat { get; set; }
[ObservableAsProperty] private ConnStringFormat? _detectedFormat; [ObservableAsProperty] private ConnStringFormat? _detectedFormat;
[Reactive] public partial ConnStringFormat OutputFormat { get; set; } [Reactive] public partial ConnStringFormat OutputFormat { get; set; }
[Reactive] public partial string OutputConnectionString { get; set; } [Reactive] public partial string? OutputConnectionString { get; set; }
public ReactiveCommand<Unit, Unit> ParseConnectionStringCommand { get; } public ReactiveCommand<Unit, Unit> ParseConnectionStringCommand { get; }
public ReactiveCommand<Unit, Unit> GenerateConnectionStringCommand { get; } public ReactiveCommand<Unit, Unit> GenerateConnectionStringCommand { get; }
@ -69,7 +69,7 @@ public partial class EditServerConfigurationViewModel : ViewModelBase
InitializeFromCopy(); InitializeFromCopy();
} }
private ConnStringFormat? DetectFormat(ForcedFormatOption format, string input) private ConnStringFormat? DetectFormat(ForcedFormatOption format, string? input)
{ {
if (format != ForcedFormatOption.Auto) if (format != ForcedFormatOption.Auto)
return null; return null;

View file

@ -3,11 +3,12 @@ using Avalonia.Media;
using Npgsql; using Npgsql;
using pgLabII.Model; using pgLabII.Model;
using ReactiveUI; using ReactiveUI;
using ReactiveUI.SourceGenerators;
namespace pgLabII.ViewModels; namespace pgLabII.ViewModels;
// UI ViewModel that wraps the persistence entity // UI ViewModel that wraps the persistence entity
public class ServerConfigurationViewModel : ReactiveObject public partial class ServerConfigurationViewModel : ReactiveObject
{ {
private readonly ServerConfigurationEntity _entity; private readonly ServerConfigurationEntity _entity;
@ -16,7 +17,7 @@ public class ServerConfigurationViewModel : ReactiveObject
_entity = entity ?? throw new ArgumentNullException(nameof(entity)); _entity = entity ?? throw new ArgumentNullException(nameof(entity));
EditCommand = ReactiveCommand.Create(() => EditCommand = ReactiveCommand.Create(() =>
{ {
var window = new Views.EditServerConfigurationWindow(new(this)) { New = false }; var window = new Views.EditServerConfigurationWindow(new(this));
window.Show(); window.Show();
}); });
ExploreCommand = ReactiveCommand.Create(() => ExploreCommand = ReactiveCommand.Create(() =>
@ -24,6 +25,10 @@ public class ServerConfigurationViewModel : ReactiveObject
var window = new Views.SingleDatabaseWindow(entity); var window = new Views.SingleDatabaseWindow(entity);
window.Show(); window.Show();
}); });
_backgroundBrushHelper = this.WhenAnyValue(x => x.ColorEnabled, x => x.Color,
(enabled, color) => enabled ? new SolidColorBrush(color) : null)
.ToProperty(this, x => x.BackgroundBrush);
} }
public ServerConfigurationEntity Entity => _entity; public ServerConfigurationEntity Entity => _entity;
@ -67,7 +72,7 @@ public class ServerConfigurationViewModel : ReactiveObject
public bool ColorEnabled public bool ColorEnabled
{ {
get => _entity.ColorEnabled; get => _entity.ColorEnabled;
set { if (_entity.ColorEnabled != value) { _entity.ColorEnabled = value; this.RaisePropertyChanged(); this.RaisePropertyChanged(nameof(BackgroundBrush)); } } set { if (_entity.ColorEnabled != value) { _entity.ColorEnabled = value; this.RaisePropertyChanged(); } }
} }
public Color Color public Color Color
@ -80,12 +85,12 @@ public class ServerConfigurationViewModel : ReactiveObject
{ {
_entity.ColorArgb = argb; _entity.ColorArgb = argb;
this.RaisePropertyChanged(); this.RaisePropertyChanged();
this.RaisePropertyChanged(nameof(BackgroundBrush));
} }
} }
} }
public IBrush? BackgroundBrush => ColorEnabled ? new SolidColorBrush(Color) : null; //public IBrush? BackgroundBrush => ColorEnabled ? new SolidColorBrush(Color) : null;
[ObservableAsProperty] private IBrush? _backgroundBrush;
public string UserName public string UserName
{ {
@ -116,6 +121,7 @@ public class ServerConfigurationViewModel : ReactiveObject
} }
public ReactiveCommand<Unit, Unit> EditCommand { get; } public ReactiveCommand<Unit, Unit> EditCommand { get; }
public ReactiveCommand<Unit, Unit> ExploreCommand { get; } public ReactiveCommand<Unit, Unit> ExploreCommand { get; }
} }

View file

@ -3,11 +3,14 @@ using ReactiveUI;
using System.Reactive; using System.Reactive;
using Avalonia.Media; using Avalonia.Media;
using pgLabII.Views; using pgLabII.Views;
using ReactiveUI.SourceGenerators;
namespace pgLabII.ViewModels; namespace pgLabII.ViewModels;
public class ServerListViewModel : ViewModelBase public partial class ServerListViewModel : ViewModelBase
{ {
[Reactive] public partial ServerConfigurationViewModel? SelectedServerConfiguration { get; set; }
public ObservableCollection<ServerConfigurationViewModel> ServerConfigurations { get; } = public ObservableCollection<ServerConfigurationViewModel> ServerConfigurations { get; } =
[ [
new (new() new (new()
@ -18,7 +21,7 @@ public class ServerListViewModel : ViewModelBase
Port = 5418, Port = 5418,
InitialDatabase = "postgres", InitialDatabase = "postgres",
UserName = "postgres", UserName = "postgres",
Password = "admin", Password = "admin"
}) })
{ {
Color = Colors.Aquamarine, Color = Colors.Aquamarine,
@ -31,22 +34,48 @@ public class ServerListViewModel : ViewModelBase
}), }),
]; ];
public ReactiveCommand<ServerConfigurationViewModel, Unit> RemoveServerCommand { get; } public ReactiveCommand<ServerConfigurationViewModel?, Unit> ExploreServerCommand { get; }
public ReactiveCommand<ServerConfigurationViewModel?, Unit> EditServerCommand { get; }
public ReactiveCommand<ServerConfigurationViewModel?, Unit> RemoveServerCommand { get; }
public ReactiveCommand<Unit, Unit> AddServerCommand { get; } public ReactiveCommand<Unit, Unit> AddServerCommand { get; }
public ServerListViewModel() public ServerListViewModel()
{ {
RemoveServerCommand = ReactiveCommand.Create<ServerConfigurationViewModel, Unit>((sc) => ExploreServerCommand = ReactiveCommand.Create<ServerConfigurationViewModel?, Unit>((sc) =>
{ {
ServerConfigurations.Remove(sc); var conf = sc ?? SelectedServerConfiguration;
if (conf is null)
return Unit.Default;
var window = new Views.SingleDatabaseWindow(conf.Entity);
window.Show();
return Unit.Default;
});
EditServerCommand = ReactiveCommand.Create<ServerConfigurationViewModel?, Unit>((sc) =>
{
var conf = sc ?? SelectedServerConfiguration;
if (conf is null)
return Unit.Default;
EditServerConfigurationWindow window = new(new(conf));
window.Show();
return Unit.Default;
});
RemoveServerCommand = ReactiveCommand.Create<ServerConfigurationViewModel?, Unit>((sc) =>
{
var conf = sc ?? SelectedServerConfiguration;
if (conf is null)
return Unit.Default;
ServerConfigurations.Remove(conf);
return Unit.Default; return Unit.Default;
}); });
AddServerCommand = ReactiveCommand.Create(() => AddServerCommand = ReactiveCommand.Create(() =>
{ {
EditServerConfigurationViewModel vm = new(); EditServerConfigurationWindow window = new(new());
EditServerConfigurationWindow window = new() { DataContext = vm, New = true };
window.Show(); window.Show();
}); });
} }

View file

@ -32,6 +32,5 @@ public partial class EditServerConfigurationWindow : ReactiveWindow<EditServerCo
}); });
} }
public bool New { get; set; }
} }

View file

@ -15,56 +15,35 @@
</Design.DataContext> </Design.DataContext>
<StackPanel x:Name="ServerList" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <StackPanel x:Name="ServerList" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid ColumnDefinitions="Auto,*,Auto"> <StackPanel Orientation="Horizontal"
<Button Grid.Column="2" Content="+" Command="{Binding AddServerCommand}"/> HorizontalAlignment="Stretch"
</Grid> VerticalAlignment="Top">
<ListBox x:Name="Servers" ItemsSource="{Binding ServerConfigurations}"> <Button Content="New"
<ListBox.ItemTemplate> Command="{Binding AddServerCommand}"/>
<DataTemplate> <Button Content="Edit"
<Border BorderBrush="{Binding BackgroundBrush}" BorderThickness="2"> Command="{Binding EditServerCommand}"/>
<Grid ColumnDefinitions="*,*"> <Button Content="Open"
<StackPanel Command="{Binding ExploreServerCommand}"/>
Orientation="Vertical" </StackPanel>
HorizontalAlignment="Stretch"
Grid.Row="0"
Grid.Column="0">
<TextBlock Text="{Binding Name}" FontSize="18" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Host}" />
<TextBlock Text=":" />
<TextBlock Text="{Binding Port}" />
<TextBlock Text="/" />
<TextBlock Text="{Binding InitialDatabase}" />
</StackPanel>
</StackPanel> <DataGrid ItemsSource="{Binding ServerConfigurations}"
SelectedItem="{Binding SelectedServerConfiguration, Mode=TwoWay}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
AutoGenerateColumns="False"
IsReadOnly="True"
GridLinesVisibility="All"
BorderThickness="1">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
<DataGridTextColumn Header="Host" Binding="{Binding Host}" />
<DataGridTextColumn Header="Port" Binding="{Binding Port}" />
<DataGridTextColumn Header="Db" Binding="{Binding InitialDatabase}" />
<DataGridTextColumn Header="User" Binding="{Binding UserName}" />
<DataGridTextColumn Header="SSL" Binding="{Binding DefaultSslMode}" />
</DataGrid.Columns>
<StackPanel
Orientation="Horizontal" </DataGrid>
HorizontalAlignment="Right"
Grid.Row="0"
Grid.Column="1">
<Button Command="{Binding ExploreCommand}">
DB
</Button>
<Button>Server</Button>
<Button Content="...">
<Button.Flyout>
<MenuFlyout>
<MenuItem Header="Edit" Command="{Binding EditCommand}" />
<Separator />
<MenuItem Header="Remove"
Command="{Binding #ServerList.((vm:ServerListViewModel)DataContext).RemoveServerCommand}"
CommandParameter="{Binding .}"
Foreground="Crimson" />
</MenuFlyout>
</Button.Flyout>
</Button>
</StackPanel>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel> </StackPanel>
</UserControl> </UserControl>