├── .editorconfig ├── .github └── FUNDING.yml ├── .gitignore ├── Directory.Build.props ├── Directory.Build.targets ├── Icon.png ├── LICENSE.md ├── NuGet.config ├── README.md ├── appveyor.yml ├── build └── Targets │ ├── AvaloniaDiagnostics.props │ ├── Import.props │ ├── Import.targets │ ├── PackageProperties.props │ ├── PackageProperties.targets │ ├── PackageVersions.targets │ └── RepoLayout.props ├── dotnet-properties.sln ├── install.bat ├── install.sh └── src └── dotnet-properties ├── App.xaml ├── App.xaml.cs ├── DataAnnotations └── NuGetPackageLicenseExpressionAttribute.cs ├── Dialogs ├── Models │ └── UnsavedChangesDialogResult.cs ├── ViewModels │ └── UnsavedChangesDialogViewModel.cs └── Views │ ├── UnsavedChangesDialog.xaml │ └── UnsavedChangesDialog.xaml.cs ├── Pages ├── Models │ ├── DotNetFrameworkTfm.cs │ ├── OutputType.cs │ ├── PlatformTarget.cs │ ├── RunPostBuildEvent.cs │ └── TargetFramework.cs ├── ViewModels │ ├── ApplicationPageViewModel.cs │ ├── BuildEventsPageViewModel.cs │ ├── BuildPageViewModel.cs │ ├── PackagePageViewModel.cs │ ├── PropertyPageViewModel.cs │ └── SigningPageViewModel.cs └── Views │ ├── ApplicationPage.xaml │ ├── ApplicationPage.xaml.cs │ ├── BuildEventsPage.xaml │ ├── BuildEventsPage.xaml.cs │ ├── BuildPage.xaml │ ├── BuildPage.xaml.cs │ ├── PackagePage.xaml │ ├── PackagePage.xaml.cs │ ├── SigningPage.xaml │ └── SigningPage.xaml.cs ├── PagesViewLocator.cs ├── Project ├── BuildEnvironment.cs ├── DotNetSdkPaths.cs ├── MSBuildProject.cs └── MSBuildProperties.cs ├── Properties └── launchSettings.json ├── Resources ├── Fonts │ └── OpenSans │ │ ├── OpenSans-Bold.ttf │ │ ├── OpenSans-BoldItalic.ttf │ │ ├── OpenSans-ExtraBold.ttf │ │ ├── OpenSans-ExtraBoldItalic.ttf │ │ ├── OpenSans-Italic.ttf │ │ ├── OpenSans-Light.ttf │ │ ├── OpenSans-LightItalic.ttf │ │ ├── OpenSans-Regular.ttf │ │ ├── OpenSans-SemiBold.ttf │ │ └── OpenSans-SemiBoldItalic.ttf ├── Icon.ico └── Icon.png ├── Services ├── DialogService.cs ├── DotNetInfo.cs ├── DotNetSdkResolver.cs ├── IDialogService.cs ├── IDotNetSdkResolver.cs ├── IMSBuildLoader.cs ├── IOpenFileDialogService.cs ├── IPropertyManager.cs ├── ITheme.cs ├── IThemeService.cs ├── MSBuildLoader.cs ├── OpenFileDialogService.cs ├── PropertyManager.cs ├── Theme.cs └── ThemeService.cs ├── Styles ├── Accents │ ├── BaseLightBlue.xaml │ ├── CitrusThemeAccents.xaml │ ├── DefaultThemeAccents.xaml │ └── FluentThemeAccents.xaml ├── SideBar.xaml └── Styles.xaml ├── ViewModels └── MainWindowViewModel.cs ├── Views ├── MainWindow.xaml └── MainWindow.xaml.cs └── dotnet-properties.csproj /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_size = 4 5 | indent_style = space 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | end_of_line = crlf 9 | 10 | [*.cs] 11 | csharp_style_conditional_delegate_call = true:warning 12 | csharp_style_expression_bodied_accessors = true:suggestion 13 | csharp_style_expression_bodied_constructors = false:warning 14 | csharp_style_expression_bodied_indexers = true:suggestion 15 | csharp_style_expression_bodied_methods = true:suggestion 16 | csharp_style_expression_bodied_operators = true:suggestion 17 | csharp_style_expression_bodied_properties = true:suggestion 18 | csharp_style_inlined_variable_declaration = true:warning 19 | csharp_style_pattern_matching_over_as_with_null_check = true:warning 20 | csharp_style_pattern_matching_over_is_with_cast_check = true:warning 21 | csharp_style_throw_expression = true:warning 22 | csharp_style_var_elsewhere = true:suggestion 23 | csharp_style_var_for_built_in_types = false:none 24 | csharp_style_var_when_type_is_apparent = true:suggestion 25 | 26 | csharp_new_line_before_catch = true:warning 27 | csharp_new_line_before_else = true:warning 28 | csharp_new_line_before_finally = true:warning 29 | csharp_new_line_before_members_in_anonymous_types = true:warning 30 | csharp_new_line_before_members_in_object_initializers = true:warning 31 | #csharp_new_line_before_open_brace = all:warning 32 | csharp_new_line_between_query_expression_clauses = true:warning 33 | 34 | csharp_indent_case_contents = true:warning 35 | csharp_indent_labels = one_less_than_current:suggestion 36 | csharp_indent_switch_labels = true:warning 37 | 38 | csharp_preserve_single_line_blocks = true:suggestion 39 | csharp_preserve_single_line_statements = false:warning 40 | 41 | csharp_space_after_cast = false:warning 42 | csharp_space_after_keywords_in_control_flow_statements = true:warning 43 | csharp_space_between_method_call_parameter_list_parentheses = false:warning 44 | csharp_space_between_method_declaration_parameter_list_parentheses = false:warning 45 | csharp_space_between_parentheses = false:warning 46 | 47 | csharp_prefer_braces = true:warning 48 | csharp_prefer_simple_default_expression = false:none 49 | 50 | dotnet_sort_system_directives_first = true:suggestion 51 | 52 | dotnet_style_coalesce_expression = true:warning 53 | dotnet_style_collection_initializer = true:suggestion 54 | dotnet_style_explicit_tuple_names = true:suggestion 55 | dotnet_style_null_propagation = true:warning 56 | dotnet_style_object_initializer = true:suggestion 57 | dotnet_style_predefined_type_for_locals_parameters_members = true:warning 58 | dotnet_style_predefined_type_for_member_access = false:warning 59 | dotnet_style_qualification_for_event = false:warning 60 | dotnet_style_qualification_for_field = false:warning 61 | dotnet_style_qualification_for_method = false:warning 62 | dotnet_style_qualification_for_property = false:warning 63 | 64 | dotnet_naming_rule.camel_case_for_private_fields.severity = suggestion 65 | dotnet_naming_rule.camel_case_for_private_fields.symbols = private_fields 66 | dotnet_naming_rule.camel_case_for_private_fields.style = camel_case_underscore_style 67 | 68 | dotnet_naming_symbols.private_fields.applicable_kinds = field 69 | dotnet_naming_symbols.private_fields.applicable_accessibilities = private 70 | 71 | dotnet_naming_style.camel_case_underscore_style.required_prefix = _ 72 | dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case 73 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: jp2masa 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # User-specific files 2 | 3 | .vs/ 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Build results 10 | 11 | [Aa]rtifacts/ 12 | [Bb]in/ 13 | [Oo]bj/ 14 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildThisFileDirectory) 5 | 6 | 7 | 8 | 8.0 9 | Enable 10 | CA1303;CA1812;CA1822;$(NoWarn) 11 | 12 | 13 | 14 | 15 | False 16 | 17 | 18 | 19 | 0.3.0 20 | -build.$(APPVEYOR_BUILD_NUMBER)+$(APPVEYOR_REPO_COMMIT.Substring(0, 7)) 21 | -localbuild$([System.DateTime]::Now.ToString("yyyyMMddHHmmss")) 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jp2masa/dotnet-properties/73755a414f0e4a191719e69f9644e0427e61cd76/Icon.png -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright © jp2masa 5 | 6 | Permission is hereby granted, free of charge, to any person 7 | obtaining a copy of this software and associated documentation 8 | files (the “Software”), to deal in the Software without 9 | restriction, including without limitation the rights to use, 10 | copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the 12 | Software is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | OTHER DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /NuGet.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build status](https://ci.appveyor.com/api/projects/status/ibaest9dvdww8dtg/branch/master?svg=true)](https://ci.appveyor.com/project/jp2masa/dotnet-properties/branch/master) 2 | [![NuGet](https://img.shields.io/nuget/v/dotnet-properties.svg)](https://www.nuget.org/packages/dotnet-properties/) 3 | [![MyGet](https://img.shields.io/myget/jp2masa/vpre/dotnet-properties.svg?label=myget)](https://www.myget.org/feed/jp2masa/package/nuget/dotnet-properties) 4 | 5 | # dotnet-properties 6 | 7 | ![dotnet-properties](Icon.png) 8 | 9 | dotnet-properties is a .NET Core CLI extension which allows to edit project properties using a cross platform UI. 10 | 11 | ## Special Thanks 12 | 13 | ### Avalonia 14 | 15 | It's the UI framework used by this project and part of the UI is inspired on the control catalog sample. 16 | 17 | [](https://github.com/AvaloniaUI/Avalonia) 18 | 19 | ### Buildalyzer 20 | 21 | A big part of the project loading code comes from Buildalyzer. 22 | 23 | [](https://github.com/daveaglick/Buildalyzer) 24 | 25 | ### Citrus.Avalonia 26 | 27 | [Avalonia themes.](https://github.com/worldbeater/Citrus.Avalonia) 28 | 29 | ### linea 30 | 31 | The project logo is from [linea.io](http://linea.io). 32 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 0.3.0-build{build} 2 | image: Visual Studio 2019 3 | 4 | shallow_clone: true 5 | clone_folder: c:\dotnet-properties 6 | 7 | configuration: 8 | - Debug 9 | - Release 10 | platform: Any CPU 11 | 12 | matrix: 13 | fast_finish: true 14 | 15 | nuget: 16 | account_feed: false 17 | project_feed: true 18 | disable_publish_on_pr: true 19 | 20 | build_script: 21 | - cmd: dotnet build dotnet-properties.sln 22 | 23 | test: off 24 | 25 | artifacts: 26 | - path: 'artifacts\Debug\nupkg\*.nupkg' 27 | name: DebugNupkg 28 | - path: 'artifacts\Release\nupkg\*.nupkg' 29 | name: ReleaseNupkg 30 | 31 | deploy: 32 | #- provider: GitHub 33 | # auth_token: 34 | # secure: +kq8LAyp9cUJjEdoBhOCsldBLElEjcl8M+yF4Ml4bMNqaJLqvwG7nqODNBAn+kxR 35 | # artifact: ReleaseNupkg 36 | # on: 37 | # branch: master 38 | # configuration: Release 39 | # appveyor_repo_tag: true 40 | - provider: NuGet 41 | api_key: 42 | secure: 5yR2VWgBBYQvQu+F6p367NXT2SYI+vzy29YEI+Rs+T9Zq2www5kMgMV/vSiQQfgP 43 | artifact: ReleaseNupkg 44 | on: 45 | branch: master 46 | configuration: Release 47 | appveyor_repo_tag: true 48 | - provider: NuGet 49 | server: https://www.myget.org/F/jp2masa/api/v2/package 50 | artifact: DebugNupkg 51 | api_key: 52 | secure: puOcEbngEmaVMEnUL20u4mzATgvoyaTPRWGGwE98as1+8KGY3ypOKzt5OV63duwI 53 | on: 54 | branch: master 55 | configuration: Debug 56 | -------------------------------------------------------------------------------- /build/Targets/AvaloniaDiagnostics.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | AVALONIA_DIAGNOSTICS;$(DefineConstants) 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /build/Targets/Import.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /build/Targets/Import.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /build/Targets/PackageProperties.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(PackageVersion)$(PackageVersionSuffix) 5 | 6 | 7 | 8 | dotnet-properties is a .NET Core global tool which allows to edit project properties using a cross-platform UI. 9 | 10 | 11 | 12 | jp2masa 13 | Copyright © $([System.DateTime]::Now.Year) jp2masa 14 | Icon.png 15 | http://github.com/jp2masa/dotnet-properties 16 | MIT 17 | True 18 | dotnet properties core csharp vb visual basic fsharp csproj vbproj fsproj proj 19 | git 20 | https://github.com/jp2masa/dotnet-properties 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /build/Targets/PackageProperties.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(PackageDescription) 5 | 6 | $(BaseDescription) 7 | $(BaseDescription) 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /build/Targets/PackageVersions.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 0.10.0 5 | 1.68.0.2 6 | 1.4.3 7 | 16.9.0 8 | 5.0.3 9 | 5.9.0 10 | 13.2.2 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /build/Targets/RepoLayout.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | $(Platform) 7 | 8 | 9 | 10 | $(RepoRoot)artifacts\ 11 | $(ArtifactsDir)$(Configuration)\nupkg\ 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /dotnet-properties.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.28407.52 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dotnet-properties", "src\dotnet-properties\dotnet-properties.csproj", "{4B0E3D01-F0C9-42EF-9556-32C96F59E353}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{89EF6D8C-C8F7-4374-B424-C88013F4791C}" 9 | ProjectSection(SolutionItems) = preProject 10 | .editorconfig = .editorconfig 11 | Directory.Build.props = Directory.Build.props 12 | Directory.Build.targets = Directory.Build.targets 13 | NuGet.config = NuGet.config 14 | EndProjectSection 15 | EndProject 16 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{233FDDCD-5706-4E83-AF15-41A0CA9F0E42}" 17 | EndProject 18 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Targets", "Targets", "{9C9397C0-677A-481E-9A3B-5C0152ECF6E1}" 19 | ProjectSection(SolutionItems) = preProject 20 | build\Targets\AvaloniaDiagnostics.props = build\Targets\AvaloniaDiagnostics.props 21 | build\Targets\Import.props = build\Targets\Import.props 22 | build\Targets\Import.targets = build\Targets\Import.targets 23 | build\Targets\PackageProperties.props = build\Targets\PackageProperties.props 24 | build\Targets\PackageProperties.targets = build\Targets\PackageProperties.targets 25 | build\Targets\PackageVersions.targets = build\Targets\PackageVersions.targets 26 | build\Targets\RepoLayout.props = build\Targets\RepoLayout.props 27 | EndProjectSection 28 | EndProject 29 | Global 30 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 31 | Debug|Any CPU = Debug|Any CPU 32 | Release|Any CPU = Release|Any CPU 33 | EndGlobalSection 34 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 35 | {4B0E3D01-F0C9-42EF-9556-32C96F59E353}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 36 | {4B0E3D01-F0C9-42EF-9556-32C96F59E353}.Debug|Any CPU.Build.0 = Debug|Any CPU 37 | {4B0E3D01-F0C9-42EF-9556-32C96F59E353}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {4B0E3D01-F0C9-42EF-9556-32C96F59E353}.Release|Any CPU.Build.0 = Release|Any CPU 39 | EndGlobalSection 40 | GlobalSection(SolutionProperties) = preSolution 41 | HideSolutionNode = FALSE 42 | EndGlobalSection 43 | GlobalSection(NestedProjects) = preSolution 44 | {233FDDCD-5706-4E83-AF15-41A0CA9F0E42} = {89EF6D8C-C8F7-4374-B424-C88013F4791C} 45 | {9C9397C0-677A-481E-9A3B-5C0152ECF6E1} = {233FDDCD-5706-4E83-AF15-41A0CA9F0E42} 46 | EndGlobalSection 47 | GlobalSection(ExtensibilityGlobals) = postSolution 48 | SolutionGuid = {32FBE198-F721-4B81-B08E-A834940EC363} 49 | EndGlobalSection 50 | EndGlobal 51 | -------------------------------------------------------------------------------- /install.bat: -------------------------------------------------------------------------------- 1 | dotnet tool uninstall --global dotnet-properties 2 | 3 | dotnet build dotnet-properties.sln -c Release 4 | dotnet tool install --global --add-source artifacts\Release\nupkg\ --version 0.3.0-* dotnet-properties 5 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | dotnet tool uninstall --global dotnet-properties 4 | 5 | dotnet build dotnet-properties.sln -c Release 6 | dotnet tool install --global --add-source artifacts/Release/nupkg/ --version 0.3.0-* dotnet-properties 7 | -------------------------------------------------------------------------------- /src/dotnet-properties/App.xaml: -------------------------------------------------------------------------------- 1 |  4 | -------------------------------------------------------------------------------- /src/dotnet-properties/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.IO; 5 | using System.Runtime.Loader; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | using Avalonia; 10 | using Avalonia.Controls; 11 | using Avalonia.Controls.ApplicationLifetimes; 12 | using Avalonia.Controls.Platform; 13 | using Avalonia.Markup.Xaml; 14 | using Avalonia.ReactiveUI; 15 | using Avalonia.Threading; 16 | 17 | using DotNet.Properties.Dialogs.ViewModels; 18 | using DotNet.Properties.Dialogs.Views; 19 | using DotNet.Properties.Services; 20 | using DotNet.Properties.ViewModels; 21 | using DotNet.Properties.Views; 22 | 23 | namespace DotNet.Properties 24 | { 25 | internal sealed class App : Application 26 | { 27 | private static readonly List OpenProjectFileDialogFilters = new List() 28 | { 29 | new FileDialogFilter() { Name = "All Project Files", Extensions = new List() { "*proj" } }, 30 | new FileDialogFilter() { Name = "C# Project Files", Extensions = new List() { "csproj" } }, 31 | new FileDialogFilter() { Name = "Visual Basic Project Files", Extensions = new List() { "vbproj" } }, 32 | new FileDialogFilter() { Name = "F# Project Files", Extensions = new List() { "fsproj" } }, 33 | }; 34 | 35 | public override void Initialize() => AvaloniaXamlLoader.Load(this); 36 | 37 | public override void OnFrameworkInitializationCompleted() 38 | { 39 | if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) 40 | { 41 | var mainWindow = new MainWindow(); 42 | var mainWindowViewModel = BuildMainWindowDataContext(mainWindow); 43 | 44 | if (mainWindowViewModel != null) 45 | { 46 | mainWindow.DataContext = mainWindowViewModel; 47 | desktop.MainWindow = mainWindow; 48 | } 49 | } 50 | 51 | base.OnFrameworkInitializationCompleted(); 52 | } 53 | 54 | private static int Main(string[] args) => 55 | BuildAvaloniaApp().StartWithClassicDesktopLifetime(args, ShutdownMode.OnMainWindowClose); 56 | 57 | private MainWindowViewModel? BuildMainWindowDataContext(MainWindow mainWindow) 58 | { 59 | var projFiles = Directory.GetFiles(Environment.CurrentDirectory, "*.*proj"); 60 | 61 | var projectPath = projFiles.Length == 1 ? projFiles[0] : OpenProjectFile(); 62 | var projectDirectory = Path.GetDirectoryName(projectPath); 63 | 64 | // TODO: replace with File.Exists and Directory.Exists when nullable annotations are correct 65 | if (!FileExists(projectPath) || !DirectoryExists(projectDirectory)) 66 | { 67 | return null; 68 | } 69 | 70 | IDotNetSdkResolver dotnetSdkResolver = new DotNetSdkResolver(); 71 | 72 | if (!dotnetSdkResolver.TryResolveSdkPath(projectDirectory, out var dotnetSdkPath)) 73 | { 74 | return null; 75 | } 76 | 77 | var dotnetSdkPaths = new DotNetSdkPaths(dotnetSdkPath); 78 | 79 | IMSBuildLoader msBuildLoader = new MSBuildLoader(dotnetSdkPaths); 80 | 81 | AssemblyLoadContext.Default.Resolving += (context, name) => 82 | { 83 | if (name.Name != null && msBuildLoader.TryResolveMSBuildAssembly(context, name.Name, out var assembly)) 84 | { 85 | return assembly; 86 | } 87 | 88 | return null; 89 | }; 90 | 91 | var msBuildProject = new MSBuildProject(dotnetSdkPaths, projectPath); 92 | 93 | return new MainWindowViewModel( 94 | msBuildProject, 95 | new DialogService(NewUnsavedChangesDialog, mainWindow), 96 | new OpenFileDialogService(mainWindow), 97 | new ThemeService(this)); 98 | } 99 | 100 | private static string? OpenProjectFile() 101 | { 102 | Task task; 103 | 104 | using (var source = new CancellationTokenSource()) 105 | { 106 | task = OpenProjectFileAsync(); 107 | task.ContinueWith(t => source.Cancel(), TaskScheduler.FromCurrentSynchronizationContext()); 108 | 109 | Dispatcher.UIThread.MainLoop(source.Token); 110 | } 111 | 112 | return task.Result; 113 | } 114 | 115 | private static async Task OpenProjectFileAsync() 116 | { 117 | var openFileDialog = new OpenFileDialog 118 | { 119 | AllowMultiple = false, 120 | Filters = OpenProjectFileDialogFilters 121 | }; 122 | 123 | var result = await ShowOpenFileDialogAsync(openFileDialog).ConfigureAwait(false); 124 | 125 | if (result != null && result.Length > 0) 126 | { 127 | return result[0]; 128 | } 129 | 130 | return null; 131 | } 132 | 133 | private static AppBuilder BuildAvaloniaApp() => 134 | AppBuilder.Configure() 135 | .UsePlatformDetect() 136 | #if DEBUG 137 | .LogToTrace() 138 | #endif 139 | .UseReactiveUI(); 140 | 141 | private static UnsavedChangesDialog NewUnsavedChangesDialog() => new UnsavedChangesDialog(); 142 | 143 | private static bool DirectoryExists([NotNullWhen(true)] string? path) => Directory.Exists(path); 144 | 145 | private static bool FileExists([NotNullWhen(true)] string? path) => File.Exists(path); 146 | 147 | private static Task ShowOpenFileDialogAsync(FileDialog dialog, Window? parent = null) 148 | { 149 | var systemDialogImpl = AvaloniaLocator.Current.GetService(); 150 | return systemDialogImpl.ShowFileDialogAsync(dialog, parent); 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/dotnet-properties/DataAnnotations/NuGetPackageLicenseExpressionAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | using NuGet.Packaging.Licenses; 5 | 6 | namespace DotNet.Properties.DataAnnotations 7 | { 8 | internal sealed class NuGetPackageLicenseExpressionAttribute : ValidationAttribute 9 | { 10 | protected override ValidationResult? IsValid(object? value, ValidationContext validationContext) 11 | { 12 | if (!(value is string str)) 13 | { 14 | throw new InvalidOperationException(); 15 | } 16 | 17 | try 18 | { 19 | var expression = NuGetLicenseExpression.Parse(str); 20 | 21 | if (expression.HasOnlyStandardIdentifiers()) 22 | { 23 | return ValidationResult.Success; 24 | } 25 | 26 | return new ValidationResult("Unknown license identifier(s)!"); 27 | } 28 | catch (NuGetLicenseExpressionParsingException e) 29 | { 30 | return new ValidationResult(e.Message); 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/dotnet-properties/Dialogs/Models/UnsavedChangesDialogResult.cs: -------------------------------------------------------------------------------- 1 | namespace DotNet.Properties.Dialogs.Models 2 | { 3 | internal enum UnsavedChangesDialogResult 4 | { 5 | Yes, 6 | No, 7 | Cancel 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/dotnet-properties/Dialogs/ViewModels/UnsavedChangesDialogViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Input; 2 | 3 | using Avalonia.Controls; 4 | 5 | using ReactiveUI; 6 | 7 | using DotNet.Properties.Dialogs.Models; 8 | 9 | namespace DotNet.Properties.Dialogs.ViewModels 10 | { 11 | internal sealed class UnsavedChangesDialogViewModel : ReactiveObject 12 | { 13 | private UnsavedChangesDialogResult _dialogResult = UnsavedChangesDialogResult.Cancel; 14 | 15 | public UnsavedChangesDialogViewModel() 16 | { 17 | YesCommand = ReactiveCommand.Create(window => Close(UnsavedChangesDialogResult.Yes, window)); 18 | NoCommand = ReactiveCommand.Create(window => Close(UnsavedChangesDialogResult.No, window)); 19 | CancelCommand = ReactiveCommand.Create(window => Close(UnsavedChangesDialogResult.Cancel, window)); 20 | } 21 | 22 | public ICommand YesCommand { get; } 23 | 24 | public ICommand NoCommand { get; } 25 | 26 | public ICommand CancelCommand { get; } 27 | 28 | public UnsavedChangesDialogResult DialogResult 29 | { 30 | get => _dialogResult; 31 | set => this.RaiseAndSetIfChanged(ref _dialogResult, value); 32 | } 33 | 34 | private void Close(UnsavedChangesDialogResult dialogResult, Window window) 35 | { 36 | DialogResult = dialogResult; 37 | window.Close(); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/dotnet-properties/Dialogs/Views/UnsavedChangesDialog.xaml: -------------------------------------------------------------------------------- 1 |  11 | 12 | 14 | 15 | 16 | 17 | 20 | 21 |