├── Icon
├── tftpclient.ico
├── tftpclient_256x256.png
├── convertico.bat
└── tftpclient.svg
├── Docs
└── Screenshot-win11.png
├── AvaloniaTFTPClient
├── TFTPClient
│ ├── publishlinux.bat
│ ├── Assets
│ │ └── tftpclient.ico
│ ├── DialogCloser
│ │ ├── IDialogCloser.cs
│ │ └── DialogCloserConverter.cs
│ ├── ViewModels
│ │ ├── ViewModelBase.cs
│ │ ├── ErrorWindowViewModel.cs
│ │ ├── SettingsWindowViewModel.cs
│ │ └── MainWindowViewModel.cs
│ ├── Properties
│ │ ├── launchSettings.json
│ │ └── PublishProfiles
│ │ │ ├── PublishLinuxX64Net8.pubxml
│ │ │ ├── PublishWindowsX64Net8.pubxml
│ │ │ ├── PublishLinuxX64Selfcontained.pubxml
│ │ │ └── PublishWindowsX64Selfcontained.pubxml
│ ├── Views
│ │ ├── ErrorWindow.axaml.cs
│ │ ├── SettingsWindow.axaml.cs
│ │ ├── ErrorWindow.axaml
│ │ ├── MainWindow.axaml.cs
│ │ ├── MainWindow.axaml
│ │ └── SettingsWindow.axaml
│ ├── zippublish.ps1
│ ├── Program.cs
│ ├── App.axaml
│ ├── PersistedData.cs
│ ├── TFTPClient.csproj
│ ├── App.axaml.cs
│ └── SuspensionDriver.cs
├── AvaloniaTFTPClient.sln
└── .editorconfig
├── README.md
├── LICENSE
└── .gitignore
/Icon/tftpclient.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpmikkers/Avalonia-TFTPClient/HEAD/Icon/tftpclient.ico
--------------------------------------------------------------------------------
/Docs/Screenshot-win11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpmikkers/Avalonia-TFTPClient/HEAD/Docs/Screenshot-win11.png
--------------------------------------------------------------------------------
/Icon/tftpclient_256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpmikkers/Avalonia-TFTPClient/HEAD/Icon/tftpclient_256x256.png
--------------------------------------------------------------------------------
/AvaloniaTFTPClient/TFTPClient/publishlinux.bat:
--------------------------------------------------------------------------------
1 | dotnet clean
2 | dotnet build
3 | dotnet publish --sc --runtime:linux-x64
4 |
--------------------------------------------------------------------------------
/AvaloniaTFTPClient/TFTPClient/Assets/tftpclient.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jpmikkers/Avalonia-TFTPClient/HEAD/AvaloniaTFTPClient/TFTPClient/Assets/tftpclient.ico
--------------------------------------------------------------------------------
/AvaloniaTFTPClient/TFTPClient/DialogCloser/IDialogCloser.cs:
--------------------------------------------------------------------------------
1 | namespace UIClient.DialogCloser;
2 |
3 | public interface IDialogCloser
4 | {
5 | void Close();
6 | void Close(object? result);
7 | }
8 |
--------------------------------------------------------------------------------
/AvaloniaTFTPClient/TFTPClient/ViewModels/ViewModelBase.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.ComponentModel;
2 | namespace UIClient.ViewModels;
3 |
4 | public class ViewModelBase : ObservableObject
5 | {
6 | }
7 |
--------------------------------------------------------------------------------
/Icon/convertico.bat:
--------------------------------------------------------------------------------
1 | .\imagemagick\magick.exe -density 384 -background none -define icon:auto-resize .\tftpclient.svg .\tftpclient.ico
2 | .\imagemagick\magick.exe -size 256x256 -background none .\tftpclient.svg .\tftpclient_256x256.png
3 |
--------------------------------------------------------------------------------
/AvaloniaTFTPClient/TFTPClient/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "TFTPClient": {
4 | "commandName": "Project"
5 | },
6 | "WSL": {
7 | "commandName": "WSL2",
8 | "distributionName": ""
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/AvaloniaTFTPClient/TFTPClient/Views/ErrorWindow.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls;
3 | using Avalonia.Markup.Xaml;
4 |
5 | namespace UIClient.Views;
6 |
7 | public partial class ErrorWindow : Window
8 | {
9 | public ErrorWindow()
10 | {
11 | InitializeComponent();
12 | }
13 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Avalonia-TFTPClient
2 | Cross platform Trivial File Transfer Protocol (TFTP) client desktop application, uses [Avalonia UI](https://github.com/AvaloniaUI) as the UI framework.
3 |
4 | It relies on my TFTP protocol client library Baksteen.Net.TFTP.Client ; see the [Nuget package](https://www.nuget.org/packages/Baksteen.Net.TFTP.Client) or [repository](http://github.com/jpmikkers/TFTPClient) for more information.
5 |
6 | # Screenshots
7 |
8 | ## TFTP Client running on win11
9 |
10 |
11 |
--------------------------------------------------------------------------------
/AvaloniaTFTPClient/TFTPClient/Views/SettingsWindow.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Markup.Xaml;
3 | using UIClient.ViewModels;
4 | using System;
5 | using Avalonia.Controls;
6 | namespace UIClient.Views;
7 |
8 | public partial class SettingsWindow : Window
9 | {
10 | public SettingsWindow()
11 | {
12 | InitializeComponent();
13 | #if DEBUG
14 | this.AttachDevTools();
15 | #endif
16 | }
17 |
18 | private void InitializeComponent()
19 | {
20 | AvaloniaXamlLoader.Load(this);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/AvaloniaTFTPClient/TFTPClient/zippublish.ps1:
--------------------------------------------------------------------------------
1 | dotnet publish /p:Configuration=Release /p:PublishProfile=PublishLinuxX64Net8
2 | dotnet publish /p:Configuration=Release /p:PublishProfile=PublishLinuxX64Selfcontained
3 | dotnet publish /p:Configuration=Release /p:PublishProfile=PublishWindowsX64Net8
4 | dotnet publish /p:Configuration=Release /p:PublishProfile=PublishWindowsX64Selfcontained
5 | Push-Location "publish"
6 | Get-ChildItem -Directory | ForEach-Object {
7 | Compress-Archive -Path "$($_.FullName)\*" -DestinationPath "AvaloniaTFTPClient_$($_.Name).zip" -CompressionLevel Optimal -Force
8 | }
9 | Pop-Location
--------------------------------------------------------------------------------
/AvaloniaTFTPClient/TFTPClient/Properties/PublishProfiles/PublishLinuxX64Net8.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | Release
8 | Any CPU
9 | publish\linux_x64_net8
10 | FileSystem
11 | <_TargetId>Folder
12 | net8.0
13 | linux-x64
14 | false
15 | false
16 |
17 |
--------------------------------------------------------------------------------
/AvaloniaTFTPClient/TFTPClient/Properties/PublishProfiles/PublishWindowsX64Net8.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | Release
8 | Any CPU
9 | publish\windows_x64_net8
10 | FileSystem
11 | <_TargetId>Folder
12 | net8.0
13 | false
14 | win-x64
15 | false
16 | false
17 |
18 |
--------------------------------------------------------------------------------
/AvaloniaTFTPClient/TFTPClient/Properties/PublishProfiles/PublishLinuxX64Selfcontained.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | Release
8 | Any CPU
9 | publish\linux_x64_selfcontained
10 | FileSystem
11 | <_TargetId>Folder
12 | net8.0
13 | linux-x64
14 | true
15 | false
16 | false
17 |
18 |
--------------------------------------------------------------------------------
/AvaloniaTFTPClient/TFTPClient/Properties/PublishProfiles/PublishWindowsX64Selfcontained.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | Release
8 | Any CPU
9 | publish\windows_x64_selfcontained
10 | FileSystem
11 | <_TargetId>Folder
12 | net8.0
13 | win-x64
14 | true
15 | false
16 | false
17 | false
18 |
19 |
--------------------------------------------------------------------------------
/AvaloniaTFTPClient/TFTPClient/Program.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls.ApplicationLifetimes;
3 | using Avalonia.Dialogs;
4 | using System;
5 | namespace UIClient;
6 |
7 | internal class Program
8 | {
9 | // Initialization code. Don't use any Avalonia, third-party APIs or any
10 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
11 | // yet and stuff might break.
12 | [STAThread]
13 | public static void Main(string[] args) => BuildAvaloniaApp()
14 | .StartWithClassicDesktopLifetime(args);
15 |
16 | // Avalonia configuration, don't remove; also used by visual designer.
17 | public static AppBuilder BuildAvaloniaApp()
18 | => AppBuilder.Configure()
19 | .UsePlatformDetect()
20 | .LogToTrace();
21 | //.UseManagedSystemDialogs()
22 | }
23 |
--------------------------------------------------------------------------------
/AvaloniaTFTPClient/TFTPClient/ViewModels/ErrorWindowViewModel.cs:
--------------------------------------------------------------------------------
1 | using CommunityToolkit.Mvvm.ComponentModel;
2 | using CommunityToolkit.Mvvm.Input;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using UIClient.DialogCloser;
9 |
10 | namespace UIClient.ViewModels;
11 |
12 | public partial class ErrorWindowViewModel : ViewModelBase
13 | {
14 | [ObservableProperty]
15 | private string _title = "Error";
16 |
17 | [ObservableProperty]
18 | private string _message = "Message";
19 |
20 | [ObservableProperty]
21 | private string _details = "Details";
22 |
23 | public ErrorWindowViewModel() { }
24 |
25 | [RelayCommand]
26 | private Task DoOk(IDialogCloser closer)
27 | {
28 | closer.Close();
29 | return Task.CompletedTask;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Jean-Paul Mikkers
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/AvaloniaTFTPClient/AvaloniaTFTPClient.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.2.32630.192
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TFTPClient", "TFTPClient\TFTPClient.csproj", "{A1FCCC06-C676-4EC5-9A62-BFE6A963C54A}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {A1FCCC06-C676-4EC5-9A62-BFE6A963C54A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {A1FCCC06-C676-4EC5-9A62-BFE6A963C54A}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {A1FCCC06-C676-4EC5-9A62-BFE6A963C54A}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {A1FCCC06-C676-4EC5-9A62-BFE6A963C54A}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {7006CB9C-EBA3-449F-AE68-8F0D3D17DA53}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/AvaloniaTFTPClient/TFTPClient/App.axaml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
24 |
25 |
26 |
27 |
28 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/AvaloniaTFTPClient/TFTPClient/PersistedData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Baksteen.Net.TFTP.Client;
3 | namespace UIClient;
4 |
5 | public class MySavedState
6 | {
7 | public bool IsDownload { get; set; } = true;
8 | public bool IsAutoGenerateNames { get; set; }
9 | public string Server { get; set; } = string.Empty;
10 | public string RemoteFile { get; set; } = string.Empty;
11 | public string RemoteDir { get; set; } = string.Empty;
12 | public string LocalFile { get; set; } = string.Empty;
13 | public MySavedSettings Settings { get; set; } = MySavedSettings.FromTFTPSettings(new TFTPClient.Settings());
14 | }
15 |
16 | public class MySavedSettings
17 | {
18 | public int BlockSize { get; set; }
19 | public bool DontFragment { get; set; }
20 | public int Timeout { get; set; }
21 | public int Retries { get; set; }
22 | public int Ttl { get; set; }
23 |
24 | public static MySavedSettings FromTFTPSettings(TFTPClient.Settings settings)
25 | {
26 | return new MySavedSettings
27 | {
28 | BlockSize = settings.BlockSize,
29 | DontFragment = settings.DontFragment,
30 | Retries = settings.Retries,
31 | Timeout = (int)settings.ResponseTimeout.TotalMilliseconds,
32 | Ttl = settings.Ttl
33 | };
34 | }
35 |
36 | public TFTPClient.Settings ToTFTPSettings()
37 | {
38 | return new TFTPClient.Settings()
39 | {
40 | BlockSize = BlockSize,
41 | DontFragment = DontFragment,
42 | ResponseTimeout = TimeSpan.FromMilliseconds(Timeout),
43 | Retries = Retries,
44 | Ttl = (short)Ttl
45 | };
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/AvaloniaTFTPClient/TFTPClient/TFTPClient.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | WinExe
4 | net9.0
5 | enable
6 |
7 | copyused
8 | true
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/AvaloniaTFTPClient/TFTPClient/App.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls.ApplicationLifetimes;
3 | using Avalonia.Markup.Xaml;
4 | using UIClient.ViewModels;
5 | using UIClient.Views;
6 | using Avalonia.Data.Core.Plugins;
7 | using System.Linq;
8 | namespace UIClient;
9 |
10 | public partial class App : Application
11 | {
12 | private readonly SuspensionDriver suspensionDriver = new("AvaloniaTFTPClient");
13 |
14 | public override void Initialize()
15 | {
16 | AvaloniaXamlLoader.Load(this);
17 | }
18 |
19 | public override void OnFrameworkInitializationCompleted()
20 | {
21 | if(ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
22 | {
23 | // Line below is needed to remove Avalonia data validation.
24 | // Without this line you will get duplicate validations from both Avalonia and CT
25 |
26 | foreach(var validator in BindingPlugins.DataValidators.Where(x => x is DataAnnotationsValidationPlugin).ToList())
27 | {
28 | BindingPlugins.DataValidators.Remove(validator);
29 | }
30 |
31 | //desktop.MainWindow = AppHost!.Services.GetRequiredService();
32 |
33 | desktop.MainWindow = new MainWindow
34 | {
35 | DataContext = suspensionDriver.LoadState(),
36 | };
37 |
38 | desktop.ShutdownRequested += Desktop_ShutdownRequested;
39 | }
40 |
41 | base.OnFrameworkInitializationCompleted();
42 | }
43 |
44 | private void Desktop_ShutdownRequested(object? sender, ShutdownRequestedEventArgs e)
45 | {
46 | if(sender is IClassicDesktopStyleApplicationLifetime desktop)
47 | {
48 | if(desktop.MainWindow?.DataContext is MainWindowViewModel vm)
49 | {
50 | suspensionDriver.SaveState(vm);
51 | }
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/AvaloniaTFTPClient/TFTPClient/Views/ErrorWindow.axaml:
--------------------------------------------------------------------------------
1 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
32 |
33 |
43 |
44 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/AvaloniaTFTPClient/TFTPClient/DialogCloser/DialogCloserConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Avalonia.Data.Converters;
3 | using System.Globalization;
4 | using Avalonia.Controls;
5 |
6 | namespace UIClient.DialogCloser;
7 |
8 | ///
9 | /// ValueConverter that converts a Window to an . With the following XAML declaration you can
10 | /// then bind buttons to a command that accepts an IDialogCloser interface that lets you close the window/dialog from the
11 | /// viewmodel:
12 | ///
13 | ///
14 | ///
15 | ///
16 | /// <Button
17 | /// IsDefault="True"
18 | /// Command="{CompiledBinding DoOkCommand}"
19 | /// CommandParameter="{CompiledBinding RelativeSource={RelativeSource AncestorType=Window}, Converter={StaticResource dialogCloserConverter}}">
20 | /// OK
21 | /// </Button>
22 | ///
23 | ///
24 | /// And the following command in the viewmodel:
25 | ///
26 | ///
27 | /// [RelayCommand]
28 | /// private async Task DoOk(IDialogCloser closer)
29 | /// {
30 | /// closer.Close(this);
31 | /// await Task.CompletedTask;
32 | /// }
33 | ///
34 | ///
35 | ///
36 | public class DialogCloserConverter : IValueConverter
37 | {
38 | private class Wrapper(Window window) : IDialogCloser
39 | {
40 | public void Close() => window.Close();
41 | public void Close(object? result) => window.Close(result);
42 | }
43 |
44 | public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
45 | {
46 | if(value is Window source)
47 | {
48 | return new Wrapper(source);
49 | }
50 | throw new InvalidCastException();
51 | }
52 |
53 | public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
54 | => throw new NotImplementedException();
55 | }
56 |
--------------------------------------------------------------------------------
/AvaloniaTFTPClient/TFTPClient/ViewModels/SettingsWindowViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Avalonia.Controls;
4 | using Baksteen.Net.TFTP.Client;
5 | using CommunityToolkit.Mvvm.ComponentModel;
6 | using CommunityToolkit.Mvvm.Input;
7 | using UIClient.DialogCloser;
8 | using static Baksteen.Net.TFTP.Client.TFTPClient;
9 |
10 | namespace UIClient.ViewModels;
11 |
12 | public partial class SettingsWindowViewModel : ViewModelBase
13 | {
14 | [ObservableProperty]
15 | private int _blockSize;
16 |
17 | [ObservableProperty]
18 | private int _timeout = 0;
19 |
20 | [ObservableProperty]
21 | private int _retries = 0;
22 |
23 | [ObservableProperty]
24 | private int _ttl = 1;
25 |
26 | [ObservableProperty]
27 | private bool _dontFragment;
28 |
29 | public SettingsWindowViewModel(TFTPClient.Settings? settings)
30 | {
31 | if(settings is not null)
32 | {
33 | Settings = settings;
34 | }
35 | }
36 |
37 | public SettingsWindowViewModel() : this(null)
38 | {
39 | }
40 |
41 | public TFTPClient.Settings Settings {
42 | get => new()
43 | {
44 | BlockSize = BlockSize,
45 | DontFragment = DontFragment,
46 | Retries = Retries,
47 | ResponseTimeout = TimeSpan.FromMilliseconds(Timeout),
48 | Ttl = (short)Ttl
49 | };
50 |
51 | set {
52 | DontFragment = value.DontFragment;
53 | BlockSize = value.BlockSize;
54 | Retries = value.Retries;
55 | Timeout = (int)value.ResponseTimeout.TotalMilliseconds;
56 | Ttl = value.Ttl;
57 | }
58 | }
59 |
60 | [RelayCommand]
61 | private async Task DoOk(IDialogCloser closer)
62 | {
63 | closer.Close(this);
64 | await Task.CompletedTask;
65 | }
66 |
67 | [RelayCommand]
68 | private async Task DoCancel(IDialogCloser closer)
69 | {
70 | closer.Close();
71 | await Task.CompletedTask;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/AvaloniaTFTPClient/TFTPClient/SuspensionDriver.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using UIClient.ViewModels;
3 | using System.Text.Json;
4 | using System.IO;
5 | namespace UIClient;
6 |
7 | public class SuspensionDriver(string applicationName)
8 | {
9 | private JsonSerializerOptions jsonSerializerOptions = new() { WriteIndented = true };
10 |
11 | private string GetLocalApplicationFolder()
12 | {
13 | var result = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData, Environment.SpecialFolderOption.Create), applicationName);
14 | if(!Directory.Exists(result))
15 | {
16 | Directory.CreateDirectory(result);
17 | }
18 | return result;
19 | }
20 |
21 | // on linux: ~/.local/share/AvaloniaTFTPClient/State.json
22 | // on windows: %LocalAppData%\AvaloniaTFTPClient\State.json
23 | private string StatePath => Path.Combine(GetLocalApplicationFolder(), "State.json");
24 |
25 | public MainWindowViewModel LoadState()
26 | {
27 | try
28 | {
29 | using var stream = File.OpenRead(StatePath);
30 | var state = JsonSerializer.Deserialize(stream);
31 |
32 | if(state != null)
33 | {
34 | return new()
35 | {
36 | IsAutoGenerateNames = state.IsAutoGenerateNames,
37 | IsDownload = state.IsDownload,
38 | Server = state.Server,
39 | RemoteDir = state.RemoteDir,
40 | LocalFile = state.LocalFile,
41 | RemoteFile = state.RemoteFile,
42 | Settings = state.Settings.ToTFTPSettings()
43 | };
44 | }
45 | }
46 | catch
47 | {
48 | }
49 | return new MainWindowViewModel();
50 | }
51 |
52 | public void SaveState(MainWindowViewModel vm)
53 | {
54 | try
55 | {
56 | using var stream = File.Create(StatePath);
57 | jsonSerializerOptions = new JsonSerializerOptions { WriteIndented = true };
58 | JsonSerializer.Serialize(stream,
59 | new MySavedState
60 | {
61 | Server = vm.Server,
62 | IsDownload = vm.IsDownload,
63 | LocalFile = vm.LocalFile,
64 | RemoteFile = vm.RemoteFile,
65 | RemoteDir = vm.RemoteDir,
66 | IsAutoGenerateNames = vm.IsAutoGenerateNames,
67 | Settings = MySavedSettings.FromTFTPSettings(vm.Settings)
68 | },
69 | jsonSerializerOptions);
70 | }
71 | catch
72 | {
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/AvaloniaTFTPClient/TFTPClient/Views/MainWindow.axaml.cs:
--------------------------------------------------------------------------------
1 | using UIClient.ViewModels;
2 | using System.Threading.Tasks;
3 | using Baksteen.Net.TFTP.Client;
4 | using Avalonia.Platform.Storage;
5 | using Avalonia.Controls;
6 | using System;
7 | using System.Linq;
8 | namespace UIClient.Views;
9 |
10 | public partial class MainWindow : Window
11 | {
12 | public MainWindow()
13 | {
14 | InitializeComponent();
15 | //this.Title = $"{Assembly.GetEntryAssembly()!.GetName().Version}";
16 | }
17 |
18 | protected override void OnDataContextChanged(EventArgs e)
19 | {
20 | base.OnDataContextChanged(e);
21 | if(DataContext is MainWindowViewModel viewModel)
22 | {
23 | viewModel.InteractionOpenFile = DoShowOpenFileDialogAsync;
24 | viewModel.InteractionSaveFile = DoShowSaveFileDialogAsync;
25 | viewModel.InteractionShowError = DoShowErrorAsync;
26 | viewModel.InteractionShowSettings = DoShowSettingsAsync;
27 | }
28 | }
29 |
30 | private async Task DoShowErrorAsync(Exception ex)
31 | {
32 | var dialog = new ErrorWindow()
33 | {
34 | DataContext = new ErrorWindowViewModel
35 | {
36 | Details = ex.ToString(),
37 | Message = ex.Message,
38 | Title = "TFTPClient Error"
39 | }
40 | };
41 | await dialog.ShowDialog(this);
42 | }
43 |
44 | private async Task DoShowSettingsAsync(TFTPClient.Settings settings)
45 | {
46 | var dialog = new SettingsWindow
47 | {
48 | DataContext = new SettingsWindowViewModel()
49 | {
50 | Settings = settings
51 | }
52 | };
53 | return (await dialog.ShowDialog(this))?.Settings;
54 | }
55 |
56 | private async Task DoShowOpenFileDialogAsync()
57 | {
58 | var files = await StorageProvider.OpenFilePickerAsync(
59 | new FilePickerOpenOptions
60 | {
61 | AllowMultiple = false,
62 | FileTypeFilter = new[] { FilePickerFileTypes.All },
63 | Title = "Select file to upload.."
64 | }
65 | );
66 |
67 | return files.FirstOrDefault()?.TryGetLocalPath();
68 | }
69 |
70 | private async Task DoShowSaveFileDialogAsync()
71 | {
72 | var file = await StorageProvider.SaveFilePickerAsync(
73 | new FilePickerSaveOptions
74 | {
75 | ShowOverwritePrompt = true, // doesn't work most of the time for managed mode..
76 | Title = "Save file as..",
77 | FileTypeChoices = [FilePickerFileTypes.All]
78 | }
79 | );
80 |
81 | return file?.Path.LocalPath;
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/Icon/tftpclient.svg:
--------------------------------------------------------------------------------
1 |
2 |
126 |
--------------------------------------------------------------------------------
/AvaloniaTFTPClient/TFTPClient/Views/MainWindow.axaml:
--------------------------------------------------------------------------------
1 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | _Download
44 | _Upload
45 |
46 |
47 | _Auto construct remote filename
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/AvaloniaTFTPClient/TFTPClient/Views/SettingsWindow.axaml:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
46 |
52 |
53 |
60 |
72 |
78 |
79 |
86 |
96 |
103 |
104 |
111 |
122 |
129 |
130 |
137 |
147 |
154 |
155 |
163 |
171 |
179 |
180 |
181 |
182 |
--------------------------------------------------------------------------------
/AvaloniaTFTPClient/TFTPClient/ViewModels/MainWindowViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Threading.Tasks;
4 | using System.Net;
5 | using System.Linq;
6 | using Baksteen.Net.TFTP.Client;
7 | using System.Net.Sockets;
8 | using System.ComponentModel.DataAnnotations;
9 |
10 | using CommunityToolkit.Mvvm.ComponentModel;
11 | using CommunityToolkit.Mvvm.Input;
12 |
13 | namespace UIClient.ViewModels;
14 |
15 | // use NotifyDataErrorInfo for mvvmct validation, see https://github.com/AvaloniaUI/Avalonia/issues/8397
16 | // then on Apply/OK buttons you can bind IsEnabled to !HasErrors
17 | public partial class MainWindowViewModel : ObservableValidator
18 | {
19 | [ObservableProperty]
20 | [Required(AllowEmptyStrings = false, ErrorMessage = "This field is required")]
21 | [NotifyDataErrorInfo]
22 | private string _server = "localhost:69";
23 |
24 | [ObservableProperty]
25 | private string _status = " ";
26 |
27 | [ObservableProperty]
28 | private bool _isAutoGenerateNames = true;
29 |
30 | [ObservableProperty]
31 | private bool _isDownload = true;
32 |
33 | [ObservableProperty]
34 | private double _progress = 0.0;
35 |
36 | [ObservableProperty]
37 | private string _remoteDir = "";
38 |
39 | [ObservableProperty]
40 | [Required(AllowEmptyStrings = false, ErrorMessage = "This field is required")]
41 | [NotifyDataErrorInfo]
42 | private string _remoteFile = "";
43 |
44 | [ObservableProperty]
45 | [Required(AllowEmptyStrings = false, ErrorMessage = "This field is required")]
46 | [NotifyDataErrorInfo]
47 | private string _localFile = "";
48 |
49 | [ObservableProperty]
50 | private bool _isBusy = false;
51 |
52 | public bool IsIndeterminateProgress { get; set; } = false;
53 |
54 | partial void OnIsAutoGenerateNamesChanged(bool value) => GenerateRemoteFile();
55 |
56 | partial void OnRemoteDirChanged(string value) => GenerateRemoteFile();
57 |
58 | partial void OnLocalFileChanged(string value) => GenerateRemoteFile();
59 |
60 | public TFTPClient.Settings Settings { get; set; } = new TFTPClient.Settings();
61 |
62 | public Func> InteractionOpenFile { get; set; } = () => Task.FromResult(null);
63 |
64 | public Func> InteractionShowSettings { get; set; } = _ => Task.FromResult(null);
65 | public Func> InteractionSaveFile { get; set; } = () => Task.FromResult(null);
66 |
67 | public Func InteractionShowError { get; set; } = _ => Task.CompletedTask;
68 |
69 | private void GenerateRemoteFile()
70 | {
71 | if(IsAutoGenerateNames)
72 | {
73 | RemoteFile = Path.Combine(RemoteDir, Path.GetFileName(LocalFile));
74 | }
75 | }
76 |
77 | [RelayCommand]
78 | private async Task DoDownloadUpload()
79 | {
80 | try
81 | {
82 | if(string.IsNullOrWhiteSpace(LocalFile))
83 | {
84 | throw new ValidationException("Please enter a valid local filename");
85 | }
86 |
87 | if(string.IsNullOrWhiteSpace(RemoteFile))
88 | {
89 | throw new ValidationException("Please enter a valid remote filename");
90 | }
91 |
92 | Progress = 0.0;
93 | Status = $"Starting {(IsDownload ? "download" : "upload")} ...";
94 |
95 | var settings = new TFTPClient.Settings
96 | {
97 | OnProgress = OnProgress,
98 | ProgressInterval = TimeSpan.FromMilliseconds(500),
99 | BlockSize = Settings.BlockSize,
100 | DontFragment = Settings.DontFragment,
101 | ResponseTimeout = Settings.ResponseTimeout,
102 | Retries = Settings.Retries,
103 | Ttl = Settings.Ttl
104 | };
105 |
106 | try
107 | {
108 | IsBusy = true;
109 | var endpoint = await ResolveServer(Server);
110 |
111 | if(IsDownload)
112 | {
113 | await TFTPClient.DownloadAsync(endpoint, LocalFile, RemoteFile, settings);
114 | Status = $"Download of '{RemoteFile}' complete.";
115 | }
116 | else
117 | {
118 | await TFTPClient.UploadAsync(endpoint, LocalFile, RemoteFile, settings);
119 | Status = $"Upload of '{RemoteFile}' complete.";
120 | }
121 | }
122 | finally
123 | {
124 | IsBusy = false;
125 | }
126 | }
127 | catch(Exception ex)
128 | {
129 | Status = $"Error: '{ex.Message}'";
130 | await InteractionShowError(ex);
131 | }
132 | }
133 |
134 | ///
135 | /// Parses/resolves a string to an endpoint, supporting the following formats:
136 | /// x.x.x.x -> 127.0.0.1
137 | /// x.x.x.x:p -> 127.0.0.1:69
138 | /// xxxx:xxxx:xxxx:xxxx -> fe80::6982:bedb:3ffd:5741
139 | /// [xxxx:xxxx:xxxx:xxxx]:p -> [fe80::6982:bedb:3ffd:5741]:69
140 | /// hostname -> localhost
141 | /// hostname:p -> localhost:69
142 | ///
143 | /// string to parse
144 | /// the IPEndPoint or null
145 | private static async Task ResolveServer(string server)
146 | {
147 | // nested functions are cool
148 | static int ParseIntDefault(string str, int def)
149 | {
150 | return int.TryParse(str, out var val) ? val : def;
151 | }
152 |
153 | IPEndPoint? result = null;
154 | IPAddress? address = null;
155 | int port = 69;
156 |
157 | // attempt to parse it as a ipv6 address
158 | var parts = server.Split(["[", "]:"], StringSplitOptions.RemoveEmptyEntries);
159 |
160 | if(parts.Length > 0 && IPAddress.TryParse(parts[0], out address))
161 | {
162 | if(parts.Length > 1)
163 | {
164 | port = ParseIntDefault(parts[1], 69);
165 | }
166 |
167 | result = new IPEndPoint(address, port);
168 | }
169 | else
170 | {
171 | // no luck, try it as a ipv4 address
172 | parts = server.Split([":"], StringSplitOptions.RemoveEmptyEntries);
173 |
174 | if(parts.Length > 0)
175 | {
176 | if(parts.Length > 1)
177 | {
178 | port = ParseIntDefault(parts[1], 69);
179 | }
180 |
181 | if(IPAddress.TryParse(parts[0], out address))
182 | {
183 | result = new IPEndPoint(address, port);
184 | }
185 | else
186 | {
187 | // still nothing, resolve the hostname
188 | var addressList = (await Dns.GetHostEntryAsync(parts[0])).AddressList;
189 |
190 | // prefer ipv4 addresses, fall back to ipv6
191 | address = addressList.Where(x => x.AddressFamily == AddressFamily.InterNetwork).FirstOrDefault() ??
192 | addressList.Where(x => x.AddressFamily == AddressFamily.InterNetworkV6).FirstOrDefault();
193 |
194 | if(address != null)
195 | {
196 | result = new IPEndPoint(address, port);
197 | }
198 | }
199 | }
200 | }
201 |
202 | if(result == null)
203 | {
204 | throw new ArgumentException("Couldn't resolve the hostname or IP address");
205 | }
206 |
207 | return result;
208 | }
209 |
210 | private void OnProgress(object? sender, TFTPClient.ProgressEventArgs e)
211 | {
212 | Progress = (e.TransferSize > 0) ? (int)(100.0 * e.Transferred / e.TransferSize) : 0;
213 | Status = $"({e.Transferred}/{((e.TransferSize >= 0) ? e.TransferSize.ToString() : "?")} bytes) {(e.IsUpload ? "Uploading" : "Downloading")} '{e.Filename}'";
214 | }
215 |
216 | [RelayCommand]
217 | private async Task DoSelectFile()
218 | {
219 | if(IsDownload)
220 | {
221 | var fn = await InteractionSaveFile();
222 | if(fn != null)
223 | {
224 | LocalFile = fn;
225 | }
226 | }
227 | else
228 | {
229 | var fn = await InteractionOpenFile();
230 | if(fn != null)
231 | {
232 | LocalFile = fn;
233 | }
234 | }
235 | }
236 |
237 | [RelayCommand]
238 | private async Task DoSettings()
239 | {
240 | var result = await InteractionShowSettings(Settings);
241 |
242 | if(result != null)
243 | {
244 | Settings = result;
245 | }
246 | }
247 |
248 | public MainWindowViewModel() : base()
249 | {
250 | ValidateAllProperties();
251 | }
252 | }
253 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Ww][Ii][Nn]32/
27 | [Aa][Rr][Mm]/
28 | [Aa][Rr][Mm]64/
29 | bld/
30 | [Bb]in/
31 | [Oo]bj/
32 | [Ll]og/
33 | [Ll]ogs/
34 |
35 | # Visual Studio 2015/2017 cache/options directory
36 | .vs/
37 | # Uncomment if you have tasks that create the project's static files in wwwroot
38 | #wwwroot/
39 |
40 | # Visual Studio 2017 auto generated files
41 | Generated\ Files/
42 |
43 | # MSTest test Results
44 | [Tt]est[Rr]esult*/
45 | [Bb]uild[Ll]og.*
46 |
47 | # NUnit
48 | *.VisualState.xml
49 | TestResult.xml
50 | nunit-*.xml
51 |
52 | # Build Results of an ATL Project
53 | [Dd]ebugPS/
54 | [Rr]eleasePS/
55 | dlldata.c
56 |
57 | # Benchmark Results
58 | BenchmarkDotNet.Artifacts/
59 |
60 | # .NET Core
61 | project.lock.json
62 | project.fragment.lock.json
63 | artifacts/
64 |
65 | # Tye
66 | .tye/
67 |
68 | # ASP.NET Scaffolding
69 | ScaffoldingReadMe.txt
70 |
71 | # StyleCop
72 | StyleCopReport.xml
73 |
74 | # Files built by Visual Studio
75 | *_i.c
76 | *_p.c
77 | *_h.h
78 | *.ilk
79 | *.meta
80 | *.obj
81 | *.iobj
82 | *.pch
83 | *.pdb
84 | *.ipdb
85 | *.pgc
86 | *.pgd
87 | *.rsp
88 | *.sbr
89 | *.tlb
90 | *.tli
91 | *.tlh
92 | *.tmp
93 | *.tmp_proj
94 | *_wpftmp.csproj
95 | *.log
96 | *.vspscc
97 | *.vssscc
98 | .builds
99 | *.pidb
100 | *.svclog
101 | *.scc
102 |
103 | # Chutzpah Test files
104 | _Chutzpah*
105 |
106 | # Visual C++ cache files
107 | ipch/
108 | *.aps
109 | *.ncb
110 | *.opendb
111 | *.opensdf
112 | *.sdf
113 | *.cachefile
114 | *.VC.db
115 | *.VC.VC.opendb
116 |
117 | # Visual Studio profiler
118 | *.psess
119 | *.vsp
120 | *.vspx
121 | *.sap
122 |
123 | # Visual Studio Trace Files
124 | *.e2e
125 |
126 | # TFS 2012 Local Workspace
127 | $tf/
128 |
129 | # Guidance Automation Toolkit
130 | *.gpState
131 |
132 | # ReSharper is a .NET coding add-in
133 | _ReSharper*/
134 | *.[Rr]e[Ss]harper
135 | *.DotSettings.user
136 |
137 | # TeamCity is a build add-in
138 | _TeamCity*
139 |
140 | # DotCover is a Code Coverage Tool
141 | *.dotCover
142 |
143 | # AxoCover is a Code Coverage Tool
144 | .axoCover/*
145 | !.axoCover/settings.json
146 |
147 | # Coverlet is a free, cross platform Code Coverage Tool
148 | coverage*.json
149 | coverage*.xml
150 | coverage*.info
151 |
152 | # Visual Studio code coverage results
153 | *.coverage
154 | *.coveragexml
155 |
156 | # NCrunch
157 | _NCrunch_*
158 | .*crunch*.local.xml
159 | nCrunchTemp_*
160 |
161 | # MightyMoose
162 | *.mm.*
163 | AutoTest.Net/
164 |
165 | # Web workbench (sass)
166 | .sass-cache/
167 |
168 | # Installshield output folder
169 | [Ee]xpress/
170 |
171 | # DocProject is a documentation generator add-in
172 | DocProject/buildhelp/
173 | DocProject/Help/*.HxT
174 | DocProject/Help/*.HxC
175 | DocProject/Help/*.hhc
176 | DocProject/Help/*.hhk
177 | DocProject/Help/*.hhp
178 | DocProject/Help/Html2
179 | DocProject/Help/html
180 |
181 | # Click-Once directory
182 | publish/
183 |
184 | # Publish Web Output
185 | *.[Pp]ublish.xml
186 | *.azurePubxml
187 | # Note: Comment the next line if you want to checkin your web deploy settings,
188 | # but database connection strings (with potential passwords) will be unencrypted
189 | # *.pubxml
190 | *.publishproj
191 |
192 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
193 | # checkin your Azure Web App publish settings, but sensitive information contained
194 | # in these scripts will be unencrypted
195 | PublishScripts/
196 |
197 | # NuGet Packages
198 | *.nupkg
199 | # NuGet Symbol Packages
200 | *.snupkg
201 | # The packages folder can be ignored because of Package Restore
202 | **/[Pp]ackages/*
203 | # except build/, which is used as an MSBuild target.
204 | !**/[Pp]ackages/build/
205 | # Uncomment if necessary however generally it will be regenerated when needed
206 | #!**/[Pp]ackages/repositories.config
207 | # NuGet v3's project.json files produces more ignorable files
208 | *.nuget.props
209 | *.nuget.targets
210 |
211 | # Microsoft Azure Build Output
212 | csx/
213 | *.build.csdef
214 |
215 | # Microsoft Azure Emulator
216 | ecf/
217 | rcf/
218 |
219 | # Windows Store app package directories and files
220 | AppPackages/
221 | BundleArtifacts/
222 | Package.StoreAssociation.xml
223 | _pkginfo.txt
224 | *.appx
225 | *.appxbundle
226 | *.appxupload
227 |
228 | # Visual Studio cache files
229 | # files ending in .cache can be ignored
230 | *.[Cc]ache
231 | # but keep track of directories ending in .cache
232 | !?*.[Cc]ache/
233 |
234 | # Others
235 | ClientBin/
236 | ~$*
237 | *~
238 | *.dbmdl
239 | *.dbproj.schemaview
240 | *.jfm
241 | *.pfx
242 | *.publishsettings
243 | orleans.codegen.cs
244 |
245 | # Including strong name files can present a security risk
246 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
247 | #*.snk
248 |
249 | # Since there are multiple workflows, uncomment next line to ignore bower_components
250 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
251 | #bower_components/
252 |
253 | # RIA/Silverlight projects
254 | Generated_Code/
255 |
256 | # Backup & report files from converting an old project file
257 | # to a newer Visual Studio version. Backup files are not needed,
258 | # because we have git ;-)
259 | _UpgradeReport_Files/
260 | Backup*/
261 | UpgradeLog*.XML
262 | UpgradeLog*.htm
263 | ServiceFabricBackup/
264 | *.rptproj.bak
265 |
266 | # SQL Server files
267 | *.mdf
268 | *.ldf
269 | *.ndf
270 |
271 | # Business Intelligence projects
272 | *.rdl.data
273 | *.bim.layout
274 | *.bim_*.settings
275 | *.rptproj.rsuser
276 | *- [Bb]ackup.rdl
277 | *- [Bb]ackup ([0-9]).rdl
278 | *- [Bb]ackup ([0-9][0-9]).rdl
279 |
280 | # Microsoft Fakes
281 | FakesAssemblies/
282 |
283 | # GhostDoc plugin setting file
284 | *.GhostDoc.xml
285 |
286 | # Node.js Tools for Visual Studio
287 | .ntvs_analysis.dat
288 | node_modules/
289 |
290 | # Visual Studio 6 build log
291 | *.plg
292 |
293 | # Visual Studio 6 workspace options file
294 | *.opt
295 |
296 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
297 | *.vbw
298 |
299 | # Visual Studio LightSwitch build output
300 | **/*.HTMLClient/GeneratedArtifacts
301 | **/*.DesktopClient/GeneratedArtifacts
302 | **/*.DesktopClient/ModelManifest.xml
303 | **/*.Server/GeneratedArtifacts
304 | **/*.Server/ModelManifest.xml
305 | _Pvt_Extensions
306 |
307 | # Paket dependency manager
308 | .paket/paket.exe
309 | paket-files/
310 |
311 | # FAKE - F# Make
312 | .fake/
313 |
314 | # CodeRush personal settings
315 | .cr/personal
316 |
317 | # Python Tools for Visual Studio (PTVS)
318 | __pycache__/
319 | *.pyc
320 |
321 | # Cake - Uncomment if you are using it
322 | # tools/**
323 | # !tools/packages.config
324 |
325 | # Tabs Studio
326 | *.tss
327 |
328 | # Telerik's JustMock configuration file
329 | *.jmconfig
330 |
331 | # BizTalk build output
332 | *.btp.cs
333 | *.btm.cs
334 | *.odx.cs
335 | *.xsd.cs
336 |
337 | # OpenCover UI analysis results
338 | OpenCover/
339 |
340 | # Azure Stream Analytics local run output
341 | ASALocalRun/
342 |
343 | # MSBuild Binary and Structured Log
344 | *.binlog
345 |
346 | # NVidia Nsight GPU debugger configuration file
347 | *.nvuser
348 |
349 | # MFractors (Xamarin productivity tool) working folder
350 | .mfractor/
351 |
352 | # Local History for Visual Studio
353 | .localhistory/
354 |
355 | # BeatPulse healthcheck temp database
356 | healthchecksdb
357 |
358 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
359 | MigrationBackup/
360 |
361 | # Ionide (cross platform F# VS Code tools) working folder
362 | .ionide/
363 |
364 | # Fody - auto-generated XML schema
365 | FodyWeavers.xsd
366 |
367 | ##
368 | ## Visual studio for Mac
369 | ##
370 |
371 |
372 | # globs
373 | Makefile.in
374 | *.userprefs
375 | *.usertasks
376 | config.make
377 | config.status
378 | aclocal.m4
379 | install-sh
380 | autom4te.cache/
381 | *.tar.gz
382 | tarballs/
383 | test-results/
384 |
385 | # Mac bundle stuff
386 | *.dmg
387 | *.app
388 |
389 | # content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
390 | # General
391 | .DS_Store
392 | .AppleDouble
393 | .LSOverride
394 |
395 | # Icon must end with two \r
396 | Icon
397 |
398 |
399 | # Thumbnails
400 | ._*
401 |
402 | # Files that might appear in the root of a volume
403 | .DocumentRevisions-V100
404 | .fseventsd
405 | .Spotlight-V100
406 | .TemporaryItems
407 | .Trashes
408 | .VolumeIcon.icns
409 | .com.apple.timemachine.donotpresent
410 |
411 | # Directories potentially created on remote AFP share
412 | .AppleDB
413 | .AppleDesktop
414 | Network Trash Folder
415 | Temporary Items
416 | .apdisk
417 |
418 | # content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
419 | # Windows thumbnail cache files
420 | Thumbs.db
421 | ehthumbs.db
422 | ehthumbs_vista.db
423 |
424 | # Dump file
425 | *.stackdump
426 |
427 | # Folder config file
428 | [Dd]esktop.ini
429 |
430 | # Recycle Bin used on file shares
431 | $RECYCLE.BIN/
432 |
433 | # Windows Installer files
434 | *.cab
435 | *.msi
436 | *.msix
437 | *.msm
438 | *.msp
439 |
440 | # Windows shortcuts
441 | *.lnk
442 |
443 | # JetBrains Rider
444 | .idea/
445 | *.sln.iml
446 |
447 | ##
448 | ## Visual Studio Code
449 | ##
450 | .vscode/*
451 | !.vscode/settings.json
452 | !.vscode/tasks.json
453 | !.vscode/launch.json
454 | !.vscode/extensions.json
455 |
--------------------------------------------------------------------------------
/AvaloniaTFTPClient/.editorconfig:
--------------------------------------------------------------------------------
1 | # Remove the line below if you want to inherit .editorconfig settings from higher directories
2 | root = true
3 |
4 | # C# files
5 | [*.cs]
6 |
7 | #### Core EditorConfig Options ####
8 |
9 | # Indentation and spacing
10 | indent_size = 4
11 | indent_style = space
12 | tab_width = 4
13 |
14 | # New line preferences
15 | end_of_line = crlf
16 | insert_final_newline = false
17 |
18 | #### .NET Code Actions ####
19 |
20 | # Type members
21 | dotnet_hide_advanced_members = false
22 | dotnet_member_insertion_location = with_other_members_of_the_same_kind
23 | dotnet_property_generation_behavior = prefer_throwing_properties
24 |
25 | # Symbol search
26 | dotnet_search_reference_assemblies = true
27 |
28 | #### .NET Coding Conventions ####
29 |
30 | # Organize usings
31 | dotnet_separate_import_directive_groups = false
32 | dotnet_sort_system_directives_first = false
33 | file_header_template = unset
34 |
35 | # this. and Me. preferences
36 | dotnet_style_qualification_for_event = false
37 | dotnet_style_qualification_for_field = false
38 | dotnet_style_qualification_for_method = false
39 | dotnet_style_qualification_for_property = false
40 |
41 | # Language keywords vs BCL types preferences
42 | dotnet_style_predefined_type_for_locals_parameters_members = true
43 | dotnet_style_predefined_type_for_member_access = true
44 |
45 | # Parentheses preferences
46 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
47 | dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
48 | dotnet_style_parentheses_in_other_operators = never_if_unnecessary
49 | dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
50 |
51 | # Modifier preferences
52 | dotnet_style_require_accessibility_modifiers = for_non_interface_members
53 |
54 | # Expression-level preferences
55 | dotnet_prefer_system_hash_code = true
56 | dotnet_style_coalesce_expression = true
57 | dotnet_style_collection_initializer = true
58 | dotnet_style_explicit_tuple_names = true
59 | dotnet_style_namespace_match_folder = true
60 | dotnet_style_null_propagation = true
61 | dotnet_style_object_initializer = true
62 | dotnet_style_operator_placement_when_wrapping = beginning_of_line
63 | dotnet_style_prefer_auto_properties = true
64 | dotnet_style_prefer_collection_expression = when_types_loosely_match
65 | dotnet_style_prefer_compound_assignment = true
66 | dotnet_style_prefer_conditional_expression_over_assignment = true
67 | dotnet_style_prefer_conditional_expression_over_return = true
68 | dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed
69 | dotnet_style_prefer_inferred_anonymous_type_member_names = true
70 | dotnet_style_prefer_inferred_tuple_names = true
71 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true
72 | dotnet_style_prefer_simplified_boolean_expressions = true
73 | dotnet_style_prefer_simplified_interpolation = true
74 |
75 | # Field preferences
76 | dotnet_style_readonly_field = true
77 |
78 | # Parameter preferences
79 | dotnet_code_quality_unused_parameters = all
80 |
81 | # Suppression preferences
82 | dotnet_remove_unnecessary_suppression_exclusions = none
83 |
84 | # New line preferences
85 | dotnet_style_allow_multiple_blank_lines_experimental = true
86 | dotnet_style_allow_statement_immediately_after_block_experimental = true
87 |
88 | #### C# Coding Conventions ####
89 |
90 | # var preferences
91 | csharp_style_var_elsewhere = false
92 | csharp_style_var_for_built_in_types = false
93 | csharp_style_var_when_type_is_apparent = false
94 |
95 | # Expression-bodied members
96 | csharp_style_expression_bodied_accessors = true:silent
97 | csharp_style_expression_bodied_constructors = false:silent
98 | csharp_style_expression_bodied_indexers = true:silent
99 | csharp_style_expression_bodied_lambdas = true:silent
100 | csharp_style_expression_bodied_local_functions = false:silent
101 | csharp_style_expression_bodied_methods = false:silent
102 | csharp_style_expression_bodied_operators = false:silent
103 | csharp_style_expression_bodied_properties = true:silent
104 |
105 | # Pattern matching preferences
106 | csharp_style_pattern_matching_over_as_with_null_check = true
107 | csharp_style_pattern_matching_over_is_with_cast_check = true
108 | csharp_style_prefer_extended_property_pattern = true
109 | csharp_style_prefer_not_pattern = true
110 | csharp_style_prefer_pattern_matching = true
111 | csharp_style_prefer_switch_expression = true
112 |
113 | # Null-checking preferences
114 | csharp_style_conditional_delegate_call = true
115 |
116 | # Modifier preferences
117 | csharp_prefer_static_anonymous_function = true
118 | csharp_prefer_static_local_function = true
119 | csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async
120 | csharp_style_prefer_readonly_struct = true
121 | csharp_style_prefer_readonly_struct_member = true
122 |
123 | # Code-block preferences
124 | csharp_prefer_braces = true:silent
125 | csharp_prefer_simple_using_statement = true:suggestion
126 | csharp_prefer_system_threading_lock = true:suggestion
127 | csharp_style_namespace_declarations = file_scoped:silent
128 | csharp_style_prefer_method_group_conversion = true:silent
129 | csharp_style_prefer_primary_constructors = true:suggestion
130 | csharp_style_prefer_top_level_statements = true:silent
131 |
132 | # Expression-level preferences
133 | csharp_prefer_simple_default_expression = true
134 | csharp_style_deconstructed_variable_declaration = true
135 | csharp_style_implicit_object_creation_when_type_is_apparent = true
136 | csharp_style_inlined_variable_declaration = true
137 | csharp_style_prefer_index_operator = true
138 | csharp_style_prefer_local_over_anonymous_function = true
139 | csharp_style_prefer_null_check_over_type_check = true
140 | csharp_style_prefer_range_operator = true
141 | csharp_style_prefer_tuple_swap = true
142 | csharp_style_prefer_unbound_generic_type_in_nameof = true
143 | csharp_style_prefer_utf8_string_literals = true
144 | csharp_style_throw_expression = true
145 | csharp_style_unused_value_assignment_preference = discard_variable
146 | csharp_style_unused_value_expression_statement_preference = discard_variable
147 |
148 | # 'using' directive preferences
149 | csharp_using_directive_placement = inside_namespace:silent
150 |
151 | # New line preferences
152 | csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
153 | csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true
154 | csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true
155 | csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true
156 | csharp_style_allow_embedded_statements_on_same_line_experimental = true
157 |
158 | #### C# Formatting Rules ####
159 |
160 | # New line preferences
161 | csharp_new_line_before_catch = true
162 | csharp_new_line_before_else = true
163 | csharp_new_line_before_finally = true
164 | csharp_new_line_before_members_in_anonymous_types = true
165 | csharp_new_line_before_members_in_object_initializers = true
166 | csharp_new_line_before_open_brace = all
167 | csharp_new_line_between_query_expression_clauses = true
168 |
169 | # Indentation preferences
170 | csharp_indent_block_contents = true
171 | csharp_indent_braces = false
172 | csharp_indent_case_contents = true
173 | csharp_indent_case_contents_when_block = true
174 | csharp_indent_labels = one_less_than_current
175 | csharp_indent_switch_labels = true
176 |
177 | # Space preferences
178 | csharp_space_after_cast = false
179 | csharp_space_after_colon_in_inheritance_clause = true
180 | csharp_space_after_comma = true
181 | csharp_space_after_dot = false
182 | csharp_space_after_keywords_in_control_flow_statements = false
183 | csharp_space_after_semicolon_in_for_statement = true
184 | csharp_space_around_binary_operators = before_and_after
185 | csharp_space_around_declaration_statements = false
186 | csharp_space_before_colon_in_inheritance_clause = true
187 | csharp_space_before_comma = false
188 | csharp_space_before_dot = false
189 | csharp_space_before_open_square_brackets = false
190 | csharp_space_before_semicolon_in_for_statement = false
191 | csharp_space_between_empty_square_brackets = false
192 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
193 | csharp_space_between_method_call_name_and_opening_parenthesis = false
194 | csharp_space_between_method_call_parameter_list_parentheses = false
195 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
196 | csharp_space_between_method_declaration_name_and_open_parenthesis = false
197 | csharp_space_between_method_declaration_parameter_list_parentheses = false
198 | csharp_space_between_parentheses = false
199 | csharp_space_between_square_brackets = false
200 |
201 | # Wrapping preferences
202 | csharp_preserve_single_line_blocks = true
203 | csharp_preserve_single_line_statements = true
204 |
205 | #### Naming styles ####
206 |
207 | # Naming rules
208 |
209 | dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
210 | dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
211 | dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
212 |
213 | dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
214 | dotnet_naming_rule.types_should_be_pascal_case.symbols = types
215 | dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
216 |
217 | dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
218 | dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
219 | dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
220 |
221 | dotnet_naming_rule.private_or_internal_field_should_be_underscorecamelcase.severity = suggestion
222 | dotnet_naming_rule.private_or_internal_field_should_be_underscorecamelcase.symbols = private_or_internal_field
223 | dotnet_naming_rule.private_or_internal_field_should_be_underscorecamelcase.style = underscorecamelcase
224 |
225 | dotnet_naming_rule.private_or_internal_static_field_should_be_sunderscorecamelcase.severity = suggestion
226 | dotnet_naming_rule.private_or_internal_static_field_should_be_sunderscorecamelcase.symbols = private_or_internal_static_field
227 | dotnet_naming_rule.private_or_internal_static_field_should_be_sunderscorecamelcase.style = sunderscorecamelcase
228 |
229 | # Symbol specifications
230 |
231 | dotnet_naming_symbols.interface.applicable_kinds = interface
232 | dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
233 | dotnet_naming_symbols.interface.required_modifiers =
234 |
235 | dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field
236 | dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private, private_protected
237 | dotnet_naming_symbols.private_or_internal_field.required_modifiers =
238 |
239 | dotnet_naming_symbols.private_or_internal_static_field.applicable_kinds = field
240 | dotnet_naming_symbols.private_or_internal_static_field.applicable_accessibilities = internal, private, private_protected
241 | dotnet_naming_symbols.private_or_internal_static_field.required_modifiers = static
242 |
243 | dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
244 | dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
245 | dotnet_naming_symbols.types.required_modifiers =
246 |
247 | dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
248 | dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
249 | dotnet_naming_symbols.non_field_members.required_modifiers =
250 |
251 | # Naming styles
252 |
253 | dotnet_naming_style.pascal_case.required_prefix =
254 | dotnet_naming_style.pascal_case.required_suffix =
255 | dotnet_naming_style.pascal_case.word_separator =
256 | dotnet_naming_style.pascal_case.capitalization = pascal_case
257 |
258 | dotnet_naming_style.begins_with_i.required_prefix = I
259 | dotnet_naming_style.begins_with_i.required_suffix =
260 | dotnet_naming_style.begins_with_i.word_separator =
261 | dotnet_naming_style.begins_with_i.capitalization = pascal_case
262 |
263 | dotnet_naming_style.underscorecamelcase.required_prefix = _
264 | dotnet_naming_style.underscorecamelcase.required_suffix =
265 | dotnet_naming_style.underscorecamelcase.word_separator =
266 | dotnet_naming_style.underscorecamelcase.capitalization = camel_case
267 |
268 | dotnet_naming_style.sunderscorecamelcase.required_prefix = s_
269 | dotnet_naming_style.sunderscorecamelcase.required_suffix =
270 | dotnet_naming_style.sunderscorecamelcase.word_separator =
271 | dotnet_naming_style.sunderscorecamelcase.capitalization = camel_case
272 |
273 | [*.{cs,vb}]
274 | dotnet_style_operator_placement_when_wrapping = beginning_of_line
275 | tab_width = 4
276 | indent_size = 4
277 | end_of_line = crlf
278 | dotnet_style_coalesce_expression = true:suggestion
279 | dotnet_style_null_propagation = true:suggestion
280 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
--------------------------------------------------------------------------------