diff --git a/.gitignore b/.gitignore index f3ccef0..4800d1c 100644 --- a/.gitignore +++ b/.gitignore @@ -359,3 +359,4 @@ MigrationBackup/ /src/FormsTemplatesCLI/Install-Template.bat /src/FormsTemplatesCLI/Install-Debug-Template.bat /src/FormsTemplatesCLI/Install-Release-Template.bat +/xamarin-forms-templates.code-workspace diff --git a/README.md b/README.md index b5e86d3..eceddfb 100644 --- a/README.md +++ b/README.md @@ -123,14 +123,27 @@ And `-na` denotes the namespace under which the file is to be created (Can also #### Generic Item Templates: +![Xamarin.Forms Generic Item Templates by Vijay Anand E G](images/xamarin-forms-add-new-item.png) + * A revolutionary generic item template, in XAML and C#, for creating items of any type -* And it is named `forms-item` and `forms-item-cs` +* Supported both within the VS2022 IDE and CLI +* On CLI, it is named as `forms-item` and `forms-item-cs` +* The same set of parameters is defined in the UI as `Dropdown`, `TextBox` and `CheckBox` for ease of use * Both need one required parameter, `-b` / `--base`, the base type * Optionally takes another parameter, `-g` / `--generic`, to specify the generic base type * In addition, the XAML template takes one more parameter, `-xo` / `--xaml-only`, when defined, generates only the XAML definition +* Frequently used base types are loaded in the Editable dropdown, user can also enter their value here +* Ensure the values are entered in Pascal notation. XAML templates support XML namespace prefix, quite like how it is used in real world (`xct:Popup`) +* The one big advantage of using this on IDE is the relative namespace to the folder where the item is created whereas on CLI, this defaults to the root namespace. As relative namespace resolution is yet to be fully supported by the CLI templating engine and is actively tracked [here](https://github.com/dotnet/templating/issues/6010) + +![Xamarin.Forms Generic Item Dialog by Vijay Anand E G](images/xamarin-forms-generic-item-dialog.png) *Note: Namespace resolution in both XAML and C# files is left to the user as deriving them with the template is outside its scope.* +*Tip 1: For XAML template, pass the `xmlns` scope, like `xct:Popup`, as part of the input parameter value and it'll be used appropriately in the generated source files.* + +*Tip: Tip: Use `local` scope to refer to the types in the same directory like `Views`. For e.g., `local:BasePage`.* + Example: ```shell dotnet new forms-item -n ThemePopup -b xct:Popup -p:na MyApp.Views diff --git a/images/xamarin-forms-add-new-item.png b/images/xamarin-forms-add-new-item.png new file mode 100644 index 0000000..5e7547d Binary files /dev/null and b/images/xamarin-forms-add-new-item.png differ diff --git a/images/xamarin-forms-class-library-options.png b/images/xamarin-forms-class-library-options.png index eba611a..f011580 100644 Binary files a/images/xamarin-forms-class-library-options.png and b/images/xamarin-forms-class-library-options.png differ diff --git a/images/xamarin-forms-generic-item-dialog.png b/images/xamarin-forms-generic-item-dialog.png new file mode 100644 index 0000000..03dea8f Binary files /dev/null and b/images/xamarin-forms-generic-item-dialog.png differ diff --git a/src/Extensions/XFormsTemplates/XFormsTemplates/CustomActionWizard.cs b/src/Extensions/XFormsTemplates/XFormsTemplates/CustomActionWizard.cs new file mode 100644 index 0000000..b0ddea4 --- /dev/null +++ b/src/Extensions/XFormsTemplates/XFormsTemplates/CustomActionWizard.cs @@ -0,0 +1,129 @@ +using EnvDTE; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.TemplateWizard; +using System; +using System.Collections.Generic; +using System.IO; + +namespace VijayAnand.XFormsTemplates +{ + public class CustomActionWizard : IWizard + { + bool xamlOnly; + bool userCancel; + + /// This method is called before opening any item that has the OpenInEditor attribute. + public void BeforeOpeningFile(ProjectItem projectItem) + { + + } + + public void ProjectFinishedGenerating(Project project) + { + + } + + /// This method is only called for item templates, not for project templates. + public void ProjectItemFinishedGenerating(ProjectItem projectItem) + { + + } + + /// This method is called after the project is created. + public void RunFinished() + { + + } + + public async void RunStarted(object automationObject, + Dictionary replacementsDictionary, + WizardRunKind runKind, + object[] customParams) + { + try + { + //ThreadHelper.ThrowIfNotOnUIThread(); + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + + if (runKind == WizardRunKind.AsNewItem) + { + if (replacementsDictionary.ContainsKey("$basetype$")) + { + var xamlItem = replacementsDictionary.ContainsKey("$xaml$"); + var window = new GenericItemDialog(xamlItem); + var result = window.ShowDialog(); + + if (result is true) + { + xamlOnly = window.XamlOnly; + replacementsDictionary["$xaml$"] = xamlOnly.ToString().ToLowerInvariant(); + + var baseType = window.BaseType; + var genericType = window.GenericType; + var baseTypeCS = baseType.Contains(":") ? baseType.Substring(baseType.IndexOf(':') + 1) : baseType; + var genericTypeCS = genericType.Contains(":") ? genericType.Substring(genericType.IndexOf(':') + 1) : genericType; + + if (!string.IsNullOrEmpty(baseType)) + { + if (xamlItem) + { + replacementsDictionary["$basetype$"] = baseType; + + if (string.IsNullOrEmpty(genericTypeCS)) + { + replacementsDictionary["$csbasetype$"] = baseTypeCS; + replacementsDictionary["$generic$"] = bool.FalseString.ToLowerInvariant(); + } + else + { + replacementsDictionary["$csbasetype$"] = $"{baseTypeCS}<{genericTypeCS}>"; + replacementsDictionary["$generic$"] = bool.TrueString.ToLowerInvariant(); + replacementsDictionary["$typearg$"] = genericType; + } + } + else + { + // For C# template, basetype is the parameter name + if (string.IsNullOrEmpty(genericTypeCS)) + { + replacementsDictionary["$basetype$"] = baseTypeCS; + } + else + { + replacementsDictionary["$basetype$"] = $"{baseTypeCS}<{genericTypeCS}>"; + } + } + } + } + else + { + userCancel = true; + } + } + } + } + catch (Exception ex) + { + // Log the exception. + await ex.LogAsync(); + } + } + + /// This method is only called for item templates, not for project templates. + public bool ShouldAddProjectItem(string filePath) + { + if (userCancel) + { + return false; + } + else if (filePath.EndsWith(".xaml.cs")) + { + return !xamlOnly; + } + else + { + return !File.Exists(filePath); + } + } + } +} diff --git a/src/Extensions/XFormsTemplates/XFormsTemplates/GenericItemDialog.xaml b/src/Extensions/XFormsTemplates/XFormsTemplates/GenericItemDialog.xaml new file mode 100644 index 0000000..66a9673 --- /dev/null +++ b/src/Extensions/XFormsTemplates/XFormsTemplates/GenericItemDialog.xaml @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + +