mirror of
https://github.com/frosch95/K8sFileBrowser.git
synced 2026-04-11 12:58:22 +02:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7f7471d47b | |||
| 6d03c88261 |
@@ -75,8 +75,7 @@
|
|||||||
"Clean",
|
"Clean",
|
||||||
"Publish",
|
"Publish",
|
||||||
"PublishLinux",
|
"PublishLinux",
|
||||||
"PublishWin",
|
"PublishWin"
|
||||||
"Restore"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -89,8 +88,7 @@
|
|||||||
"Clean",
|
"Clean",
|
||||||
"Publish",
|
"Publish",
|
||||||
"PublishLinux",
|
"PublishLinux",
|
||||||
"PublishWin",
|
"PublishWin"
|
||||||
"Restore"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
Accent="#677696"
|
Accent="#677696"
|
||||||
RegionColor="#282c34"
|
RegionColor="#282c34"
|
||||||
ErrorText="Red"
|
ErrorText="Red"
|
||||||
AltHigh="#282c34"
|
AltHigh="#343a45"
|
||||||
AltMediumLow="#2c313c"
|
AltMediumLow="#2c313c"
|
||||||
ListLow="#21252b"
|
ListLow="#21252b"
|
||||||
ListMedium="#2c313c"
|
ListMedium="#2c313c"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<Configurations>Debug;Release</Configurations>
|
<Configurations>Debug;Release</Configurations>
|
||||||
<Platforms>AnyCPU</Platforms>
|
<Platforms>AnyCPU</Platforms>
|
||||||
<ApplicationIcon>Assets/app.ico</ApplicationIcon>
|
<ApplicationIcon>Assets/app.ico</ApplicationIcon>
|
||||||
<Version>0.0.9</Version>
|
<Version>0.1.1</Version>
|
||||||
<RuntimeIdentifiers>win-x64;linux-x64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win-x64;linux-x64</RuntimeIdentifiers>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||||
|
|||||||
@@ -4,16 +4,25 @@ using System.Linq;
|
|||||||
using System.Reactive;
|
using System.Reactive;
|
||||||
using System.Reactive.Concurrency;
|
using System.Reactive.Concurrency;
|
||||||
using System.Reactive.Linq;
|
using System.Reactive.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using K8sFileBrowser.Models;
|
using K8sFileBrowser.Models;
|
||||||
using K8sFileBrowser.Services;
|
using K8sFileBrowser.Services;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
using ReactiveUI.Fody.Helpers;
|
using ReactiveUI.Fody.Helpers;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
namespace K8sFileBrowser.ViewModels;
|
namespace K8sFileBrowser.ViewModels;
|
||||||
|
|
||||||
public class MainWindowViewModel : ViewModelBase
|
public class MainWindowViewModel : ViewModelBase
|
||||||
{
|
{
|
||||||
|
|
||||||
|
#region Properties
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public string? Version { get; set; } = null!;
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public IEnumerable<ClusterContext> ClusterContexts { get; set; } = null!;
|
public IEnumerable<ClusterContext> ClusterContexts { get; set; } = null!;
|
||||||
|
|
||||||
@@ -50,19 +59,24 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
[Reactive]
|
[Reactive]
|
||||||
public Message Message { get; set; } = null!;
|
public Message Message { get; set; } = null!;
|
||||||
|
|
||||||
|
#endregion Properties
|
||||||
|
|
||||||
|
#region Commands
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> DownloadCommand { get; private set; } = null!;
|
public ReactiveCommand<Unit, Unit> DownloadCommand { get; private set; } = null!;
|
||||||
public ReactiveCommand<Unit, Unit> DownloadLogCommand { get; private set; } = null!;
|
public ReactiveCommand<Unit, Unit> DownloadLogCommand { get; private set; } = null!;
|
||||||
public ReactiveCommand<Unit, Unit> ParentCommand { get; private set; } = null!;
|
public ReactiveCommand<Unit, Unit> ParentCommand { get; private set; } = null!;
|
||||||
public ReactiveCommand<Unit, Unit> OpenCommand { get; private set; } = null!;
|
public ReactiveCommand<Unit, Unit> OpenCommand { get; private set; } = null!;
|
||||||
|
|
||||||
private ReactiveCommand<Namespace, IEnumerable<Pod>> GetPodsForNamespace { get; set; } = null!;
|
private ReactiveCommand<Namespace, IEnumerable<Pod>> GetPodsForNamespace { get; set; } = null!;
|
||||||
|
|
||||||
|
#endregion Commands
|
||||||
|
|
||||||
public MainWindowViewModel()
|
public MainWindowViewModel()
|
||||||
{
|
{
|
||||||
//TODO: use dependency injection to get the kubernetes service
|
|
||||||
IKubernetesService kubernetesService = new KubernetesService();
|
IKubernetesService kubernetesService = new KubernetesService();
|
||||||
|
|
||||||
|
Version = Assembly.GetExecutingAssembly().GetName().Version?.ToString();
|
||||||
|
|
||||||
// commands
|
// commands
|
||||||
ConfigureOpenDirectoryCommand();
|
ConfigureOpenDirectoryCommand();
|
||||||
ConfigureDownloadFileCommand(kubernetesService);
|
ConfigureDownloadFileCommand(kubernetesService);
|
||||||
@@ -81,6 +95,8 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
InitiallyLoadContexts(kubernetesService);
|
InitiallyLoadContexts(kubernetesService);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Property Subscriptions
|
||||||
|
|
||||||
private void InitiallyLoadContexts(IKubernetesService kubernetesService)
|
private void InitiallyLoadContexts(IKubernetesService kubernetesService)
|
||||||
{
|
{
|
||||||
// load the cluster contexts when the view model is created
|
// load the cluster contexts when the view model is created
|
||||||
@@ -91,70 +107,12 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
.ObserveOn(RxApp.MainThreadScheduler)
|
.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
.Subscribe(x =>
|
.Subscribe(x =>
|
||||||
{
|
{
|
||||||
|
ResetNamespaces();
|
||||||
ClusterContexts = x;
|
ClusterContexts = x;
|
||||||
|
|
||||||
// select the current cluster context
|
// select the current cluster context
|
||||||
SelectedClusterContext = ClusterContexts
|
SelectedClusterContext = ClusterContexts
|
||||||
.FirstOrDefault(x => x.Name == kubernetesService.GetCurrentContext());
|
.FirstOrDefault(c => c.Name == kubernetesService.GetCurrentContext());
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RegisterResetPath()
|
|
||||||
{
|
|
||||||
// reset the path when the pod or namespace changes
|
|
||||||
this.WhenAnyValue(c => c.SelectedContainer)
|
|
||||||
.Throttle(new TimeSpan(10))
|
|
||||||
.ObserveOn(RxApp.TaskpoolScheduler)
|
|
||||||
.Subscribe(_ => SelectedPath = "/");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RegisterReadContainers()
|
|
||||||
{
|
|
||||||
// read the file information when the path changes
|
|
||||||
this
|
|
||||||
.WhenAnyValue(c => c.SelectedPod)
|
|
||||||
.Throttle(new TimeSpan(10))
|
|
||||||
.Select(x => x == null
|
|
||||||
? new List<Container>()
|
|
||||||
: x.Containers.Select(c => new Container {Name = c}))
|
|
||||||
.ObserveOn(RxApp.MainThreadScheduler)
|
|
||||||
.Subscribe( x =>
|
|
||||||
{
|
|
||||||
Containers = x;
|
|
||||||
FileInformation = new List<FileInformation>();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.WhenAnyValue(x => x.Containers)
|
|
||||||
.Throttle(new TimeSpan(10))
|
|
||||||
.ObserveOn(RxApp.MainThreadScheduler)
|
|
||||||
.Subscribe(x => SelectedContainer = x?.FirstOrDefault());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RegisterReadFiles(IKubernetesService kubernetesService)
|
|
||||||
{
|
|
||||||
// read the file information when the path changes
|
|
||||||
this
|
|
||||||
.WhenAnyValue(c => c.SelectedContainer, c => c.SelectedPath)
|
|
||||||
.Throttle(new TimeSpan(10))
|
|
||||||
.Select(x => x.Item1 == null || x.Item2 == null
|
|
||||||
? new List<FileInformation>()
|
|
||||||
: GetFileInformation(kubernetesService, x.Item2, SelectedPod!, SelectedNamespace!, x.Item1))
|
|
||||||
.ObserveOn(RxApp.MainThreadScheduler)
|
|
||||||
.Subscribe(x => FileInformation = x);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RegisterReadPods()
|
|
||||||
{
|
|
||||||
// read the pods when the namespace changes
|
|
||||||
this
|
|
||||||
.WhenAnyValue(c => c.SelectedNamespace)
|
|
||||||
.Throttle(new TimeSpan(10))
|
|
||||||
.SelectMany(ns => GetPodsForNamespace.Execute(ns!))
|
|
||||||
.ObserveOn(RxApp.MainThreadScheduler)
|
|
||||||
.Subscribe(x =>
|
|
||||||
{
|
|
||||||
Pods = x;
|
|
||||||
Containers = null;
|
|
||||||
FileInformation = new List<FileInformation>();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,20 +126,80 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
.ObserveOn(RxApp.MainThreadScheduler)
|
.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
.Subscribe(ns =>
|
.Subscribe(ns =>
|
||||||
{
|
{
|
||||||
|
ResetPods();
|
||||||
Namespaces = ns;
|
Namespaces = ns;
|
||||||
Pods = new List<Pod>();
|
|
||||||
Containers = null;
|
|
||||||
FileInformation = new List<FileInformation>();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void RegisterReadPods()
|
||||||
|
{
|
||||||
|
// read the pods when the namespace changes
|
||||||
|
this
|
||||||
|
.WhenAnyValue(c => c.SelectedNamespace)
|
||||||
|
.Throttle(new TimeSpan(10))
|
||||||
|
.Where(x => x != null)
|
||||||
|
.SelectMany(ns => GetPodsForNamespace.Execute(ns!))
|
||||||
|
.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
|
.Subscribe(x =>
|
||||||
|
{
|
||||||
|
ResetContainers();
|
||||||
|
Pods = x;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegisterReadContainers()
|
||||||
|
{
|
||||||
|
// read the file information when the path changes
|
||||||
|
this
|
||||||
|
.WhenAnyValue(c => c.SelectedPod)
|
||||||
|
.Throttle(new TimeSpan(10))
|
||||||
|
.Where(x => x != null)
|
||||||
|
.Select(x => x!.Containers.Select(c => new Container {Name = c}))
|
||||||
|
.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
|
.Subscribe( x =>
|
||||||
|
{
|
||||||
|
ResetPath();
|
||||||
|
Containers = x;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.WhenAnyValue(x => x.Containers)
|
||||||
|
.Throttle(new TimeSpan(10))
|
||||||
|
.Where(x => !x.IsNullOrEmpty())
|
||||||
|
.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
|
.Subscribe(x => SelectedContainer = x?.FirstOrDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegisterResetPath()
|
||||||
|
{
|
||||||
|
// reset the path when the pod or namespace changes
|
||||||
|
this.WhenAnyValue(c => c.SelectedContainer)
|
||||||
|
.Throttle(new TimeSpan(10))
|
||||||
|
.Where(x => x != null)
|
||||||
|
.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
|
.Subscribe(_ => SelectedPath = "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegisterReadFiles(IKubernetesService kubernetesService)
|
||||||
|
{
|
||||||
|
// read the file information when the path changes
|
||||||
|
this
|
||||||
|
.WhenAnyValue(c => c.SelectedContainer, c => c.SelectedPath)
|
||||||
|
.Throttle(new TimeSpan(10))
|
||||||
|
.Where(x => x is { Item1: not null, Item2: not null })
|
||||||
|
.Select(x => GetFileInformation(kubernetesService, x.Item2!, SelectedPod!, SelectedNamespace!, x.Item1!))
|
||||||
|
.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
|
.Subscribe(x => FileInformation = x);
|
||||||
|
}
|
||||||
|
#endregion Property Subscriptions
|
||||||
|
|
||||||
|
#region Configure Commands
|
||||||
private void ConfigureGetPodsForNamespaceCommand(IKubernetesService kubernetesService)
|
private void ConfigureGetPodsForNamespaceCommand(IKubernetesService kubernetesService)
|
||||||
{
|
{
|
||||||
GetPodsForNamespace = ReactiveCommand.CreateFromObservable<Namespace, IEnumerable<Pod>>(ns =>
|
GetPodsForNamespace = ReactiveCommand.CreateFromObservable<Namespace, IEnumerable<Pod>>(ns =>
|
||||||
Observable.StartAsync(_ => PodsAsync(ns, kubernetesService), RxApp.TaskpoolScheduler));
|
Observable.StartAsync(_ => PodsAsync(ns, kubernetesService), RxApp.TaskpoolScheduler));
|
||||||
|
|
||||||
GetPodsForNamespace.ThrownExceptions.ObserveOn(RxApp.MainThreadScheduler)
|
GetPodsForNamespace.ThrownExceptions.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
.Subscribe(ex => ShowErrorMessage(ex.Message).ConfigureAwait(false).GetAwaiter().GetResult());
|
.Subscribe(ShowErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ConfigureParentDirectoryCommand()
|
private void ConfigureParentDirectoryCommand()
|
||||||
@@ -200,7 +218,7 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
}, isNotRoot, RxApp.MainThreadScheduler);
|
}, isNotRoot, RxApp.MainThreadScheduler);
|
||||||
|
|
||||||
ParentCommand.ThrownExceptions.ObserveOn(RxApp.MainThreadScheduler)
|
ParentCommand.ThrownExceptions.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
.Subscribe(ex => ShowErrorMessage(ex.Message).ConfigureAwait(false).GetAwaiter().GetResult());
|
.Subscribe(ShowErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ConfigureDownloadLogCommand(IKubernetesService kubernetesService)
|
private void ConfigureDownloadLogCommand(IKubernetesService kubernetesService)
|
||||||
@@ -225,7 +243,7 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
}, isSelectedPod, RxApp.MainThreadScheduler);
|
}, isSelectedPod, RxApp.MainThreadScheduler);
|
||||||
|
|
||||||
DownloadLogCommand.ThrownExceptions.ObserveOn(RxApp.MainThreadScheduler)
|
DownloadLogCommand.ThrownExceptions.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
.Subscribe(ex => ShowErrorMessage(ex.Message).ConfigureAwait(false).GetAwaiter().GetResult());
|
.Subscribe(ShowErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ConfigureDownloadFileCommand(IKubernetesService kubernetesService)
|
private void ConfigureDownloadFileCommand(IKubernetesService kubernetesService)
|
||||||
@@ -251,7 +269,7 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
}, isFile, RxApp.MainThreadScheduler);
|
}, isFile, RxApp.MainThreadScheduler);
|
||||||
|
|
||||||
DownloadCommand.ThrownExceptions.ObserveOn(RxApp.MainThreadScheduler)
|
DownloadCommand.ThrownExceptions.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
.Subscribe(ex => ShowErrorMessage(ex.Message).ConfigureAwait(false).GetAwaiter().GetResult());
|
.Subscribe(ShowErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ConfigureOpenDirectoryCommand()
|
private void ConfigureOpenDirectoryCommand()
|
||||||
@@ -270,9 +288,13 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
isDirectory, RxApp.MainThreadScheduler);
|
isDirectory, RxApp.MainThreadScheduler);
|
||||||
|
|
||||||
OpenCommand.ThrownExceptions.ObserveOn(RxApp.MainThreadScheduler)
|
OpenCommand.ThrownExceptions.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
.Subscribe(ex => ShowErrorMessage(ex.Message).ConfigureAwait(false).GetAwaiter().GetResult());
|
.Subscribe(ShowErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion Configure Commands
|
||||||
|
|
||||||
|
#region Get Data
|
||||||
|
|
||||||
private static async Task<IEnumerable<Pod>> PodsAsync(Namespace? ns, IKubernetesService kubernetesService)
|
private static async Task<IEnumerable<Pod>> PodsAsync(Namespace? ns, IKubernetesService kubernetesService)
|
||||||
{
|
{
|
||||||
if (ns == null)
|
if (ns == null)
|
||||||
@@ -296,10 +318,8 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
RxApp.MainThreadScheduler.Schedule(Action);
|
ShowErrorMessage(e);
|
||||||
return new List<Namespace>();
|
return new List<Namespace>();
|
||||||
|
|
||||||
async void Action() => await ShowErrorMessage(e.Message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,35 +347,89 @@ public class MainWindowViewModel : ViewModelBase
|
|||||||
}).ToList();
|
}).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ShowWorkingMessage(string message)
|
#endregion Get Data
|
||||||
|
|
||||||
|
#region Reset Data
|
||||||
|
|
||||||
|
private void ResetPath()
|
||||||
{
|
{
|
||||||
Message = new Message
|
FileInformation = new List<FileInformation>();
|
||||||
{
|
SelectedPath = null;
|
||||||
IsVisible = true,
|
SelectedContainer = null;
|
||||||
Text = message,
|
|
||||||
IsError = false
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ShowErrorMessage(string message)
|
private void ResetContainers()
|
||||||
{
|
{
|
||||||
Message = new Message
|
ResetPath();
|
||||||
|
Containers = new List<Container>();
|
||||||
|
SelectedPod = null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetPods()
|
||||||
|
{
|
||||||
|
ResetContainers();
|
||||||
|
SelectedNamespace = null;
|
||||||
|
Pods = new List<Pod>();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetNamespaces()
|
||||||
|
{
|
||||||
|
ResetPods();
|
||||||
|
Namespaces = new List<Namespace>();
|
||||||
|
SelectedClusterContext = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Reset Data
|
||||||
|
|
||||||
|
#region show messages
|
||||||
|
|
||||||
|
private void ShowWorkingMessage(string message)
|
||||||
|
{
|
||||||
|
RxApp.MainThreadScheduler.Schedule(Action);
|
||||||
|
return;
|
||||||
|
|
||||||
|
void Action()
|
||||||
{
|
{
|
||||||
IsVisible = true,
|
Message = new Message
|
||||||
Text = message,
|
{
|
||||||
IsError = true
|
IsVisible = true,
|
||||||
};
|
Text = message,
|
||||||
await Task.Delay(7000);
|
IsError = false
|
||||||
HideWorkingMessage();
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowErrorMessage(string message)
|
||||||
|
{
|
||||||
|
RxApp.MainThreadScheduler.Schedule(Action);
|
||||||
|
return;
|
||||||
|
|
||||||
|
async void Action()
|
||||||
|
{
|
||||||
|
Message = new Message { IsVisible = true, Text = message, IsError = true };
|
||||||
|
await Task.Delay(7000);
|
||||||
|
HideWorkingMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowErrorMessage(Exception exception)
|
||||||
|
{
|
||||||
|
// ReSharper disable once TemplateIsNotCompileTimeConstantProblem
|
||||||
|
Log.Error(exception, exception.Message);
|
||||||
|
ShowErrorMessage(exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HideWorkingMessage()
|
private void HideWorkingMessage()
|
||||||
{
|
{
|
||||||
Message = new Message
|
RxApp.MainThreadScheduler.Schedule(() => Message = new Message
|
||||||
{
|
{
|
||||||
IsVisible = false,
|
IsVisible = false,
|
||||||
Text = "",
|
Text = "",
|
||||||
IsError = false
|
IsError = false
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion show messages
|
||||||
}
|
}
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
</Border>
|
</Border>
|
||||||
<Grid RowDefinitions="Auto, *">
|
<Grid RowDefinitions="Auto, *">
|
||||||
<Border Padding="10 14" Background="#21252b">
|
<Border Padding="10 14" Background="#21252b">
|
||||||
<Grid ColumnDefinitions="Auto,Auto,Auto,*">
|
<Grid ColumnDefinitions="Auto,Auto,Auto,*,Auto">
|
||||||
<Label Grid.Column="0"
|
<Label Grid.Column="0"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Margin="0 0 10 0">
|
Margin="0 0 10 0">
|
||||||
@@ -49,6 +49,7 @@
|
|||||||
MinWidth="200"
|
MinWidth="200"
|
||||||
Margin="0 0 10 0">
|
Margin="0 0 10 0">
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
|
<TextBlock Grid.Column="4" HorizontalAlignment="Right" VerticalAlignment="Center" Text="{Binding Version}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user