From 777bfa1358e50d455d26eb0bdd08f83292a19d6e Mon Sep 17 00:00:00 2001 From: Anton Volkov Date: Tue, 26 Mar 2019 18:22:29 +0300 Subject: [PATCH] Add pages and some infrastructure --- src/JiraTimeBot.Ui/App.config | 14 + src/JiraTimeBot.Ui/App.xaml | 8 + src/JiraTimeBot.Ui/App.xaml.cs | 56 + src/JiraTimeBot.Ui/ApplicationNavigator.cs | 38 + src/JiraTimeBot.Ui/Commands/RelayCommand'.cs | 77 ++ src/JiraTimeBot.Ui/Commands/RelayCommand.cs | 55 + .../Commands/RelayCommandAsync'.cs | 24 + src/JiraTimeBot.Ui/IApplicationNavigator.cs | 8 + src/JiraTimeBot.Ui/IRegisteredPages.cs | 9 + src/JiraTimeBot.Ui/JiraTimeBot.Ui.csproj | 171 +++ src/JiraTimeBot.Ui/Models/ApplicationModel.cs | 7 + src/JiraTimeBot.Ui/Properties/Annotations.cs | 1065 +++++++++++++++++ src/JiraTimeBot.Ui/Properties/AssemblyInfo.cs | 55 + .../Properties/Resources.Designer.cs | 71 ++ src/JiraTimeBot.Ui/Properties/Resources.resx | 117 ++ .../Properties/Settings.Designer.cs | 30 + .../Properties/Settings.settings | 7 + src/JiraTimeBot.Ui/Splash.png | Bin 0 -> 15509 bytes .../ViewModels/MainViewModel.cs | 22 + .../ViewModels/SettingsViewModel.cs | 7 + .../ViewModels/ViewModelBase.cs | 17 + src/JiraTimeBot.Ui/Views/MainPage.xaml | 21 + src/JiraTimeBot.Ui/Views/MainPage.xaml.cs | 28 + src/JiraTimeBot.Ui/Views/MainWindow.xaml | 11 + src/JiraTimeBot.Ui/Views/MainWindow.xaml.cs | 13 + src/JiraTimeBot.Ui/Views/Settings.xaml | 12 + src/JiraTimeBot.Ui/Views/Settings.xaml.cs | 28 + src/JiraTimeBot.Ui/Views/SettingsPage.xaml | 14 + src/JiraTimeBot.Ui/Views/SettingsPage.xaml.cs | 15 + src/JiraTimeBot.Ui/packages.config | 6 + src/JiraTimeBot.sln | 6 + src/JiraTimeBot.sln.DotSettings | 3 + 32 files changed, 2015 insertions(+) create mode 100644 src/JiraTimeBot.Ui/App.config create mode 100644 src/JiraTimeBot.Ui/App.xaml create mode 100644 src/JiraTimeBot.Ui/App.xaml.cs create mode 100644 src/JiraTimeBot.Ui/ApplicationNavigator.cs create mode 100644 src/JiraTimeBot.Ui/Commands/RelayCommand'.cs create mode 100644 src/JiraTimeBot.Ui/Commands/RelayCommand.cs create mode 100644 src/JiraTimeBot.Ui/Commands/RelayCommandAsync'.cs create mode 100644 src/JiraTimeBot.Ui/IApplicationNavigator.cs create mode 100644 src/JiraTimeBot.Ui/IRegisteredPages.cs create mode 100644 src/JiraTimeBot.Ui/JiraTimeBot.Ui.csproj create mode 100644 src/JiraTimeBot.Ui/Models/ApplicationModel.cs create mode 100644 src/JiraTimeBot.Ui/Properties/Annotations.cs create mode 100644 src/JiraTimeBot.Ui/Properties/AssemblyInfo.cs create mode 100644 src/JiraTimeBot.Ui/Properties/Resources.Designer.cs create mode 100644 src/JiraTimeBot.Ui/Properties/Resources.resx create mode 100644 src/JiraTimeBot.Ui/Properties/Settings.Designer.cs create mode 100644 src/JiraTimeBot.Ui/Properties/Settings.settings create mode 100644 src/JiraTimeBot.Ui/Splash.png create mode 100644 src/JiraTimeBot.Ui/ViewModels/MainViewModel.cs create mode 100644 src/JiraTimeBot.Ui/ViewModels/SettingsViewModel.cs create mode 100644 src/JiraTimeBot.Ui/ViewModels/ViewModelBase.cs create mode 100644 src/JiraTimeBot.Ui/Views/MainPage.xaml create mode 100644 src/JiraTimeBot.Ui/Views/MainPage.xaml.cs create mode 100644 src/JiraTimeBot.Ui/Views/MainWindow.xaml create mode 100644 src/JiraTimeBot.Ui/Views/MainWindow.xaml.cs create mode 100644 src/JiraTimeBot.Ui/Views/Settings.xaml create mode 100644 src/JiraTimeBot.Ui/Views/Settings.xaml.cs create mode 100644 src/JiraTimeBot.Ui/Views/SettingsPage.xaml create mode 100644 src/JiraTimeBot.Ui/Views/SettingsPage.xaml.cs create mode 100644 src/JiraTimeBot.Ui/packages.config create mode 100644 src/JiraTimeBot.sln.DotSettings diff --git a/src/JiraTimeBot.Ui/App.config b/src/JiraTimeBot.Ui/App.config new file mode 100644 index 0000000..9039726 --- /dev/null +++ b/src/JiraTimeBot.Ui/App.config @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/JiraTimeBot.Ui/App.xaml b/src/JiraTimeBot.Ui/App.xaml new file mode 100644 index 0000000..ccba635 --- /dev/null +++ b/src/JiraTimeBot.Ui/App.xaml @@ -0,0 +1,8 @@ + + + + + diff --git a/src/JiraTimeBot.Ui/App.xaml.cs b/src/JiraTimeBot.Ui/App.xaml.cs new file mode 100644 index 0000000..1eb0115 --- /dev/null +++ b/src/JiraTimeBot.Ui/App.xaml.cs @@ -0,0 +1,56 @@ +using Autofac; +using Autofac.Extras.AggregateService; +using JiraTimeBot.Ui.Models; +using JiraTimeBot.Ui.ViewModels; +using System.Windows; + +namespace JiraTimeBot.Ui +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + private IContainer _container; + + public App() + { + } + + protected override void OnStartup(StartupEventArgs e) + { + var builder = new ContainerBuilder(); + builder.Register(c => new MainWindow()) + .SingleInstance(); + + builder.Register(c => new ApplicationModel()).SingleInstance(); + builder.Register(c => new SettingsViewModel()).SingleInstance(); + + builder.RegisterType().SingleInstance(); + builder.RegisterType().SingleInstance(); + + builder.RegisterType().SingleInstance(); + builder.RegisterType().SingleInstance(); + + + builder.RegisterAggregateService(); + builder.RegisterAggregateService(); + + builder.RegisterType() + .As() + .SingleInstance(); + + _container = builder.Build(); + + var mainWindow = _container.Resolve(); + + var nav = _container.Resolve(); + nav.NavigateToMain(); + + //var mainPage = _container.Resolve(); + //mainWindow.Navigate(mainPage); + mainWindow.ShowDialog(); + base.OnStartup(e); + } + } +} diff --git a/src/JiraTimeBot.Ui/ApplicationNavigator.cs b/src/JiraTimeBot.Ui/ApplicationNavigator.cs new file mode 100644 index 0000000..2cdf01a --- /dev/null +++ b/src/JiraTimeBot.Ui/ApplicationNavigator.cs @@ -0,0 +1,38 @@ +using JiraTimeBot.Ui.ViewModels; + +namespace JiraTimeBot.Ui +{ + public interface IRegisteredViewModels + { + MainViewModel Main { get; set; } + SettingsViewModel Settings { get; set; } + } + + public class ApplicationNavigator : IApplicationNavigator + { + private readonly MainWindow _mainWindow; + private readonly IRegisteredPages _pages; + private readonly IRegisteredViewModels _viewModels; + + public ApplicationNavigator(MainWindow mainWindow, + IRegisteredPages pages, + IRegisteredViewModels viewModels) + { + _mainWindow = mainWindow; + _pages = pages; + _viewModels = viewModels; + } + + public void NavigateToMain() + { + _pages.Main.DataContext = _viewModels.Main; + _mainWindow.Navigate(_pages.Main); + } + + public void NavigateToSettings() + { + _pages.Settings.DataContext = _viewModels.Settings; + _mainWindow.Navigate(_pages.Settings); + } + } +} \ No newline at end of file diff --git a/src/JiraTimeBot.Ui/Commands/RelayCommand'.cs b/src/JiraTimeBot.Ui/Commands/RelayCommand'.cs new file mode 100644 index 0000000..5286cda --- /dev/null +++ b/src/JiraTimeBot.Ui/Commands/RelayCommand'.cs @@ -0,0 +1,77 @@ +using System; +using System.Windows.Input; + +namespace Aeroclub.CL.ASIA.Application.UI.Wpf.Commands +{ + public sealed class RelayCommand : ICommand + { + private readonly Action _execute; + + private readonly Func _canExecute; + + public RelayCommand(Action execute, Func canExecute = null) + { + _execute = execute ?? throw new ArgumentNullException(nameof(execute)); + + if (canExecute != null) + { + _canExecute = canExecute; + } + } + + public event EventHandler CanExecuteChanged + { + add + { + if (_canExecute != null) + { + CommandManager.RequerySuggested += value; + } + } + + remove + { + if (_canExecute != null) + { + CommandManager.RequerySuggested -= value; + } + } + } + + public bool CanExecute(object parameter) + { + if (_canExecute == null) + { + return true; + } + + switch (parameter) + { + case null: + return _canExecute(default(T)); + case T variable: + return _canExecute(variable); + } + + return false; + } + + public void Execute(object parameter) + { + object val = parameter; + + if (parameter != null && parameter.GetType() != typeof(T)) + { + if (parameter is IConvertible) + { + val = Convert.ChangeType(parameter, typeof(T), null); + } + } + + if (CanExecute(val)) + { + _execute?.Invoke((T) val); + } + } + } +} \ No newline at end of file diff --git a/src/JiraTimeBot.Ui/Commands/RelayCommand.cs b/src/JiraTimeBot.Ui/Commands/RelayCommand.cs new file mode 100644 index 0000000..ac2e133 --- /dev/null +++ b/src/JiraTimeBot.Ui/Commands/RelayCommand.cs @@ -0,0 +1,55 @@ +using System; +using System.Windows.Input; + +namespace Aeroclub.CL.ASIA.Application.UI.Wpf.Commands +{ + public sealed class RelayCommand : ICommand + { + private readonly Action _execute; + + private readonly Func _canExecute; + + public RelayCommand(Action execute, Func canExecute = null) + { + _execute = execute ?? throw new ArgumentNullException(nameof(execute)); + _canExecute = canExecute; + } + + public event EventHandler CanExecuteChanged + { + add + { + if (_canExecute != null) + { + CommandManager.RequerySuggested += value; + } + } + + remove + { + if (_canExecute != null) + { + CommandManager.RequerySuggested -= value; + } + } + } + + public void RaiseCanExecuteChanged() + { + CommandManager.InvalidateRequerySuggested(); + } + + public bool CanExecute(object parameter) + { + return _canExecute == null || _canExecute(parameter); + } + + public void Execute(object parameter = default(object)) + { + if (CanExecute(parameter)) + { + _execute(); + } + } + } +} \ No newline at end of file diff --git a/src/JiraTimeBot.Ui/Commands/RelayCommandAsync'.cs b/src/JiraTimeBot.Ui/Commands/RelayCommandAsync'.cs new file mode 100644 index 0000000..c9767ea --- /dev/null +++ b/src/JiraTimeBot.Ui/Commands/RelayCommandAsync'.cs @@ -0,0 +1,24 @@ +using System; +using System.Threading.Tasks; +using System.Windows.Input; + +namespace Aeroclub.CL.ASIA.Application.UI.Wpf.Commands +{ + public class RelayCommandAsync : ICommand + { + private readonly Func _executedMethod; + private readonly Func _canExecuteMethod; + + public event EventHandler CanExecuteChanged; + + public RelayCommandAsync(Func execute, Func canExecute = null) + { + _executedMethod = execute; + _canExecuteMethod = canExecute; + } + + public bool CanExecute(object parameter) => _canExecuteMethod == null || _canExecuteMethod((T)parameter); + public async void Execute(object parameter) => await _executedMethod((T)parameter).ConfigureAwait(true); + public void OnCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty); + } +} \ No newline at end of file diff --git a/src/JiraTimeBot.Ui/IApplicationNavigator.cs b/src/JiraTimeBot.Ui/IApplicationNavigator.cs new file mode 100644 index 0000000..5210f0c --- /dev/null +++ b/src/JiraTimeBot.Ui/IApplicationNavigator.cs @@ -0,0 +1,8 @@ +namespace JiraTimeBot.Ui +{ + public interface IApplicationNavigator + { + void NavigateToMain(); + void NavigateToSettings(); + } +} \ No newline at end of file diff --git a/src/JiraTimeBot.Ui/IRegisteredPages.cs b/src/JiraTimeBot.Ui/IRegisteredPages.cs new file mode 100644 index 0000000..8d2d6cd --- /dev/null +++ b/src/JiraTimeBot.Ui/IRegisteredPages.cs @@ -0,0 +1,9 @@ +namespace JiraTimeBot.Ui +{ + public interface IRegisteredPages + { + SettingsPage Settings { get; set; } + + MainPage Main { get; set; } + } +} \ No newline at end of file diff --git a/src/JiraTimeBot.Ui/JiraTimeBot.Ui.csproj b/src/JiraTimeBot.Ui/JiraTimeBot.Ui.csproj new file mode 100644 index 0000000..0dc509f --- /dev/null +++ b/src/JiraTimeBot.Ui/JiraTimeBot.Ui.csproj @@ -0,0 +1,171 @@ + + + + + Debug + AnyCPU + {C3B7A833-B919-4299-A318-74B96544437E} + WinExe + JiraTimeBot.Ui + JiraTimeBot.Ui + v4.7.2 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + true + true + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Autofac.4.9.1\lib\net45\Autofac.dll + + + ..\packages\Autofac.Extras.AggregateService.4.1.0\lib\net45\Autofac.Extras.AggregateService.dll + + + ..\packages\Castle.Core.4.0.0\lib\net45\Castle.Core.dll + + + + + + + + + + + 4.0 + + + + + + + + MSBuild:Compile + Designer + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + App.xaml + Code + + + + + + + + + + + + + + MainPage.xaml + + + MainWindow.xaml + Code + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + + + Settings.xaml + + + SettingsPage.xaml + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + + + False + Microsoft .NET Framework 4.7.2 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 + false + + + + + + + + \ No newline at end of file diff --git a/src/JiraTimeBot.Ui/Models/ApplicationModel.cs b/src/JiraTimeBot.Ui/Models/ApplicationModel.cs new file mode 100644 index 0000000..771cd94 --- /dev/null +++ b/src/JiraTimeBot.Ui/Models/ApplicationModel.cs @@ -0,0 +1,7 @@ +namespace JiraTimeBot.Ui.Models +{ + public class ApplicationModel + { + + } +} \ No newline at end of file diff --git a/src/JiraTimeBot.Ui/Properties/Annotations.cs b/src/JiraTimeBot.Ui/Properties/Annotations.cs new file mode 100644 index 0000000..918fb32 --- /dev/null +++ b/src/JiraTimeBot.Ui/Properties/Annotations.cs @@ -0,0 +1,1065 @@ +/* MIT License + +Copyright (c) 2016 JetBrains http://www.jetbrains.com + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. */ + +using System; + +#pragma warning disable 1591 +// ReSharper disable UnusedMember.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable IntroduceOptionalParameters.Global +// ReSharper disable MemberCanBeProtected.Global +// ReSharper disable InconsistentNaming + +namespace JiraTimeBot.Ui.Annotations +{ + /// + /// Indicates that the value of the marked element could be null sometimes, + /// so the check for null is necessary before its usage. + /// + /// + /// [CanBeNull] object Test() => null; + /// + /// void UseTest() { + /// var p = Test(); + /// var s = p.ToString(); // Warning: Possible 'System.NullReferenceException' + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] + public sealed class CanBeNullAttribute : Attribute { } + + /// + /// Indicates that the value of the marked element could never be null. + /// + /// + /// [NotNull] object Foo() { + /// return null; // Warning: Possible 'null' assignment + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] + public sealed class NotNullAttribute : Attribute { } + + /// + /// Can be appplied to symbols of types derived from IEnumerable as well as to symbols of Task + /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property + /// or of the Lazy.Value property can never be null. + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field)] + public sealed class ItemNotNullAttribute : Attribute { } + + /// + /// Can be appplied to symbols of types derived from IEnumerable as well as to symbols of Task + /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property + /// or of the Lazy.Value property can be null. + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field)] + public sealed class ItemCanBeNullAttribute : Attribute { } + + /// + /// Indicates that the marked method builds string by format pattern and (optional) arguments. + /// Parameter, which contains format string, should be given in constructor. The format string + /// should be in -like form. + /// + /// + /// [StringFormatMethod("message")] + /// void ShowError(string message, params object[] args) { /* do something */ } + /// + /// void Foo() { + /// ShowError("Failed: {0}"); // Warning: Non-existing argument in format string + /// } + /// + [AttributeUsage( + AttributeTargets.Constructor | AttributeTargets.Method | + AttributeTargets.Property | AttributeTargets.Delegate)] + public sealed class StringFormatMethodAttribute : Attribute + { + /// + /// Specifies which parameter of an annotated method should be treated as format-string + /// + public StringFormatMethodAttribute([NotNull] string formatParameterName) + { + FormatParameterName = formatParameterName; + } + + [NotNull] public string FormatParameterName { get; private set; } + } + + /// + /// For a parameter that is expected to be one of the limited set of values. + /// Specify fields of which type should be used as values for this parameter. + /// + [AttributeUsage( + AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field, + AllowMultiple = true)] + public sealed class ValueProviderAttribute : Attribute + { + public ValueProviderAttribute([NotNull] string name) + { + Name = name; + } + + [NotNull] public string Name { get; private set; } + } + + /// + /// Indicates that the function argument should be string literal and match one + /// of the parameters of the caller function. For example, ReSharper annotates + /// the parameter of . + /// + /// + /// void Foo(string param) { + /// if (param == null) + /// throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol + /// } + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class InvokerParameterNameAttribute : Attribute { } + + /// + /// Indicates that the method is contained in a type that implements + /// System.ComponentModel.INotifyPropertyChanged interface and this method + /// is used to notify that some property value changed. + /// + /// + /// The method should be non-static and conform to one of the supported signatures: + /// + /// NotifyChanged(string) + /// NotifyChanged(params string[]) + /// NotifyChanged{T}(Expression{Func{T}}) + /// NotifyChanged{T,U}(Expression{Func{T,U}}) + /// SetProperty{T}(ref T, T, string) + /// + /// + /// + /// public class Foo : INotifyPropertyChanged { + /// public event PropertyChangedEventHandler PropertyChanged; + /// + /// [NotifyPropertyChangedInvocator] + /// protected virtual void NotifyChanged(string propertyName) { ... } + /// + /// string _name; + /// + /// public string Name { + /// get { return _name; } + /// set { _name = value; NotifyChanged("LastName"); /* Warning */ } + /// } + /// } + /// + /// Examples of generated notifications: + /// + /// NotifyChanged("Property") + /// NotifyChanged(() => Property) + /// NotifyChanged((VM x) => x.Property) + /// SetProperty(ref myField, value, "Property") + /// + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class NotifyPropertyChangedInvocatorAttribute : Attribute + { + public NotifyPropertyChangedInvocatorAttribute() { } + public NotifyPropertyChangedInvocatorAttribute([NotNull] string parameterName) + { + ParameterName = parameterName; + } + + [CanBeNull] public string ParameterName { get; private set; } + } + + /// + /// Describes dependency between method input and output. + /// + /// + ///

Function Definition Table syntax:

+ /// + /// FDT ::= FDTRow [;FDTRow]* + /// FDTRow ::= Input => Output | Output <= Input + /// Input ::= ParameterName: Value [, Input]* + /// Output ::= [ParameterName: Value]* {halt|stop|void|nothing|Value} + /// Value ::= true | false | null | notnull | canbenull + /// + /// If method has single input parameter, it's name could be omitted.
+ /// Using halt (or void/nothing, which is the same) for method output + /// means that the methos doesn't return normally (throws or terminates the process).
+ /// Value canbenull is only applicable for output parameters.
+ /// You can use multiple [ContractAnnotation] for each FDT row, or use single attribute + /// with rows separated by semicolon. There is no notion of order rows, all rows are checked + /// for applicability and applied per each program state tracked by R# analysis.
+ ///
+ /// + /// + /// [ContractAnnotation("=> halt")] + /// public void TerminationMethod() + /// + /// + /// [ContractAnnotation("halt <= condition: false")] + /// public void Assert(bool condition, string text) // regular assertion method + /// + /// + /// [ContractAnnotation("s:null => true")] + /// public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty() + /// + /// + /// // A method that returns null if the parameter is null, + /// // and not null if the parameter is not null + /// [ContractAnnotation("null => null; notnull => notnull")] + /// public object Transform(object data) + /// + /// + /// [ContractAnnotation("=> true, result: notnull; => false, result: null")] + /// public bool TryParse(string s, out Person result) + /// + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + public sealed class ContractAnnotationAttribute : Attribute + { + public ContractAnnotationAttribute([NotNull] string contract) + : this(contract, false) { } + + public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates) + { + Contract = contract; + ForceFullStates = forceFullStates; + } + + [NotNull] public string Contract { get; private set; } + + public bool ForceFullStates { get; private set; } + } + + /// + /// Indicates that marked element should be localized or not. + /// + /// + /// [LocalizationRequiredAttribute(true)] + /// class Foo { + /// string str = "my string"; // Warning: Localizable string + /// } + /// + [AttributeUsage(AttributeTargets.All)] + public sealed class LocalizationRequiredAttribute : Attribute + { + public LocalizationRequiredAttribute() : this(true) { } + + public LocalizationRequiredAttribute(bool required) + { + Required = required; + } + + public bool Required { get; private set; } + } + + /// + /// Indicates that the value of the marked type (or its derivatives) + /// cannot be compared using '==' or '!=' operators and Equals() + /// should be used instead. However, using '==' or '!=' for comparison + /// with null is always permitted. + /// + /// + /// [CannotApplyEqualityOperator] + /// class NoEquality { } + /// + /// class UsesNoEquality { + /// void Test() { + /// var ca1 = new NoEquality(); + /// var ca2 = new NoEquality(); + /// if (ca1 != null) { // OK + /// bool condition = ca1 == ca2; // Warning + /// } + /// } + /// } + /// + [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)] + public sealed class CannotApplyEqualityOperatorAttribute : Attribute { } + + /// + /// When applied to a target attribute, specifies a requirement for any type marked + /// with the target attribute to implement or inherit specific type or types. + /// + /// + /// [BaseTypeRequired(typeof(IComponent)] // Specify requirement + /// class ComponentAttribute : Attribute { } + /// + /// [Component] // ComponentAttribute requires implementing IComponent interface + /// class MyComponent : IComponent { } + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + [BaseTypeRequired(typeof(Attribute))] + public sealed class BaseTypeRequiredAttribute : Attribute + { + public BaseTypeRequiredAttribute([NotNull] Type baseType) + { + BaseType = baseType; + } + + [NotNull] public Type BaseType { get; private set; } + } + + /// + /// Indicates that the marked symbol is used implicitly (e.g. via reflection, in external library), + /// so this symbol will not be marked as unused (as well as by other usage inspections). + /// + [AttributeUsage(AttributeTargets.All)] + public sealed class UsedImplicitlyAttribute : Attribute + { + public UsedImplicitlyAttribute() + : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } + + public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags) + : this(useKindFlags, ImplicitUseTargetFlags.Default) { } + + public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags) + : this(ImplicitUseKindFlags.Default, targetFlags) { } + + public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) + { + UseKindFlags = useKindFlags; + TargetFlags = targetFlags; + } + + public ImplicitUseKindFlags UseKindFlags { get; private set; } + + public ImplicitUseTargetFlags TargetFlags { get; private set; } + } + + /// + /// Should be used on attributes and causes ReSharper to not mark symbols marked with such attributes + /// as unused (as well as by other usage inspections) + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.GenericParameter)] + public sealed class MeansImplicitUseAttribute : Attribute + { + public MeansImplicitUseAttribute() + : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } + + public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags) + : this(useKindFlags, ImplicitUseTargetFlags.Default) { } + + public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags) + : this(ImplicitUseKindFlags.Default, targetFlags) { } + + public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) + { + UseKindFlags = useKindFlags; + TargetFlags = targetFlags; + } + + [UsedImplicitly] public ImplicitUseKindFlags UseKindFlags { get; private set; } + + [UsedImplicitly] public ImplicitUseTargetFlags TargetFlags { get; private set; } + } + + [Flags] + public enum ImplicitUseKindFlags + { + Default = Access | Assign | InstantiatedWithFixedConstructorSignature, + /// Only entity marked with attribute considered used. + Access = 1, + /// Indicates implicit assignment to a member. + Assign = 2, + /// + /// Indicates implicit instantiation of a type with fixed constructor signature. + /// That means any unused constructor parameters won't be reported as such. + /// + InstantiatedWithFixedConstructorSignature = 4, + /// Indicates implicit instantiation of a type. + InstantiatedNoFixedConstructorSignature = 8, + } + + /// + /// Specify what is considered used implicitly when marked + /// with or . + /// + [Flags] + public enum ImplicitUseTargetFlags + { + Default = Itself, + Itself = 1, + /// Members of entity marked with attribute are considered used. + Members = 2, + /// Entity marked with attribute and all its members considered used. + WithMembers = Itself | Members + } + + /// + /// This attribute is intended to mark publicly available API + /// which should not be removed and so is treated as used. + /// + [MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)] + public sealed class PublicAPIAttribute : Attribute + { + public PublicAPIAttribute() { } + + public PublicAPIAttribute([NotNull] string comment) + { + Comment = comment; + } + + [CanBeNull] public string Comment { get; private set; } + } + + /// + /// Tells code analysis engine if the parameter is completely handled when the invoked method is on stack. + /// If the parameter is a delegate, indicates that delegate is executed while the method is executed. + /// If the parameter is an enumerable, indicates that it is enumerated while the method is executed. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class InstantHandleAttribute : Attribute { } + + /// + /// Indicates that a method does not make any observable state changes. + /// The same as System.Diagnostics.Contracts.PureAttribute. + /// + /// + /// [Pure] int Multiply(int x, int y) => x * y; + /// + /// void M() { + /// Multiply(123, 42); // Waring: Return value of pure method is not used + /// } + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class PureAttribute : Attribute { } + + /// + /// Indicates that the return value of method invocation must be used. + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class MustUseReturnValueAttribute : Attribute + { + public MustUseReturnValueAttribute() { } + + public MustUseReturnValueAttribute([NotNull] string justification) + { + Justification = justification; + } + + [CanBeNull] public string Justification { get; private set; } + } + + /// + /// Indicates the type member or parameter of some type, that should be used instead of all other ways + /// to get the value that type. This annotation is useful when you have some "context" value evaluated + /// and stored somewhere, meaning that all other ways to get this value must be consolidated with existing one. + /// + /// + /// class Foo { + /// [ProvidesContext] IBarService _barService = ...; + /// + /// void ProcessNode(INode node) { + /// DoSomething(node, node.GetGlobalServices().Bar); + /// // ^ Warning: use value of '_barService' field + /// } + /// } + /// + [AttributeUsage( + AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Method | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.GenericParameter)] + public sealed class ProvidesContextAttribute : Attribute { } + + /// + /// Indicates that a parameter is a path to a file or a folder within a web project. + /// Path can be relative or absolute, starting from web root (~). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class PathReferenceAttribute : Attribute + { + public PathReferenceAttribute() { } + + public PathReferenceAttribute([NotNull, PathReference] string basePath) + { + BasePath = basePath; + } + + [CanBeNull] public string BasePath { get; private set; } + } + + /// + /// An extension method marked with this attribute is processed by ReSharper code completion + /// as a 'Source Template'. When extension method is completed over some expression, it's source code + /// is automatically expanded like a template at call site. + /// + /// + /// Template method body can contain valid source code and/or special comments starting with '$'. + /// Text inside these comments is added as source code when the template is applied. Template parameters + /// can be used either as additional method parameters or as identifiers wrapped in two '$' signs. + /// Use the attribute to specify macros for parameters. + /// + /// + /// In this example, the 'forEach' method is a source template available over all values + /// of enumerable types, producing ordinary C# 'foreach' statement and placing caret inside block: + /// + /// [SourceTemplate] + /// public static void forEach<T>(this IEnumerable<T> xs) { + /// foreach (var x in xs) { + /// //$ $END$ + /// } + /// } + /// + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class SourceTemplateAttribute : Attribute { } + + /// + /// Allows specifying a macro for a parameter of a source template. + /// + /// + /// You can apply the attribute on the whole method or on any of its additional parameters. The macro expression + /// is defined in the property. When applied on a method, the target + /// template parameter is defined in the property. To apply the macro silently + /// for the parameter, set the property value = -1. + /// + /// + /// Applying the attribute on a source template method: + /// + /// [SourceTemplate, Macro(Target = "item", Expression = "suggestVariableName()")] + /// public static void forEach<T>(this IEnumerable<T> collection) { + /// foreach (var item in collection) { + /// //$ $END$ + /// } + /// } + /// + /// Applying the attribute on a template method parameter: + /// + /// [SourceTemplate] + /// public static void something(this Entity x, [Macro(Expression = "guid()", Editable = -1)] string newguid) { + /// /*$ var $x$Id = "$newguid$" + x.ToString(); + /// x.DoSomething($x$Id); */ + /// } + /// + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method, AllowMultiple = true)] + public sealed class MacroAttribute : Attribute + { + /// + /// Allows specifying a macro that will be executed for a source template + /// parameter when the template is expanded. + /// + [CanBeNull] public string Expression { get; set; } + + /// + /// Allows specifying which occurrence of the target parameter becomes editable when the template is deployed. + /// + /// + /// If the target parameter is used several times in the template, only one occurrence becomes editable; + /// other occurrences are changed synchronously. To specify the zero-based index of the editable occurrence, + /// use values >= 0. To make the parameter non-editable when the template is expanded, use -1. + /// > + public int Editable { get; set; } + + /// + /// Identifies the target parameter of a source template if the + /// is applied on a template method. + /// + [CanBeNull] public string Target { get; set; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcAreaMasterLocationFormatAttribute : Attribute + { + public AspMvcAreaMasterLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcAreaPartialViewLocationFormatAttribute : Attribute + { + public AspMvcAreaPartialViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcAreaViewLocationFormatAttribute : Attribute + { + public AspMvcAreaViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcMasterLocationFormatAttribute : Attribute + { + public AspMvcMasterLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcPartialViewLocationFormatAttribute : Attribute + { + public AspMvcPartialViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] + public sealed class AspMvcViewLocationFormatAttribute : Attribute + { + public AspMvcViewLocationFormatAttribute([NotNull] string format) + { + Format = format; + } + + [NotNull] public string Format { get; private set; } + } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC action. If applied to a method, the MVC action name is calculated + /// implicitly from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class AspMvcActionAttribute : Attribute + { + public AspMvcActionAttribute() { } + + public AspMvcActionAttribute([NotNull] string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + [CanBeNull] public string AnonymousProperty { get; private set; } + } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC area. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcAreaAttribute : Attribute + { + public AspMvcAreaAttribute() { } + + public AspMvcAreaAttribute([NotNull] string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + [CanBeNull] public string AnonymousProperty { get; private set; } + } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is + /// an MVC controller. If applied to a method, the MVC controller name is calculated + /// implicitly from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class AspMvcControllerAttribute : Attribute + { + public AspMvcControllerAttribute() { } + + public AspMvcControllerAttribute([NotNull] string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + [CanBeNull] public string AnonymousProperty { get; private set; } + } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC Master. Use this attribute + /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcMasterAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC model type. Use this attribute + /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, Object). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcModelTypeAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC + /// partial view. If applied to a method, the MVC partial view name is calculated implicitly + /// from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class AspMvcPartialViewAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Allows disabling inspections for MVC views within a class or a method. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public sealed class AspMvcSuppressViewErrorAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC display template. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.DisplayExtensions.DisplayForModel(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcDisplayTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC editor template. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.EditorExtensions.EditorForModel(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcEditorTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC template. + /// Use this attribute for custom wrappers similar to + /// System.ComponentModel.DataAnnotations.UIHintAttribute(System.String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC view component. If applied to a method, the MVC view name is calculated implicitly + /// from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Controller.View(Object). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class AspMvcViewAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC view component name. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcViewComponentAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC view component view. If applied to a method, the MVC view component view name is default. + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class AspMvcViewComponentViewAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. When applied to a parameter of an attribute, + /// indicates that this parameter is an MVC action name. + /// + /// + /// [ActionName("Foo")] + /// public ActionResult Login(string returnUrl) { + /// ViewBag.ReturnUrl = Url.Action("Foo"); // OK + /// return RedirectToAction("Bar"); // Error: Cannot resolve action + /// } + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)] + public sealed class AspMvcActionSelectorAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)] + public sealed class HtmlElementAttributesAttribute : Attribute + { + public HtmlElementAttributesAttribute() { } + + public HtmlElementAttributesAttribute([NotNull] string name) + { + Name = name; + } + + [CanBeNull] public string Name { get; private set; } + } + + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class HtmlAttributeValueAttribute : Attribute + { + public HtmlAttributeValueAttribute([NotNull] string name) + { + Name = name; + } + + [NotNull] public string Name { get; private set; } + } + + /// + /// Razor attribute. Indicates that a parameter or a method is a Razor section. + /// Use this attribute for custom wrappers similar to + /// System.Web.WebPages.WebPageBase.RenderSection(String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class RazorSectionAttribute : Attribute { } + + /// + /// Indicates how method, constructor invocation or property access + /// over collection type affects content of the collection. + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property)] + public sealed class CollectionAccessAttribute : Attribute + { + public CollectionAccessAttribute(CollectionAccessType collectionAccessType) + { + CollectionAccessType = collectionAccessType; + } + + public CollectionAccessType CollectionAccessType { get; private set; } + } + + [Flags] + public enum CollectionAccessType + { + /// Method does not use or modify content of the collection. + None = 0, + /// Method only reads content of the collection but does not modify it. + Read = 1, + /// Method can change content of the collection but does not add new elements. + ModifyExistingContent = 2, + /// Method can add new elements to the collection. + UpdatedContent = ModifyExistingContent | 4 + } + + /// + /// Indicates that the marked method is assertion method, i.e. it halts control flow if + /// one of the conditions is satisfied. To set the condition, mark one of the parameters with + /// attribute. + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class AssertionMethodAttribute : Attribute { } + + /// + /// Indicates the condition parameter of the assertion method. The method itself should be + /// marked by attribute. The mandatory argument of + /// the attribute is the assertion type. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AssertionConditionAttribute : Attribute + { + public AssertionConditionAttribute(AssertionConditionType conditionType) + { + ConditionType = conditionType; + } + + public AssertionConditionType ConditionType { get; private set; } + } + + /// + /// Specifies assertion type. If the assertion method argument satisfies the condition, + /// then the execution continues. Otherwise, execution is assumed to be halted. + /// + public enum AssertionConditionType + { + /// Marked parameter should be evaluated to true. + IS_TRUE = 0, + /// Marked parameter should be evaluated to false. + IS_FALSE = 1, + /// Marked parameter should be evaluated to null value. + IS_NULL = 2, + /// Marked parameter should be evaluated to not null value. + IS_NOT_NULL = 3, + } + + /// + /// Indicates that the marked method unconditionally terminates control flow execution. + /// For example, it could unconditionally throw exception. + /// + [Obsolete("Use [ContractAnnotation('=> halt')] instead")] + [AttributeUsage(AttributeTargets.Method)] + public sealed class TerminatesProgramAttribute : Attribute { } + + /// + /// Indicates that method is pure LINQ method, with postponed enumeration (like Enumerable.Select, + /// .Where). This annotation allows inference of [InstantHandle] annotation for parameters + /// of delegate type by analyzing LINQ method chains. + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class LinqTunnelAttribute : Attribute { } + + /// + /// Indicates that IEnumerable, passed as parameter, is not enumerated. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class NoEnumerationAttribute : Attribute { } + + /// + /// Indicates that parameter is regular expression pattern. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class RegexPatternAttribute : Attribute { } + + /// + /// Prevents the Member Reordering feature from tossing members of the marked class. + /// + /// + /// The attribute must be mentioned in your member reordering patterns + /// + [AttributeUsage( + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.Enum)] + public sealed class NoReorderAttribute : Attribute { } + + /// + /// XAML attribute. Indicates the type that has ItemsSource property and should be treated + /// as ItemsControl-derived type, to enable inner items DataContext type resolve. + /// + [AttributeUsage(AttributeTargets.Class)] + public sealed class XamlItemsControlAttribute : Attribute { } + + /// + /// XAML attribute. Indicates the property of some BindingBase-derived type, that + /// is used to bind some item of ItemsControl-derived type. This annotation will + /// enable the DataContext type resolve for XAML bindings for such properties. + /// + /// + /// Property should have the tree ancestor of the ItemsControl type or + /// marked with the attribute. + /// + [AttributeUsage(AttributeTargets.Property)] + public sealed class XamlItemBindingOfItemsControlAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class AspChildControlTypeAttribute : Attribute + { + public AspChildControlTypeAttribute([NotNull] string tagName, [NotNull] Type controlType) + { + TagName = tagName; + ControlType = controlType; + } + + [NotNull] public string TagName { get; private set; } + + [NotNull] public Type ControlType { get; private set; } + } + + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] + public sealed class AspDataFieldAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] + public sealed class AspDataFieldsAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property)] + public sealed class AspMethodPropertyAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class AspRequiredAttributeAttribute : Attribute + { + public AspRequiredAttributeAttribute([NotNull] string attribute) + { + Attribute = attribute; + } + + [NotNull] public string Attribute { get; private set; } + } + + [AttributeUsage(AttributeTargets.Property)] + public sealed class AspTypePropertyAttribute : Attribute + { + public bool CreateConstructorReferences { get; private set; } + + public AspTypePropertyAttribute(bool createConstructorReferences) + { + CreateConstructorReferences = createConstructorReferences; + } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RazorImportNamespaceAttribute : Attribute + { + public RazorImportNamespaceAttribute([NotNull] string name) + { + Name = name; + } + + [NotNull] public string Name { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RazorInjectionAttribute : Attribute + { + public RazorInjectionAttribute([NotNull] string type, [NotNull] string fieldName) + { + Type = type; + FieldName = fieldName; + } + + [NotNull] public string Type { get; private set; } + + [NotNull] public string FieldName { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RazorDirectiveAttribute : Attribute + { + public RazorDirectiveAttribute([NotNull] string directive) + { + Directive = directive; + } + + [NotNull] public string Directive { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RazorPageBaseTypeAttribute : Attribute + { + public RazorPageBaseTypeAttribute([NotNull] string baseType) + { + BaseType = baseType; + } + public RazorPageBaseTypeAttribute([NotNull] string baseType, string pageName) + { + BaseType = baseType; + PageName = pageName; + } + + [NotNull] public string BaseType { get; private set; } + [CanBeNull] public string PageName { get; private set; } + } + + [AttributeUsage(AttributeTargets.Method)] + public sealed class RazorHelperCommonAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property)] + public sealed class RazorLayoutAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Method)] + public sealed class RazorWriteLiteralMethodAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Method)] + public sealed class RazorWriteMethodAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class RazorWriteMethodParameterAttribute : Attribute { } +} \ No newline at end of file diff --git a/src/JiraTimeBot.Ui/Properties/AssemblyInfo.cs b/src/JiraTimeBot.Ui/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..58b4b59 --- /dev/null +++ b/src/JiraTimeBot.Ui/Properties/AssemblyInfo.cs @@ -0,0 +1,55 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("JiraTimeBot.Ui")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("JiraTimeBot.Ui")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +//In order to begin building localizable applications, set +//CultureYouAreCodingWith in your .csproj file +//inside a . For example, if you are using US english +//in your source files, set the to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/JiraTimeBot.Ui/Properties/Resources.Designer.cs b/src/JiraTimeBot.Ui/Properties/Resources.Designer.cs new file mode 100644 index 0000000..311da75 --- /dev/null +++ b/src/JiraTimeBot.Ui/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JiraTimeBot.Ui.Properties +{ + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("JiraTimeBot.Ui.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/src/JiraTimeBot.Ui/Properties/Resources.resx b/src/JiraTimeBot.Ui/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/src/JiraTimeBot.Ui/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/JiraTimeBot.Ui/Properties/Settings.Designer.cs b/src/JiraTimeBot.Ui/Properties/Settings.Designer.cs new file mode 100644 index 0000000..a0b78e7 --- /dev/null +++ b/src/JiraTimeBot.Ui/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace JiraTimeBot.Ui.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/src/JiraTimeBot.Ui/Properties/Settings.settings b/src/JiraTimeBot.Ui/Properties/Settings.settings new file mode 100644 index 0000000..033d7a5 --- /dev/null +++ b/src/JiraTimeBot.Ui/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/JiraTimeBot.Ui/Splash.png b/src/JiraTimeBot.Ui/Splash.png new file mode 100644 index 0000000000000000000000000000000000000000..edd71bb811730bde0d1fbe46143245fc5913a75b GIT binary patch literal 15509 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 n3dqOCKQz _appNavigator; + + public MainViewModel(Func appNavigator) + { + _appNavigator = appNavigator; + + NavigateToSettings = new RelayCommand(() => + { + _appNavigator().NavigateToSettings(); + }); + } + + public RelayCommand NavigateToSettings { get; set; } + } +} \ No newline at end of file diff --git a/src/JiraTimeBot.Ui/ViewModels/SettingsViewModel.cs b/src/JiraTimeBot.Ui/ViewModels/SettingsViewModel.cs new file mode 100644 index 0000000..5ed52bc --- /dev/null +++ b/src/JiraTimeBot.Ui/ViewModels/SettingsViewModel.cs @@ -0,0 +1,7 @@ +namespace JiraTimeBot.Ui.ViewModels +{ + public class SettingsViewModel : ViewModelBase + { + + } +} \ No newline at end of file diff --git a/src/JiraTimeBot.Ui/ViewModels/ViewModelBase.cs b/src/JiraTimeBot.Ui/ViewModels/ViewModelBase.cs new file mode 100644 index 0000000..03dd1c5 --- /dev/null +++ b/src/JiraTimeBot.Ui/ViewModels/ViewModelBase.cs @@ -0,0 +1,17 @@ +using JiraTimeBot.Ui.Annotations; +using System.ComponentModel; +using System.Runtime.CompilerServices; + +namespace JiraTimeBot.Ui.ViewModels +{ + public class ViewModelBase : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + [NotifyPropertyChangedInvocator] + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } +} \ No newline at end of file diff --git a/src/JiraTimeBot.Ui/Views/MainPage.xaml b/src/JiraTimeBot.Ui/Views/MainPage.xaml new file mode 100644 index 0000000..13636d2 --- /dev/null +++ b/src/JiraTimeBot.Ui/Views/MainPage.xaml @@ -0,0 +1,21 @@ + + + + + + + + +