├── README.md ├── MVVM Example ├── packages.config ├── App.xaml.cs ├── App.config ├── Model │ └── Phone.cs ├── Services │ ├── IDialogService.cs │ ├── IFileService.cs │ ├── DefaultDialogService.cs │ ├── JsonFileService.cs │ └── Commands │ │ └── RelayCommand.cs ├── App.xaml ├── View │ ├── MainWindow.xaml.cs │ └── MainWindow.xaml ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ └── Annotations.cs ├── MVVM Example.csproj └── ViewModel │ └── ApplicationViewModel.cs ├── .idea └── .idea.MVVM Example │ ├── .idea │ ├── misc.xml │ ├── vcs.xml │ ├── indexLayout.xml │ └── .gitignore │ └── riderModule.iml ├── MVVM Example.sln.DotSettings └── MVVM Example.sln /README.md: -------------------------------------------------------------------------------- 1 | # WPF MVVM Example 2 | Example of MVVM pattern usage in WPF and C# 3 | -------------------------------------------------------------------------------- /MVVM Example/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /MVVM Example/App.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace MVVM_Example 2 | { 3 | /// 4 | /// Interaction logic for App.xaml 5 | /// 6 | public partial class App 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /MVVM Example/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/.idea.MVVM Example/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/.idea.MVVM Example/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/.idea.MVVM Example/.idea/indexLayout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MVVM Example/Model/Phone.cs: -------------------------------------------------------------------------------- 1 | namespace MVVM_Example.Model 2 | { 3 | public sealed class Phone 4 | { 5 | public string Title { get; set; } 6 | 7 | public string Company { get; set; } 8 | 9 | public int Price { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /MVVM Example/Services/IDialogService.cs: -------------------------------------------------------------------------------- 1 | namespace MVVM_Example 2 | { 3 | public interface IDialogService 4 | { 5 | void ShowMessage(string message); 6 | string FilePath { get; set; } 7 | bool OpenFileDialog(); 8 | bool SaveFileDialog(); 9 | } 10 | } -------------------------------------------------------------------------------- /.idea/.idea.MVVM Example/riderModule.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /MVVM Example/Services/IFileService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using MVVM_Example.Model; 3 | 4 | namespace MVVM_Example.ViewModel 5 | { 6 | public interface IFileService 7 | { 8 | List Open(string fileName); 9 | void Save(string fileName, List phoneList); 10 | } 11 | } -------------------------------------------------------------------------------- /.idea/.idea.MVVM Example/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Rider ignored files 5 | /contentModel.xml 6 | /.idea.MVVM Example.iml 7 | /modules.xml 8 | /projectSettingsUpdater.xml 9 | # Datasource local storage ignored files 10 | /dataSources/ 11 | /dataSources.local.xml 12 | # Editor-based HTTP Client requests 13 | /httpRequests/ 14 | -------------------------------------------------------------------------------- /MVVM Example/App.xaml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /MVVM Example.sln.DotSettings: -------------------------------------------------------------------------------- 1 | 2 | True -------------------------------------------------------------------------------- /MVVM Example/View/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using MVVM_Example.ViewModel; 2 | 3 | namespace MVVM_Example 4 | { 5 | /// 6 | /// Interaction logic for MainWindow.xaml 7 | /// 8 | public partial class MainWindow 9 | { 10 | public MainWindow() 11 | { 12 | InitializeComponent(); 13 | 14 | DataContext = new ApplicationViewModel(new DefaultDialogService(), new JsonFileService()); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /MVVM Example.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MVVM Example", "MVVM Example\MVVM Example.csproj", "{85B4E53F-9D59-4AE5-AC95-9A9913625447}" 4 | EndProject 5 | Global 6 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 7 | Debug|Any CPU = Debug|Any CPU 8 | Release|Any CPU = Release|Any CPU 9 | EndGlobalSection 10 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 11 | {85B4E53F-9D59-4AE5-AC95-9A9913625447}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 12 | {85B4E53F-9D59-4AE5-AC95-9A9913625447}.Debug|Any CPU.Build.0 = Debug|Any CPU 13 | {85B4E53F-9D59-4AE5-AC95-9A9913625447}.Release|Any CPU.ActiveCfg = Release|Any CPU 14 | {85B4E53F-9D59-4AE5-AC95-9A9913625447}.Release|Any CPU.Build.0 = Release|Any CPU 15 | EndGlobalSection 16 | EndGlobal 17 | -------------------------------------------------------------------------------- /MVVM Example/Services/DefaultDialogService.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using Microsoft.Win32; 3 | 4 | namespace MVVM_Example 5 | { 6 | public class DefaultDialogService : IDialogService 7 | { 8 | public void ShowMessage(string message) 9 | { 10 | MessageBox.Show(message); 11 | } 12 | 13 | public string FilePath { get; set; } 14 | public bool OpenFileDialog() 15 | { 16 | OpenFileDialog openFileDialog = new OpenFileDialog(); 17 | if (openFileDialog.ShowDialog() == true) 18 | { 19 | FilePath = openFileDialog.FileName; 20 | return true; 21 | } 22 | 23 | return false; 24 | } 25 | 26 | public bool SaveFileDialog() 27 | { 28 | SaveFileDialog saveFileDialog = new SaveFileDialog(); 29 | if (saveFileDialog.ShowDialog() == true) 30 | { 31 | FilePath = saveFileDialog.FileName; 32 | return true; 33 | } 34 | 35 | return false; 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /MVVM Example/Services/JsonFileService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Runtime.Serialization.Json; 4 | using MVVM_Example.Model; 5 | 6 | namespace MVVM_Example.ViewModel 7 | { 8 | public class JsonFileService : IFileService 9 | { 10 | public List Open(string fileName) 11 | { 12 | List phones = new List(); 13 | DataContractJsonSerializer jsonFormatter = new DataContractJsonSerializer(typeof(List)); 14 | using (FileStream fs = new FileStream(fileName, FileMode.OpenOrCreate)) 15 | { 16 | phones = jsonFormatter.ReadObject(fs) as List; 17 | } 18 | 19 | return phones; 20 | } 21 | 22 | public void Save(string fileName, List phoneList) 23 | { 24 | DataContractJsonSerializer jsonFormatter = new DataContractJsonSerializer(typeof(List)); 25 | using (FileStream fs = new FileStream(fileName, FileMode.Create)) 26 | { 27 | jsonFormatter.WriteObject(fs, phoneList); 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /MVVM Example/Services/Commands/RelayCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Input; 3 | 4 | namespace MVVM_Example.ViewModel.Commands 5 | { 6 | public class RelayCommand : ICommand 7 | { 8 | private Action execute; //execution logic 9 | private Func canExecute; //detects whether command can be executed 10 | 11 | 12 | public event EventHandler CanExecuteChanged //is called when conditions for whether command can be executed or not change 13 | { 14 | add { CommandManager.RequerySuggested += value; } 15 | remove { CommandManager.RequerySuggested -= value; } 16 | } 17 | 18 | public RelayCommand(Action execute, Func canExecute = null) 19 | { 20 | this.execute = execute; 21 | this.canExecute = canExecute; 22 | } 23 | 24 | public bool CanExecute(object parameter) 25 | { 26 | return this.canExecute == null || this.canExecute(parameter); 27 | } 28 | 29 | public void Execute(object parameter) 30 | { 31 | this.execute(parameter); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /MVVM Example/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("MVVM_Example")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("MVVM_Example")] 15 | [assembly: AssemblyCopyright("Copyright © 2020")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [assembly: ComVisible(false)] 23 | 24 | //In order to begin building localizable applications, set 25 | //CultureYouAreCodingWith in your .csproj file 26 | //inside a . For example, if you are using US english 27 | //in your source files, set the to en-US. Then uncomment 28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 29 | //the line below to match the UICulture setting in the project file. 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 36 | //(used if a resource is not found in the page, 37 | // or application resource dictionaries) 38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 39 | //(used if a resource is not found in the page, 40 | // app, or any theme specific resource dictionaries) 41 | )] 42 | 43 | 44 | // Version information for an assembly consists of the following four values: 45 | // 46 | // Major Version 47 | // Minor Version 48 | // Build Number 49 | // Revision 50 | // 51 | // You can specify all the values or you can default the Build and Revision Numbers 52 | // by using the '*' as shown below: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.0.0.0")] 55 | [assembly: AssemblyFileVersion("1.0.0.0")] -------------------------------------------------------------------------------- /MVVM Example/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace MVVM_Example.Properties 12 | { 13 | /// 14 | /// A strongly-typed resource class, for looking up localized strings, etc. 15 | /// 16 | // This class was auto-generated by the StronglyTypedResourceBuilder 17 | // class via a tool like ResGen or Visual Studio. 18 | // To add or remove a member, edit your .ResX file then rerun ResGen 19 | // with the /str option, or rebuild your VS project. 20 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", 21 | "4.0.0.0")] 22 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 23 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 24 | internal class Resources 25 | { 26 | private static global::System.Resources.ResourceManager resourceMan; 27 | 28 | private static global::System.Globalization.CultureInfo resourceCulture; 29 | 30 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", 31 | "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() 33 | { 34 | } 35 | 36 | /// 37 | /// Returns the cached ResourceManager instance used by this class. 38 | /// 39 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState 40 | .Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) 46 | { 47 | global::System.Resources.ResourceManager temp = 48 | new global::System.Resources.ResourceManager("MVVM_Example.Properties.Resources", 49 | typeof(Resources).Assembly); 50 | resourceMan = temp; 51 | } 52 | 53 | return resourceMan; 54 | } 55 | } 56 | 57 | /// 58 | /// Overrides the current thread's CurrentUICulture property for all 59 | /// resource lookups using this strongly typed resource class. 60 | /// 61 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState 62 | .Advanced)] 63 | internal static global::System.Globalization.CultureInfo Culture 64 | { 65 | get { return resourceCulture; } 66 | set { resourceCulture = value; } 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /MVVM Example/View/MainWindow.xaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 14 | 15 | 18 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /MVVM Example/MVVM Example.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {85B4E53F-9D59-4AE5-AC95-9A9913625447} 8 | WinExe 9 | MVVM_Example 10 | MVVM_Example 11 | v4.8 12 | 512 13 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | 4 15 | true 16 | 17 | 18 | AnyCPU 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | 38 | ..\packages\System.Windows.Interactivity.WPF.2.0.20525\lib\net40\Microsoft.Expression.Interactions.dll 39 | True 40 | 41 | 42 | 43 | 44 | 45 | 46 | ..\packages\System.Windows.Interactivity.WPF.2.0.20525\lib\net40\System.Windows.Interactivity.dll 47 | True 48 | 49 | 50 | 51 | 4.0 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | MSBuild:Compile 60 | Designer 61 | 62 | 63 | App.xaml 64 | Code 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | MainWindow.xaml 76 | Code 77 | 78 | 79 | 80 | 81 | Code 82 | 83 | 84 | True 85 | True 86 | Resources.resx 87 | 88 | 89 | ResXFileCodeGenerator 90 | Resources.Designer.cs 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | MSBuild:Compile 100 | Designer 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /MVVM Example/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /MVVM Example/ViewModel/ApplicationViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.ObjectModel; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Runtime.CompilerServices; 6 | using MVVM_Example.Model; 7 | using MVVM_Example.ViewModel.Commands; 8 | 9 | namespace MVVM_Example.ViewModel 10 | { 11 | public class ApplicationViewModel : INotifyPropertyChanged 12 | { 13 | 14 | private Phone selectedPhone; 15 | 16 | private int selectedPhoneIndex; 17 | 18 | private readonly IFileService _fileService; 19 | private readonly IDialogService _dialogService; 20 | 21 | public ObservableCollection Phones { get; set; } 22 | 23 | public Phone SelectedPhone 24 | { 25 | get { return selectedPhone; } 26 | set 27 | { 28 | selectedPhone = value; 29 | OnPropertyChanged("SelectedPhone"); 30 | } 31 | } 32 | 33 | public int SelectedPhoneIndex 34 | { 35 | get { return selectedPhoneIndex; } 36 | set 37 | { 38 | selectedPhoneIndex = value; 39 | } 40 | } 41 | 42 | // Command to save file 43 | private RelayCommand _saveCommand; 44 | 45 | public RelayCommand SaveCommand 46 | { 47 | get 48 | { 49 | return _saveCommand ?? 50 | (_saveCommand = new RelayCommand(obj => 51 | { 52 | try 53 | { 54 | if (_dialogService.SaveFileDialog() == true) 55 | { 56 | _fileService.Save(_dialogService.FilePath, 57 | Phones.Select(phone => new Phone 58 | {Company = phone.Company, Price = phone.Price, Title = phone.Title}) 59 | .ToList()); 60 | _dialogService.ShowMessage("File saved"); 61 | } 62 | } 63 | catch (Exception ex) 64 | { 65 | _dialogService.ShowMessage(ex.Message); 66 | } 67 | })); 68 | } 69 | } 70 | 71 | //Command to open file 72 | private RelayCommand _openCommand; 73 | 74 | public RelayCommand OpenCommand 75 | { 76 | get 77 | { 78 | return _openCommand ?? 79 | (_openCommand = new RelayCommand(obj => 80 | { 81 | try 82 | { 83 | if (_dialogService.OpenFileDialog() == true) 84 | { 85 | var phones = _fileService.Open(_dialogService.FilePath); 86 | Phones.Clear(); 87 | foreach (var phone in phones) 88 | { 89 | Phones.Add(phone); 90 | } 91 | 92 | _dialogService.ShowMessage("File opened"); 93 | } 94 | } 95 | catch (Exception ex) 96 | { 97 | _dialogService.ShowMessage(ex.Message); 98 | } 99 | })); 100 | } 101 | } 102 | 103 | // Command to add new object 104 | private RelayCommand _addCommand; 105 | 106 | public RelayCommand AddCommand 107 | { 108 | get { return _addCommand ?? 109 | (_addCommand = new RelayCommand(obj => 110 | { 111 | Phone phone = new Phone(); 112 | Phones.Insert(0, phone); 113 | SelectedPhone = phone; 114 | 115 | })); } 116 | } 117 | 118 | // Command to delete the object 119 | private RelayCommand _removeCommand; 120 | 121 | public RelayCommand RemoveCommand 122 | { 123 | get 124 | { 125 | return _removeCommand ?? 126 | (_removeCommand = new RelayCommand(obj => 127 | { 128 | Phone phone = obj as Phone; 129 | var selectedIndex = selectedPhoneIndex; 130 | if (phone != null) 131 | { 132 | Phones.Remove(phone); 133 | } 134 | 135 | UpdatePhonesListSelection(Phones.Count, selectedIndex); 136 | }, 137 | (obj) => Phones.Count > 0)); //Button will be disabled in "canExecute = false" 138 | } 139 | } 140 | 141 | private void UpdatePhonesListSelection(int phonesCount, int selectedIndex) 142 | { 143 | if (phonesCount == 0) 144 | { 145 | SelectedPhone = null; 146 | } 147 | else if (phonesCount <= selectedIndex) 148 | { 149 | SelectedPhone = Phones[phonesCount - 1]; 150 | } 151 | else 152 | { 153 | SelectedPhone = Phones[selectedIndex]; 154 | } 155 | } 156 | 157 | //Command to create Phone record copy 158 | private RelayCommand _copyCommand; 159 | public RelayCommand CopyCommand 160 | { 161 | get 162 | { 163 | return _copyCommand ?? 164 | (_copyCommand = new RelayCommand(obj => 165 | { 166 | Phone phone = obj as Phone; 167 | if (phone != null) 168 | { 169 | Phone phoneCopy = new Phone 170 | { 171 | Company = phone.Company, 172 | Price = phone.Price, 173 | Title = phone.Title 174 | 175 | }; 176 | Phones.Insert(0, phoneCopy); 177 | } 178 | }, 179 | (obj) => !(SelectedPhone is null))); 180 | } 181 | } 182 | 183 | public ApplicationViewModel(IDialogService dialogService, IFileService fileService) 184 | { 185 | this._dialogService = dialogService; 186 | this._fileService = fileService; 187 | 188 | //Default data 189 | Phones = new ObservableCollection 190 | { 191 | new Phone {Title = "iPhone 101", Company = "Apple", Price = 100000}, 192 | new Phone {Title = "Galaxy S100500 Edge", Company = "Samsung", Price = 110000}, 193 | new Phone {Title = "Mi42S", Company = "Xiaomi", Price = 70000} 194 | }; 195 | } 196 | 197 | public event PropertyChangedEventHandler PropertyChanged; 198 | public void OnPropertyChanged([CallerMemberName] string propertyName = null) 199 | { 200 | if (PropertyChanged != null) 201 | { 202 | PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 203 | } 204 | } 205 | } 206 | } -------------------------------------------------------------------------------- /MVVM Example/Properties/Annotations.cs: -------------------------------------------------------------------------------- 1 | /* MIT License 2 | 3 | Copyright (c) 2016 JetBrains http://www.jetbrains.com 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 | 23 | using System; 24 | // ReSharper disable InheritdocConsiderUsage 25 | 26 | #pragma warning disable 1591 27 | // ReSharper disable UnusedMember.Global 28 | // ReSharper disable MemberCanBePrivate.Global 29 | // ReSharper disable UnusedAutoPropertyAccessor.Global 30 | // ReSharper disable IntroduceOptionalParameters.Global 31 | // ReSharper disable MemberCanBeProtected.Global 32 | // ReSharper disable InconsistentNaming 33 | 34 | namespace MVVM_Example.Annotations 35 | { 36 | /// 37 | /// Indicates that the value of the marked element could be null sometimes, 38 | /// so checking for null is required before its usage. 39 | /// 40 | /// 41 | /// [CanBeNull] object Test() => null; 42 | /// 43 | /// void UseTest() { 44 | /// var p = Test(); 45 | /// var s = p.ToString(); // Warning: Possible 'System.NullReferenceException' 46 | /// } 47 | /// 48 | [AttributeUsage( 49 | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | 50 | AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | 51 | AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] 52 | public sealed class CanBeNullAttribute : Attribute { } 53 | 54 | /// 55 | /// Indicates that the value of the marked element can never be null. 56 | /// 57 | /// 58 | /// [NotNull] object Foo() { 59 | /// return null; // Warning: Possible 'null' assignment 60 | /// } 61 | /// 62 | [AttributeUsage( 63 | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | 64 | AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | 65 | AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] 66 | public sealed class NotNullAttribute : Attribute { } 67 | 68 | /// 69 | /// Can be applied to symbols of types derived from IEnumerable as well as to symbols of Task 70 | /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property 71 | /// or of the Lazy.Value property can never be null. 72 | /// 73 | /// 74 | /// public void Foo([ItemNotNull]List<string> books) 75 | /// { 76 | /// foreach (var book in books) { 77 | /// if (book != null) // Warning: Expression is always true 78 | /// Console.WriteLine(book.ToUpper()); 79 | /// } 80 | /// } 81 | /// 82 | [AttributeUsage( 83 | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | 84 | AttributeTargets.Delegate | AttributeTargets.Field)] 85 | public sealed class ItemNotNullAttribute : Attribute { } 86 | 87 | /// 88 | /// Can be applied to symbols of types derived from IEnumerable as well as to symbols of Task 89 | /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property 90 | /// or of the Lazy.Value property can be null. 91 | /// 92 | /// 93 | /// public void Foo([ItemCanBeNull]List<string> books) 94 | /// { 95 | /// foreach (var book in books) 96 | /// { 97 | /// // Warning: Possible 'System.NullReferenceException' 98 | /// Console.WriteLine(book.ToUpper()); 99 | /// } 100 | /// } 101 | /// 102 | [AttributeUsage( 103 | AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | 104 | AttributeTargets.Delegate | AttributeTargets.Field)] 105 | public sealed class ItemCanBeNullAttribute : Attribute { } 106 | 107 | /// 108 | /// Indicates that the marked method builds string by the format pattern and (optional) arguments. 109 | /// The parameter, which contains the format string, should be given in constructor. The format string 110 | /// should be in -like form. 111 | /// 112 | /// 113 | /// [StringFormatMethod("message")] 114 | /// void ShowError(string message, params object[] args) { /* do something */ } 115 | /// 116 | /// void Foo() { 117 | /// ShowError("Failed: {0}"); // Warning: Non-existing argument in format string 118 | /// } 119 | /// 120 | [AttributeUsage( 121 | AttributeTargets.Constructor | AttributeTargets.Method | 122 | AttributeTargets.Property | AttributeTargets.Delegate)] 123 | public sealed class StringFormatMethodAttribute : Attribute 124 | { 125 | /// 126 | /// Specifies which parameter of an annotated method should be treated as the format string 127 | /// 128 | public StringFormatMethodAttribute([NotNull] string formatParameterName) 129 | { 130 | FormatParameterName = formatParameterName; 131 | } 132 | 133 | [NotNull] public string FormatParameterName { get; } 134 | } 135 | 136 | /// 137 | /// Use this annotation to specify a type that contains static or const fields 138 | /// with values for the annotated property/field/parameter. 139 | /// The specified type will be used to improve completion suggestions. 140 | /// 141 | /// 142 | /// namespace TestNamespace 143 | /// { 144 | /// public class Constants 145 | /// { 146 | /// public static int INT_CONST = 1; 147 | /// public const string STRING_CONST = "1"; 148 | /// } 149 | /// 150 | /// public class Class1 151 | /// { 152 | /// [ValueProvider("TestNamespace.Constants")] public int myField; 153 | /// public void Foo([ValueProvider("TestNamespace.Constants")] string str) { } 154 | /// 155 | /// public void Test() 156 | /// { 157 | /// Foo(/*try completion here*/);// 158 | /// myField = /*try completion here*/ 159 | /// } 160 | /// } 161 | /// } 162 | /// 163 | [AttributeUsage( 164 | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field, 165 | AllowMultiple = true)] 166 | public sealed class ValueProviderAttribute : Attribute 167 | { 168 | public ValueProviderAttribute([NotNull] string name) 169 | { 170 | Name = name; 171 | } 172 | 173 | [NotNull] public string Name { get; } 174 | } 175 | 176 | /// 177 | /// Indicates that the integral value falls into the specified interval. 178 | /// It's allowed to specify multiple non-intersecting intervals. 179 | /// Values of interval boundaries are inclusive. 180 | /// 181 | /// 182 | /// void Foo([ValueRange(0, 100)] int value) { 183 | /// if (value == -1) { // Warning: Expression is always 'false' 184 | /// ... 185 | /// } 186 | /// } 187 | /// 188 | [AttributeUsage( 189 | AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property | 190 | AttributeTargets.Method | AttributeTargets.Delegate, 191 | AllowMultiple = true)] 192 | public sealed class ValueRangeAttribute : Attribute 193 | { 194 | public object From { get; } 195 | public object To { get; } 196 | 197 | public ValueRangeAttribute(long from, long to) 198 | { 199 | From = from; 200 | To = to; 201 | } 202 | 203 | public ValueRangeAttribute(ulong from, ulong to) 204 | { 205 | From = from; 206 | To = to; 207 | } 208 | 209 | public ValueRangeAttribute(long value) 210 | { 211 | From = To = value; 212 | } 213 | 214 | public ValueRangeAttribute(ulong value) 215 | { 216 | From = To = value; 217 | } 218 | } 219 | 220 | /// 221 | /// Indicates that the integral value never falls below zero. 222 | /// 223 | /// 224 | /// void Foo([NonNegativeValue] int value) { 225 | /// if (value == -1) { // Warning: Expression is always 'false' 226 | /// ... 227 | /// } 228 | /// } 229 | /// 230 | [AttributeUsage( 231 | AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property | 232 | AttributeTargets.Method | AttributeTargets.Delegate)] 233 | public sealed class NonNegativeValueAttribute : Attribute { } 234 | 235 | /// 236 | /// Indicates that the function argument should be a string literal and match one 237 | /// of the parameters of the caller function. For example, ReSharper annotates 238 | /// the parameter of . 239 | /// 240 | /// 241 | /// void Foo(string param) { 242 | /// if (param == null) 243 | /// throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol 244 | /// } 245 | /// 246 | [AttributeUsage(AttributeTargets.Parameter)] 247 | public sealed class InvokerParameterNameAttribute : Attribute { } 248 | 249 | /// 250 | /// Indicates that the method is contained in a type that implements 251 | /// System.ComponentModel.INotifyPropertyChanged interface and this method 252 | /// is used to notify that some property value changed. 253 | /// 254 | /// 255 | /// The method should be non-static and conform to one of the supported signatures: 256 | /// 257 | /// NotifyChanged(string) 258 | /// NotifyChanged(params string[]) 259 | /// NotifyChanged{T}(Expression{Func{T}}) 260 | /// NotifyChanged{T,U}(Expression{Func{T,U}}) 261 | /// SetProperty{T}(ref T, T, string) 262 | /// 263 | /// 264 | /// 265 | /// public class Foo : INotifyPropertyChanged { 266 | /// public event PropertyChangedEventHandler PropertyChanged; 267 | /// 268 | /// [NotifyPropertyChangedInvocator] 269 | /// protected virtual void NotifyChanged(string propertyName) { ... } 270 | /// 271 | /// string _name; 272 | /// 273 | /// public string Name { 274 | /// get { return _name; } 275 | /// set { _name = value; NotifyChanged("LastName"); /* Warning */ } 276 | /// } 277 | /// } 278 | /// 279 | /// Examples of generated notifications: 280 | /// 281 | /// NotifyChanged("Property") 282 | /// NotifyChanged(() => Property) 283 | /// NotifyChanged((VM x) => x.Property) 284 | /// SetProperty(ref myField, value, "Property") 285 | /// 286 | /// 287 | [AttributeUsage(AttributeTargets.Method)] 288 | public sealed class NotifyPropertyChangedInvocatorAttribute : Attribute 289 | { 290 | public NotifyPropertyChangedInvocatorAttribute() { } 291 | public NotifyPropertyChangedInvocatorAttribute([NotNull] string parameterName) 292 | { 293 | ParameterName = parameterName; 294 | } 295 | 296 | [CanBeNull] public string ParameterName { get; } 297 | } 298 | 299 | /// 300 | /// Describes dependency between method input and output. 301 | /// 302 | /// 303 | ///

