Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generic Item Templates for VS2022 - #25 #26

Merged
merged 1 commit into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Binary file added images/xamarin-forms-add-new-item.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified images/xamarin-forms-class-library-options.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/xamarin-forms-generic-item-dialog.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
129 changes: 129 additions & 0 deletions src/Extensions/XFormsTemplates/XFormsTemplates/CustomActionWizard.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>This method is called before opening any item that has the OpenInEditor attribute.</summary>
public void BeforeOpeningFile(ProjectItem projectItem)
{

}

public void ProjectFinishedGenerating(Project project)
{

}

/// <summary>This method is only called for item templates, not for project templates.</summary>
public void ProjectItemFinishedGenerating(ProjectItem projectItem)
{

}

/// <summary>This method is called after the project is created.</summary>
public void RunFinished()
{

}

public async void RunStarted(object automationObject,
Dictionary<string, string> 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();
}
}

/// <summary>This method is only called for item templates, not for project templates.</summary>
public bool ShouldAddProjectItem(string filePath)
{
if (userCancel)
{
return false;
}
else if (filePath.EndsWith(".xaml.cs"))
{
return !xamlOnly;
}
else
{
return !File.Exists(filePath);
}
}
}
}
144 changes: 144 additions & 0 deletions src/Extensions/XFormsTemplates/XFormsTemplates/GenericItemDialog.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="UTF-8" ?>
<ui:DialogWindow
x:Class="VijayAnand.XFormsTemplates.GenericItemDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:VijayAnand.XFormsTemplates"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.15.0"
Title="Xamarin.Forms Generic Item"
Width="480"
Height="320"
KeyDown="OnWindowKeyDown"
Loaded="OnWindowLoaded"
ResizeMode="NoResize"
ShowInTaskbar="False"
WindowStartupLocation="CenterOwner"
mc:Ignorable="d">
<ui:DialogWindow.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height=".14*" />
<RowDefinition Height=".14*" />
<RowDefinition Height=".12*" />
<RowDefinition Height=".14*" />
<RowDefinition Height=".16*" />
<RowDefinition Height=".15*" />
<RowDefinition Height=".15*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".05*" />
<ColumnDefinition Width=".5*" />
<ColumnDefinition Width=".18*" />
<ColumnDefinition Width=".02*" />
<ColumnDefinition Width=".18*" />
<ColumnDefinition Width=".05*" />
</Grid.ColumnDefinitions>
<Label
Grid.Row="0"
Grid.Column="1"
HorizontalAlignment="Left"
VerticalAlignment="Bottom"
Content="Base Type:" />
<ComboBox
x:Name="cboBaseType"
Grid.Row="1"
Grid.Column="1"
Grid.ColumnSpan="4"
Padding="5"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
AutomationProperties.AcceleratorKey="Alt+B"
AutomationProperties.AccessKey="B"
GotFocus="OnBaseTypeGotFocus"
IsEditable="True"
IsTextSearchCaseSensitive="True"
LostFocus="OnBaseTypeLostFocus"
TabIndex="0">
<ComboBox.Items>
<ComboBoxItem>Application</ComboBoxItem>
<ComboBoxItem>ContentPage</ComboBoxItem>
<ComboBoxItem>ContentView</ComboBoxItem>
<ComboBoxItem>FlyoutPage</ComboBoxItem>
<ComboBoxItem>Grid</ComboBoxItem>
<ComboBoxItem>NavigationPage</ComboBoxItem>
<ComboBoxItem>ResourceDictionary</ComboBoxItem>
<ComboBoxItem>Shell</ComboBoxItem>
<ComboBoxItem>StackLayout</ComboBoxItem>
<ComboBoxItem>SwipeView</ComboBoxItem>
<ComboBoxItem>TabbedPage</ComboBoxItem>
</ComboBox.Items>
</ComboBox>
<Label
Grid.Row="2"
Grid.Column="1"
HorizontalAlignment="Left"
VerticalAlignment="Bottom"
Content="Generic Base Type:" />
<TextBox
x:Name="txtGenericType"
Grid.Row="3"
Grid.Column="1"
Grid.ColumnSpan="4"
Padding="5"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
AutomationProperties.AcceleratorKey="Alt+G"
AutomationProperties.AccessKey="G"
GotFocus="OnGenericTypeGotFocus"
LostFocus="OnGenericTypeLostFocus"
TabIndex="1" />
<CheckBox
x:Name="chkXamlOnly"
Grid.Row="4"
Grid.Column="1"
HorizontalAlignment="Left"
VerticalAlignment="Center"
AutomationProperties.AccessKey="X"
Content="_Xaml Only"
TabIndex="2" />
<Button
Grid.Row="5"
Grid.Column="2"
MinWidth="80"
Padding="5"
HorizontalAlignment="Center"
VerticalAlignment="Center"
AutomationProperties.AcceleratorKey="Alt+O"
AutomationProperties.AccessKey="O"
Click="OnAcceptClick"
Content="_OK"
IsDefault="True"
TabIndex="3" />
<Button
Grid.Row="5"
Grid.Column="4"
MinWidth="80"
Padding="5"
HorizontalAlignment="Center"
VerticalAlignment="Center"
AutomationProperties.AcceleratorKey="Alt+C"
AutomationProperties.AccessKey="C"
Click="OnCancelClick"
Content="_Cancel"
IsCancel="True"
TabIndex="4" />
<TextBlock
Grid.Row="6"
Grid.Column="1"
Grid.ColumnSpan="3"
HorizontalAlignment="Left"
VerticalAlignment="Center">
<TextBlock.Text>
&#169; 2024 Vijay Anand E G&#160;
</TextBlock.Text>
<Hyperlink
NavigateUri="https://egvijayanand.in/"
RequestNavigate="OnRequestNavigate">
egvijayanand.in
</Hyperlink>
</TextBlock>
</Grid>
</ui:DialogWindow.Content>
</ui:DialogWindow>
Loading