Function Definition Table syntax:

304 | /// 305 | /// FDT ::= FDTRow [;FDTRow]* 306 | /// FDTRow ::= Input => Output | Output <= Input 307 | /// Input ::= ParameterName: Value [, Input]* 308 | /// Output ::= [ParameterName: Value]* {halt|stop|void|nothing|Value} 309 | /// Value ::= true | false | null | notnull | canbenull 310 | /// 311 | /// If the method has a single input parameter, its name could be omitted.
312 | /// Using halt (or void/nothing, which is the same) for the method output 313 | /// means that the method doesn't return normally (throws or terminates the process).
314 | /// Value canbenull is only applicable for output parameters.
315 | /// You can use multiple [ContractAnnotation] for each FDT row, or use single attribute 316 | /// with rows separated by semicolon. There is no notion of order rows, all rows are checked 317 | /// for applicability and applied per each program state tracked by the analysis engine.
318 | ///
319 | /// 320 | /// 321 | /// [ContractAnnotation("=> halt")] 322 | /// public void TerminationMethod() 323 | /// 324 | /// 325 | /// [ContractAnnotation("null <= param:null")] // reverse condition syntax 326 | /// public string GetName(string surname) 327 | /// 328 | /// 329 | /// [ContractAnnotation("s:null => true")] 330 | /// public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty() 331 | /// 332 | /// 333 | /// // A method that returns null if the parameter is null, 334 | /// // and not null if the parameter is not null 335 | /// [ContractAnnotation("null => null; notnull => notnull")] 336 | /// public object Transform(object data) 337 | /// 338 | /// 339 | /// [ContractAnnotation("=> true, result: notnull; => false, result: null")] 340 | /// public bool TryParse(string s, out Person result) 341 | /// 342 | /// 343 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] 344 | public sealed class ContractAnnotationAttribute : Attribute 345 | { 346 | public ContractAnnotationAttribute([NotNull] string contract) 347 | : this(contract, false) { } 348 | 349 | public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates) 350 | { 351 | Contract = contract; 352 | ForceFullStates = forceFullStates; 353 | } 354 | 355 | [NotNull] public string Contract { get; } 356 | 357 | public bool ForceFullStates { get; } 358 | } 359 | 360 | /// 361 | /// Indicates whether the marked element should be localized. 362 | /// 363 | /// 364 | /// [LocalizationRequiredAttribute(true)] 365 | /// class Foo { 366 | /// string str = "my string"; // Warning: Localizable string 367 | /// } 368 | /// 369 | [AttributeUsage(AttributeTargets.All)] 370 | public sealed class LocalizationRequiredAttribute : Attribute 371 | { 372 | public LocalizationRequiredAttribute() : this(true) { } 373 | 374 | public LocalizationRequiredAttribute(bool required) 375 | { 376 | Required = required; 377 | } 378 | 379 | public bool Required { get; } 380 | } 381 | 382 | /// 383 | /// Indicates that the value of the marked type (or its derivatives) 384 | /// cannot be compared using '==' or '!=' operators and Equals() 385 | /// should be used instead. However, using '==' or '!=' for comparison 386 | /// with null is always permitted. 387 | /// 388 | /// 389 | /// [CannotApplyEqualityOperator] 390 | /// class NoEquality { } 391 | /// 392 | /// class UsesNoEquality { 393 | /// void Test() { 394 | /// var ca1 = new NoEquality(); 395 | /// var ca2 = new NoEquality(); 396 | /// if (ca1 != null) { // OK 397 | /// bool condition = ca1 == ca2; // Warning 398 | /// } 399 | /// } 400 | /// } 401 | /// 402 | [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)] 403 | public sealed class CannotApplyEqualityOperatorAttribute : Attribute { } 404 | 405 | /// 406 | /// When applied to a target attribute, specifies a requirement for any type marked 407 | /// with the target attribute to implement or inherit specific type or types. 408 | /// 409 | /// 410 | /// [BaseTypeRequired(typeof(IComponent)] // Specify requirement 411 | /// class ComponentAttribute : Attribute { } 412 | /// 413 | /// [Component] // ComponentAttribute requires implementing IComponent interface 414 | /// class MyComponent : IComponent { } 415 | /// 416 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 417 | [BaseTypeRequired(typeof(Attribute))] 418 | public sealed class BaseTypeRequiredAttribute : Attribute 419 | { 420 | public BaseTypeRequiredAttribute([NotNull] Type baseType) 421 | { 422 | BaseType = baseType; 423 | } 424 | 425 | [NotNull] public Type BaseType { get; } 426 | } 427 | 428 | /// 429 | /// Indicates that the marked symbol is used implicitly (e.g. via reflection, in external library), 430 | /// so this symbol will not be reported as unused (as well as by other usage inspections). 431 | /// 432 | [AttributeUsage(AttributeTargets.All)] 433 | public sealed class UsedImplicitlyAttribute : Attribute 434 | { 435 | public UsedImplicitlyAttribute() 436 | : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } 437 | 438 | public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags) 439 | : this(useKindFlags, ImplicitUseTargetFlags.Default) { } 440 | 441 | public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags) 442 | : this(ImplicitUseKindFlags.Default, targetFlags) { } 443 | 444 | public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) 445 | { 446 | UseKindFlags = useKindFlags; 447 | TargetFlags = targetFlags; 448 | } 449 | 450 | public ImplicitUseKindFlags UseKindFlags { get; } 451 | 452 | public ImplicitUseTargetFlags TargetFlags { get; } 453 | } 454 | 455 | /// 456 | /// Can be applied to attributes, type parameters, and parameters of a type assignable from . 457 | /// When applied to an attribute, the decorated attribute behaves the same as . 458 | /// When applied to a type parameter or to a parameter of type , indicates that the corresponding type 459 | /// is used implicitly. 460 | /// 461 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.GenericParameter | AttributeTargets.Parameter)] 462 | public sealed class MeansImplicitUseAttribute : Attribute 463 | { 464 | public MeansImplicitUseAttribute() 465 | : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } 466 | 467 | public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags) 468 | : this(useKindFlags, ImplicitUseTargetFlags.Default) { } 469 | 470 | public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags) 471 | : this(ImplicitUseKindFlags.Default, targetFlags) { } 472 | 473 | public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) 474 | { 475 | UseKindFlags = useKindFlags; 476 | TargetFlags = targetFlags; 477 | } 478 | 479 | [UsedImplicitly] public ImplicitUseKindFlags UseKindFlags { get; } 480 | 481 | [UsedImplicitly] public ImplicitUseTargetFlags TargetFlags { get; } 482 | } 483 | 484 | /// 485 | /// Specify the details of implicitly used symbol when it is marked 486 | /// with or . 487 | /// 488 | [Flags] 489 | public enum ImplicitUseKindFlags 490 | { 491 | Default = Access | Assign | InstantiatedWithFixedConstructorSignature, 492 | /// Only entity marked with attribute considered used. 493 | Access = 1, 494 | /// Indicates implicit assignment to a member. 495 | Assign = 2, 496 | /// 497 | /// Indicates implicit instantiation of a type with fixed constructor signature. 498 | /// That means any unused constructor parameters won't be reported as such. 499 | /// 500 | InstantiatedWithFixedConstructorSignature = 4, 501 | /// Indicates implicit instantiation of a type. 502 | InstantiatedNoFixedConstructorSignature = 8, 503 | } 504 | 505 | /// 506 | /// Specify what is considered to be used implicitly when marked 507 | /// with or . 508 | /// 509 | [Flags] 510 | public enum ImplicitUseTargetFlags 511 | { 512 | Default = Itself, 513 | Itself = 1, 514 | /// Members of entity marked with attribute are considered used. 515 | Members = 2, 516 | /// Inherited entities are considered used. 517 | WithInheritors = 4, 518 | /// Entity marked with attribute and all its members considered used. 519 | WithMembers = Itself | Members 520 | } 521 | 522 | /// 523 | /// This attribute is intended to mark publicly available API 524 | /// which should not be removed and so is treated as used. 525 | /// 526 | [MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)] 527 | [AttributeUsage(AttributeTargets.All, Inherited = false)] 528 | public sealed class PublicAPIAttribute : Attribute 529 | { 530 | public PublicAPIAttribute() { } 531 | 532 | public PublicAPIAttribute([NotNull] string comment) 533 | { 534 | Comment = comment; 535 | } 536 | 537 | [CanBeNull] public string Comment { get; } 538 | } 539 | 540 | /// 541 | /// Tells code analysis engine if the parameter is completely handled when the invoked method is on stack. 542 | /// If the parameter is a delegate, indicates that delegate is executed while the method is executed. 543 | /// If the parameter is an enumerable, indicates that it is enumerated while the method is executed. 544 | /// 545 | [AttributeUsage(AttributeTargets.Parameter)] 546 | public sealed class InstantHandleAttribute : Attribute { } 547 | 548 | /// 549 | /// Indicates that a method does not make any observable state changes. 550 | /// The same as System.Diagnostics.Contracts.PureAttribute. 551 | /// 552 | /// 553 | /// [Pure] int Multiply(int x, int y) => x * y; 554 | /// 555 | /// void M() { 556 | /// Multiply(123, 42); // Warning: Return value of pure method is not used 557 | /// } 558 | /// 559 | [AttributeUsage(AttributeTargets.Method)] 560 | public sealed class PureAttribute : Attribute { } 561 | 562 | /// 563 | /// Indicates that the return value of the method invocation must be used. 564 | /// 565 | /// 566 | /// Methods decorated with this attribute (in contrast to pure methods) might change state, 567 | /// but make no sense without using their return value.
568 | /// Similarly to , this attribute 569 | /// will help detecting usages of the method when the return value in not used. 570 | /// Additionally, you can optionally specify a custom message, which will be used when showing warnings, e.g. 571 | /// [MustUseReturnValue("Use the return value to...")]. 572 | ///
573 | [AttributeUsage(AttributeTargets.Method)] 574 | public sealed class MustUseReturnValueAttribute : Attribute 575 | { 576 | public MustUseReturnValueAttribute() { } 577 | 578 | public MustUseReturnValueAttribute([NotNull] string justification) 579 | { 580 | Justification = justification; 581 | } 582 | 583 | [CanBeNull] public string Justification { get; } 584 | } 585 | 586 | /// 587 | /// Indicates the type member or parameter of some type, that should be used instead of all other ways 588 | /// to get the value of that type. This annotation is useful when you have some "context" value evaluated 589 | /// and stored somewhere, meaning that all other ways to get this value must be consolidated with existing one. 590 | /// 591 | /// 592 | /// class Foo { 593 | /// [ProvidesContext] IBarService _barService = ...; 594 | /// 595 | /// void ProcessNode(INode node) { 596 | /// DoSomething(node, node.GetGlobalServices().Bar); 597 | /// // ^ Warning: use value of '_barService' field 598 | /// } 599 | /// } 600 | /// 601 | [AttributeUsage( 602 | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Method | 603 | AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.GenericParameter)] 604 | public sealed class ProvidesContextAttribute : Attribute { } 605 | 606 | /// 607 | /// Indicates that a parameter is a path to a file or a folder within a web project. 608 | /// Path can be relative or absolute, starting from web root (~). 609 | /// 610 | [AttributeUsage(AttributeTargets.Parameter)] 611 | public sealed class PathReferenceAttribute : Attribute 612 | { 613 | public PathReferenceAttribute() { } 614 | 615 | public PathReferenceAttribute([NotNull, PathReference] string basePath) 616 | { 617 | BasePath = basePath; 618 | } 619 | 620 | [CanBeNull] public string BasePath { get; } 621 | } 622 | 623 | /// 624 | /// An extension method marked with this attribute is processed by code completion 625 | /// as a 'Source Template'. When the extension method is completed over some expression, its source code 626 | /// is automatically expanded like a template at call site. 627 | /// 628 | /// 629 | /// Template method body can contain valid source code and/or special comments starting with '$'. 630 | /// Text inside these comments is added as source code when the template is applied. Template parameters 631 | /// can be used either as additional method parameters or as identifiers wrapped in two '$' signs. 632 | /// Use the attribute to specify macros for parameters. 633 | /// 634 | /// 635 | /// In this example, the 'forEach' method is a source template available over all values 636 | /// of enumerable types, producing ordinary C# 'foreach' statement and placing caret inside block: 637 | /// 638 | /// [SourceTemplate] 639 | /// public static void forEach<T>(this IEnumerable<T> xs) { 640 | /// foreach (var x in xs) { 641 | /// //$ $END$ 642 | /// } 643 | /// } 644 | /// 645 | /// 646 | [AttributeUsage(AttributeTargets.Method)] 647 | public sealed class SourceTemplateAttribute : Attribute { } 648 | 649 | /// 650 | /// Allows specifying a macro for a parameter of a source template. 651 | /// 652 | /// 653 | /// You can apply the attribute on the whole method or on any of its additional parameters. The macro expression 654 | /// is defined in the property. When applied on a method, the target 655 | /// template parameter is defined in the property. To apply the macro silently 656 | /// for the parameter, set the property value = -1. 657 | /// 658 | /// 659 | /// Applying the attribute on a source template method: 660 | /// 661 | /// [SourceTemplate, Macro(Target = "item", Expression = "suggestVariableName()")] 662 | /// public static void forEach<T>(this IEnumerable<T> collection) { 663 | /// foreach (var item in collection) { 664 | /// //$ $END$ 665 | /// } 666 | /// } 667 | /// 668 | /// Applying the attribute on a template method parameter: 669 | /// 670 | /// [SourceTemplate] 671 | /// public static void something(this Entity x, [Macro(Expression = "guid()", Editable = -1)] string newguid) { 672 | /// /*$ var $x$Id = "$newguid$" + x.ToString(); 673 | /// x.DoSomething($x$Id); */ 674 | /// } 675 | /// 676 | /// 677 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method, AllowMultiple = true)] 678 | public sealed class MacroAttribute : Attribute 679 | { 680 | /// 681 | /// Allows specifying a macro that will be executed for a source template 682 | /// parameter when the template is expanded. 683 | /// 684 | [CanBeNull] public string Expression { get; set; } 685 | 686 | /// 687 | /// Allows specifying which occurrence of the target parameter becomes editable when the template is deployed. 688 | /// 689 | /// 690 | /// If the target parameter is used several times in the template, only one occurrence becomes editable; 691 | /// other occurrences are changed synchronously. To specify the zero-based index of the editable occurrence, 692 | /// use values >= 0. To make the parameter non-editable when the template is expanded, use -1. 693 | /// 694 | public int Editable { get; set; } 695 | 696 | /// 697 | /// Identifies the target parameter of a source template if the 698 | /// is applied on a template method. 699 | /// 700 | [CanBeNull] public string Target { get; set; } 701 | } 702 | 703 | [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] 704 | public sealed class AspMvcAreaMasterLocationFormatAttribute : Attribute 705 | { 706 | public AspMvcAreaMasterLocationFormatAttribute([NotNull] string format) 707 | { 708 | Format = format; 709 | } 710 | 711 | [NotNull] public string Format { get; } 712 | } 713 | 714 | [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] 715 | public sealed class AspMvcAreaPartialViewLocationFormatAttribute : Attribute 716 | { 717 | public AspMvcAreaPartialViewLocationFormatAttribute([NotNull] string format) 718 | { 719 | Format = format; 720 | } 721 | 722 | [NotNull] public string Format { get; } 723 | } 724 | 725 | [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] 726 | public sealed class AspMvcAreaViewLocationFormatAttribute : Attribute 727 | { 728 | public AspMvcAreaViewLocationFormatAttribute([NotNull] string format) 729 | { 730 | Format = format; 731 | } 732 | 733 | [NotNull] public string Format { get; } 734 | } 735 | 736 | [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] 737 | public sealed class AspMvcMasterLocationFormatAttribute : Attribute 738 | { 739 | public AspMvcMasterLocationFormatAttribute([NotNull] string format) 740 | { 741 | Format = format; 742 | } 743 | 744 | [NotNull] public string Format { get; } 745 | } 746 | 747 | [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] 748 | public sealed class AspMvcPartialViewLocationFormatAttribute : Attribute 749 | { 750 | public AspMvcPartialViewLocationFormatAttribute([NotNull] string format) 751 | { 752 | Format = format; 753 | } 754 | 755 | [NotNull] public string Format { get; } 756 | } 757 | 758 | [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] 759 | public sealed class AspMvcViewLocationFormatAttribute : Attribute 760 | { 761 | public AspMvcViewLocationFormatAttribute([NotNull] string format) 762 | { 763 | Format = format; 764 | } 765 | 766 | [NotNull] public string Format { get; } 767 | } 768 | 769 | /// 770 | /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter 771 | /// is an MVC action. If applied to a method, the MVC action name is calculated 772 | /// implicitly from the context. Use this attribute for custom wrappers similar to 773 | /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). 774 | /// 775 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] 776 | public sealed class AspMvcActionAttribute : Attribute 777 | { 778 | public AspMvcActionAttribute() { } 779 | 780 | public AspMvcActionAttribute([NotNull] string anonymousProperty) 781 | { 782 | AnonymousProperty = anonymousProperty; 783 | } 784 | 785 | [CanBeNull] public string AnonymousProperty { get; } 786 | } 787 | 788 | /// 789 | /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC area. 790 | /// Use this attribute for custom wrappers similar to 791 | /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). 792 | /// 793 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] 794 | public sealed class AspMvcAreaAttribute : Attribute 795 | { 796 | public AspMvcAreaAttribute() { } 797 | 798 | public AspMvcAreaAttribute([NotNull] string anonymousProperty) 799 | { 800 | AnonymousProperty = anonymousProperty; 801 | } 802 | 803 | [CanBeNull] public string AnonymousProperty { get; } 804 | } 805 | 806 | /// 807 | /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is 808 | /// an MVC controller. If applied to a method, the MVC controller name is calculated 809 | /// implicitly from the context. Use this attribute for custom wrappers similar to 810 | /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String, String). 811 | /// 812 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] 813 | public sealed class AspMvcControllerAttribute : Attribute 814 | { 815 | public AspMvcControllerAttribute() { } 816 | 817 | public AspMvcControllerAttribute([NotNull] string anonymousProperty) 818 | { 819 | AnonymousProperty = anonymousProperty; 820 | } 821 | 822 | [CanBeNull] public string AnonymousProperty { get; } 823 | } 824 | 825 | /// 826 | /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC Master. Use this attribute 827 | /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, String). 828 | /// 829 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] 830 | public sealed class AspMvcMasterAttribute : Attribute { } 831 | 832 | /// 833 | /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC model type. Use this attribute 834 | /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, Object). 835 | /// 836 | [AttributeUsage(AttributeTargets.Parameter)] 837 | public sealed class AspMvcModelTypeAttribute : Attribute { } 838 | 839 | /// 840 | /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC 841 | /// partial view. If applied to a method, the MVC partial view name is calculated implicitly 842 | /// from the context. Use this attribute for custom wrappers similar to 843 | /// System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper, String). 844 | /// 845 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] 846 | public sealed class AspMvcPartialViewAttribute : Attribute { } 847 | 848 | /// 849 | /// ASP.NET MVC attribute. Allows disabling inspections for MVC views within a class or a method. 850 | /// 851 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] 852 | public sealed class AspMvcSuppressViewErrorAttribute : Attribute { } 853 | 854 | /// 855 | /// ASP.NET MVC attribute. Indicates that a parameter is an MVC display template. 856 | /// Use this attribute for custom wrappers similar to 857 | /// System.Web.Mvc.Html.DisplayExtensions.DisplayForModel(HtmlHelper, String). 858 | /// 859 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] 860 | public sealed class AspMvcDisplayTemplateAttribute : Attribute { } 861 | 862 | /// 863 | /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC editor template. 864 | /// Use this attribute for custom wrappers similar to 865 | /// System.Web.Mvc.Html.EditorExtensions.EditorForModel(HtmlHelper, String). 866 | /// 867 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] 868 | public sealed class AspMvcEditorTemplateAttribute : Attribute { } 869 | 870 | /// 871 | /// ASP.NET MVC attribute. Indicates that the marked parameter is an MVC template. 872 | /// Use this attribute for custom wrappers similar to 873 | /// System.ComponentModel.DataAnnotations.UIHintAttribute(System.String). 874 | /// 875 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] 876 | public sealed class AspMvcTemplateAttribute : Attribute { } 877 | 878 | /// 879 | /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter 880 | /// is an MVC view component. If applied to a method, the MVC view name is calculated implicitly 881 | /// from the context. Use this attribute for custom wrappers similar to 882 | /// System.Web.Mvc.Controller.View(Object). 883 | /// 884 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] 885 | public sealed class AspMvcViewAttribute : Attribute { } 886 | 887 | /// 888 | /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter 889 | /// is an MVC view component name. 890 | /// 891 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] 892 | public sealed class AspMvcViewComponentAttribute : Attribute { } 893 | 894 | /// 895 | /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter 896 | /// is an MVC view component view. If applied to a method, the MVC view component view name is default. 897 | /// 898 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)] 899 | public sealed class AspMvcViewComponentViewAttribute : Attribute { } 900 | 901 | /// 902 | /// ASP.NET MVC attribute. When applied to a parameter of an attribute, 903 | /// indicates that this parameter is an MVC action name. 904 | /// 905 | /// 906 | /// [ActionName("Foo")] 907 | /// public ActionResult Login(string returnUrl) { 908 | /// ViewBag.ReturnUrl = Url.Action("Foo"); // OK 909 | /// return RedirectToAction("Bar"); // Error: Cannot resolve action 910 | /// } 911 | /// 912 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)] 913 | public sealed class AspMvcActionSelectorAttribute : Attribute { } 914 | 915 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)] 916 | public sealed class HtmlElementAttributesAttribute : Attribute 917 | { 918 | public HtmlElementAttributesAttribute() { } 919 | 920 | public HtmlElementAttributesAttribute([NotNull] string name) 921 | { 922 | Name = name; 923 | } 924 | 925 | [CanBeNull] public string Name { get; } 926 | } 927 | 928 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] 929 | public sealed class HtmlAttributeValueAttribute : Attribute 930 | { 931 | public HtmlAttributeValueAttribute([NotNull] string name) 932 | { 933 | Name = name; 934 | } 935 | 936 | [NotNull] public string Name { get; } 937 | } 938 | 939 | /// 940 | /// Razor attribute. Indicates that the marked parameter or method is a Razor section. 941 | /// Use this attribute for custom wrappers similar to 942 | /// System.Web.WebPages.WebPageBase.RenderSection(String). 943 | /// 944 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] 945 | public sealed class RazorSectionAttribute : Attribute { } 946 | 947 | /// 948 | /// Indicates how method, constructor invocation, or property access 949 | /// over collection type affects the contents of the collection. 950 | /// Use to specify the access type. 951 | /// 952 | /// 953 | /// Using this attribute only makes sense if all collection methods are marked with this attribute. 954 | /// 955 | /// 956 | /// public class MyStringCollection : List<string> 957 | /// { 958 | /// [CollectionAccess(CollectionAccessType.Read)] 959 | /// public string GetFirstString() 960 | /// { 961 | /// return this.ElementAt(0); 962 | /// } 963 | /// } 964 | /// class Test 965 | /// { 966 | /// public void Foo() 967 | /// { 968 | /// // Warning: Contents of the collection is never updated 969 | /// var col = new MyStringCollection(); 970 | /// string x = col.GetFirstString(); 971 | /// } 972 | /// } 973 | /// 974 | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property)] 975 | public sealed class CollectionAccessAttribute : Attribute 976 | { 977 | public CollectionAccessAttribute(CollectionAccessType collectionAccessType) 978 | { 979 | CollectionAccessType = collectionAccessType; 980 | } 981 | 982 | public CollectionAccessType CollectionAccessType { get; } 983 | } 984 | 985 | /// 986 | /// Provides a value for the to define 987 | /// how the collection method invocation affects the contents of the collection. 988 | /// 989 | [Flags] 990 | public enum CollectionAccessType 991 | { 992 | /// Method does not use or modify content of the collection. 993 | None = 0, 994 | /// Method only reads content of the collection but does not modify it. 995 | Read = 1, 996 | /// Method can change content of the collection but does not add new elements. 997 | ModifyExistingContent = 2, 998 | /// Method can add new elements to the collection. 999 | UpdatedContent = ModifyExistingContent | 4 1000 | } 1001 | 1002 | /// 1003 | /// Indicates that the marked method is assertion method, i.e. it halts the control flow if 1004 | /// one of the conditions is satisfied. To set the condition, mark one of the parameters with 1005 | /// attribute. 1006 | /// 1007 | [AttributeUsage(AttributeTargets.Method)] 1008 | public sealed class AssertionMethodAttribute : Attribute { } 1009 | 1010 | /// 1011 | /// Indicates the condition parameter of the assertion method. The method itself should be 1012 | /// marked by attribute. The mandatory argument of 1013 | /// the attribute is the assertion type. 1014 | /// 1015 | [AttributeUsage(AttributeTargets.Parameter)] 1016 | public sealed class AssertionConditionAttribute : Attribute 1017 | { 1018 | public AssertionConditionAttribute(AssertionConditionType conditionType) 1019 | { 1020 | ConditionType = conditionType; 1021 | } 1022 | 1023 | public AssertionConditionType ConditionType { get; } 1024 | } 1025 | 1026 | /// 1027 | /// Specifies assertion type. If the assertion method argument satisfies the condition, 1028 | /// then the execution continues. Otherwise, execution is assumed to be halted. 1029 | /// 1030 | public enum AssertionConditionType 1031 | { 1032 | /// Marked parameter should be evaluated to true. 1033 | IS_TRUE = 0, 1034 | /// Marked parameter should be evaluated to false. 1035 | IS_FALSE = 1, 1036 | /// Marked parameter should be evaluated to null value. 1037 | IS_NULL = 2, 1038 | /// Marked parameter should be evaluated to not null value. 1039 | IS_NOT_NULL = 3, 1040 | } 1041 | 1042 | /// 1043 | /// Indicates that the marked method unconditionally terminates control flow execution. 1044 | /// For example, it could unconditionally throw exception. 1045 | /// 1046 | [Obsolete("Use [ContractAnnotation('=> halt')] instead")] 1047 | [AttributeUsage(AttributeTargets.Method)] 1048 | public sealed class TerminatesProgramAttribute : Attribute { } 1049 | 1050 | /// 1051 | /// Indicates that method is pure LINQ method, with postponed enumeration (like Enumerable.Select, 1052 | /// .Where). This annotation allows inference of [InstantHandle] annotation for parameters 1053 | /// of delegate type by analyzing LINQ method chains. 1054 | /// 1055 | [AttributeUsage(AttributeTargets.Method)] 1056 | public sealed class LinqTunnelAttribute : Attribute { } 1057 | 1058 | /// 1059 | /// Indicates that IEnumerable passed as a parameter is not enumerated. 1060 | /// Use this annotation to suppress the 'Possible multiple enumeration of IEnumerable' inspection. 1061 | /// 1062 | /// 1063 | /// static void ThrowIfNull<T>([NoEnumeration] T v, string n) where T : class 1064 | /// { 1065 | /// // custom check for null but no enumeration 1066 | /// } 1067 | /// 1068 | /// void Foo(IEnumerable<string> values) 1069 | /// { 1070 | /// ThrowIfNull(values, nameof(values)); 1071 | /// var x = values.ToList(); // No warnings about multiple enumeration 1072 | /// } 1073 | /// 1074 | [AttributeUsage(AttributeTargets.Parameter)] 1075 | public sealed class NoEnumerationAttribute : Attribute { } 1076 | 1077 | /// 1078 | /// Indicates that the marked parameter is a regular expression pattern. 1079 | /// 1080 | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] 1081 | public sealed class RegexPatternAttribute : Attribute { } 1082 | 1083 | /// 1084 | /// Prevents the Member Reordering feature from tossing members of the marked class. 1085 | /// 1086 | /// 1087 | /// The attribute must be mentioned in your member reordering patterns. 1088 | /// 1089 | [AttributeUsage( 1090 | AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.Enum)] 1091 | public sealed class NoReorderAttribute : Attribute { } 1092 | 1093 | /// 1094 | /// XAML attribute. Indicates the type that has ItemsSource property and should be treated 1095 | /// as ItemsControl-derived type, to enable inner items DataContext type resolve. 1096 | /// 1097 | [AttributeUsage(AttributeTargets.Class)] 1098 | public sealed class XamlItemsControlAttribute : Attribute { } 1099 | 1100 | /// 1101 | /// XAML attribute. Indicates the property of some BindingBase-derived type, that 1102 | /// is used to bind some item of ItemsControl-derived type. This annotation will 1103 | /// enable the DataContext type resolve for XAML bindings for such properties. 1104 | /// 1105 | /// 1106 | /// Property should have the tree ancestor of the ItemsControl type or 1107 | /// marked with the attribute. 1108 | /// 1109 | [AttributeUsage(AttributeTargets.Property)] 1110 | public sealed class XamlItemBindingOfItemsControlAttribute : Attribute { } 1111 | 1112 | /// 1113 | /// XAML attribute. Indicates the property of some Style-derived type, that 1114 | /// is used to style items of ItemsControl-derived type. This annotation will 1115 | /// enable the DataContext type resolve for XAML bindings for such properties. 1116 | /// 1117 | /// 1118 | /// Property should have the tree ancestor of the ItemsControl type or 1119 | /// marked with the attribute. 1120 | /// 1121 | [AttributeUsage(AttributeTargets.Property)] 1122 | public sealed class XamlItemStyleOfItemsControlAttribute : Attribute { } 1123 | 1124 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 1125 | public sealed class AspChildControlTypeAttribute : Attribute 1126 | { 1127 | public AspChildControlTypeAttribute([NotNull] string tagName, [NotNull] Type controlType) 1128 | { 1129 | TagName = tagName; 1130 | ControlType = controlType; 1131 | } 1132 | 1133 | [NotNull] public string TagName { get; } 1134 | 1135 | [NotNull] public Type ControlType { get; } 1136 | } 1137 | 1138 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] 1139 | public sealed class AspDataFieldAttribute : Attribute { } 1140 | 1141 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] 1142 | public sealed class AspDataFieldsAttribute : Attribute { } 1143 | 1144 | [AttributeUsage(AttributeTargets.Property)] 1145 | public sealed class AspMethodPropertyAttribute : Attribute { } 1146 | 1147 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 1148 | public sealed class AspRequiredAttributeAttribute : Attribute 1149 | { 1150 | public AspRequiredAttributeAttribute([NotNull] string attribute) 1151 | { 1152 | Attribute = attribute; 1153 | } 1154 | 1155 | [NotNull] public string Attribute { get; } 1156 | } 1157 | 1158 | [AttributeUsage(AttributeTargets.Property)] 1159 | public sealed class AspTypePropertyAttribute : Attribute 1160 | { 1161 | public bool CreateConstructorReferences { get; } 1162 | 1163 | public AspTypePropertyAttribute(bool createConstructorReferences) 1164 | { 1165 | CreateConstructorReferences = createConstructorReferences; 1166 | } 1167 | } 1168 | 1169 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] 1170 | public sealed class RazorImportNamespaceAttribute : Attribute 1171 | { 1172 | public RazorImportNamespaceAttribute([NotNull] string name) 1173 | { 1174 | Name = name; 1175 | } 1176 | 1177 | [NotNull] public string Name { get; } 1178 | } 1179 | 1180 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] 1181 | public sealed class RazorInjectionAttribute : Attribute 1182 | { 1183 | public RazorInjectionAttribute([NotNull] string type, [NotNull] string fieldName) 1184 | { 1185 | Type = type; 1186 | FieldName = fieldName; 1187 | } 1188 | 1189 | [NotNull] public string Type { get; } 1190 | 1191 | [NotNull] public string FieldName { get; } 1192 | } 1193 | 1194 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] 1195 | public sealed class RazorDirectiveAttribute : Attribute 1196 | { 1197 | public RazorDirectiveAttribute([NotNull] string directive) 1198 | { 1199 | Directive = directive; 1200 | } 1201 | 1202 | [NotNull] public string Directive { get; } 1203 | } 1204 | 1205 | [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] 1206 | public sealed class RazorPageBaseTypeAttribute : Attribute 1207 | { 1208 | public RazorPageBaseTypeAttribute([NotNull] string baseType) 1209 | { 1210 | BaseType = baseType; 1211 | } 1212 | public RazorPageBaseTypeAttribute([NotNull] string baseType, string pageName) 1213 | { 1214 | BaseType = baseType; 1215 | PageName = pageName; 1216 | } 1217 | 1218 | [NotNull] public string BaseType { get; } 1219 | [CanBeNull] public string PageName { get; } 1220 | } 1221 | 1222 | [AttributeUsage(AttributeTargets.Method)] 1223 | public sealed class RazorHelperCommonAttribute : Attribute { } 1224 | 1225 | [AttributeUsage(AttributeTargets.Property)] 1226 | public sealed class RazorLayoutAttribute : Attribute { } 1227 | 1228 | [AttributeUsage(AttributeTargets.Method)] 1229 | public sealed class RazorWriteLiteralMethodAttribute : Attribute { } 1230 | 1231 | [AttributeUsage(AttributeTargets.Method)] 1232 | public sealed class RazorWriteMethodAttribute : Attribute { } 1233 | 1234 | [AttributeUsage(AttributeTargets.Parameter)] 1235 | public sealed class RazorWriteMethodParameterAttribute : Attribute { } 1236 | } --------------------------------------------------------------------------------