Last active
January 30, 2021 14:38
-
-
Save astrohart/9a1e13af51df339bfc13054828ff7653 to your computer and use it in GitHub Desktop.
T4 Template that creates an ObservableListSource in the <project-name>.DataBinding Namespace. This is what Navigation Properties should use for EF6+WinForms compatibility.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<#@ template language="C#" debug="false" hostspecific="true" #> | |
<#@ output extension=".cs" #> | |
<#@ assembly name="System" #> | |
<#@ import namespace="System.Collections" #> | |
<#@ import namespace="System.Collections.Generic" #> | |
<#@ assembly name="System.Core" #> | |
<#@ assembly name="System.Data.Entity" #> | |
<#@ import namespace="System.IO" #> | |
<#@ import namespace="System.Linq" #> | |
<#@ import namespace="System.Reflection" #> | |
<#@ assembly name="EnvDTE" #> | |
<#@ import namespace="EnvDTE" #> | |
<#@ assembly name="System.Data.Entity.Design" #> | |
<#@ import namespace="System.Data.Entity.Design.PluralizationServices" #> | |
<# | |
// TODO: Place calls here to class-feature method calls. | |
InitializeActiveProjectAndSolution(); | |
#> | |
<#= GenerateFileHeader()#> | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Collections.ObjectModel; | |
using System.ComponentModel; | |
using System.Data.Entity; | |
// ReSharper disable once CheckNamespace | |
namespace <#=this.DataBindingLayerNameSpace#> | |
{ | |
<# PushIndent(" "); #> | |
/// <summary> | |
/// This is a collection that is compatible with Windows Forms' data-bound | |
/// controls, as it allows Windows Forms graphical, data-bound presentation | |
/// controls to engage in two-way communications with memory objects. Please | |
/// also derive all your entities' navigation properties from this class by | |
/// modifying the Entities.Context.tt and Entities.tt T4 templates in your | |
/// Data Access Layer. | |
/// </summary> | |
/// <typeparam name="T">Name of the entity object being wrapped by this collection.</typeparam> | |
public class ObservableListSource<T> : ObservableCollection<T>, IListSource | |
where T : class | |
{ | |
<# PushIndent(" "); #> | |
/// <summary> | |
/// Constructs a new instance of | |
/// <see cref="T:<#=this.DataBindingLayerNameSpace#>.ObservableListSource" /> and returns a | |
/// reference to it. | |
/// </summary> | |
public ObservableListSource() { } | |
/// <summary> | |
/// Constructs a new instance of | |
/// <see cref="T:<#=this.DataBindingLayerNameSpace#>.ObservableListSource" /> and returns a | |
/// reference to it. | |
/// </summary> | |
/// <param name="collection"> | |
/// Enumerable collection of objects of | |
/// <typeparamref name="T" />. | |
/// </param> | |
public ObservableListSource(IEnumerable<T> collection) : base(collection) { } | |
/// <summary> | |
/// Gets a value indicating whether the collection is a collection of | |
/// <see cref="T:System.Collections.IList" /> objects. | |
/// </summary> | |
/// <returns> | |
/// <see langword="true" /> if the collection is a collection of | |
/// <see cref="T:System.Collections.IList" /> objects; otherwise, | |
/// <see langword="false" />. | |
/// </returns> | |
bool IListSource.ContainsListCollection | |
{ | |
get { return false; } | |
} | |
/// <summary> | |
/// Gets a reference to an instance of this collection that implements the | |
/// <see cref="T:System.ComponentModel.IBindingList" /> interface. | |
/// </summary> | |
public IBindingList List { get; private set; } | |
/// <summary> | |
/// Returns an <see cref="T:System.Collections.IList" /> that can be bound | |
/// to a data source from an object that does not implement an | |
/// <see cref="T:System.Collections.IList" /> itself. | |
/// </summary> | |
/// <returns> | |
/// An <see cref="T:System.Collections.IList" /> that can be bound to a | |
/// data source from the object. | |
/// </returns> | |
IList IListSource.GetList() | |
{ | |
return List ?? (List = this.ToBindingList()); | |
} | |
<# PopIndent(); #> | |
} | |
<# PopIndent(); #> | |
} | |
<# CreateFile("ObservableListSource.cs"); #> | |
<#+ | |
/// <summary> | |
/// Generates a header comment for the file so that users of the file know that it's generated from a template. | |
/// </summary> | |
public string GenerateFileHeader() | |
{ | |
return "//------------------------------------------------------------------------------\r\n// <auto-generated>\r\n// This code was generated from a template.\r\n//\r\n// Manual changes to this file may cause unexpected behavior in your application.\r\n// Manual changes to this file will be overwritten if the code is regenerated.\r\n// </auto-generated>\r\n//------------------------------------------------------------------------------"; | |
} | |
/// <summary> | |
/// Gets or sets a reference to the currently-active project in the integrated development environment (IDE). | |
/// </summary> | |
private Project ActiveProject { get; set; } | |
/// <summary> | |
/// Gets or sets a reference to the currently-active solution in the integrated development environment (IDE). | |
/// </summary> | |
private Solution ActiveSolution { get; set; } | |
/// <summary> | |
/// Gets or sets a reference to the top-level object in the Visual Studio automation object model. | |
/// </summary> | |
private DTE DTE { get; set; } | |
/// <summary> | |
/// Gets a string that contains the C# namespace name for the business layer. | |
/// </summary> | |
/// <remarks> | |
/// The business layer is the tier of your solution that contains classes generated from this T4 template. | |
/// </remarks> | |
private string BusinessLayerNameSpace { | |
get { | |
return ActiveProject == null ? string.Empty : string.Format( | |
"{0}.BusinessLayer", ActiveProject.Name | |
); | |
} | |
} | |
/// <summary> | |
/// Gets a string that contains the C# namespace name for the data-binding layer. | |
/// </summary> | |
/// <remarks> | |
/// The data-binding layer is where we implement useful classes that are utilized by | |
/// Windwos Forms for data-binding. | |
/// </remarks> | |
private string DataBindingLayerNameSpace { | |
get { | |
return ActiveProject == null ? string.Empty : string.Format( | |
"{0}.DataBinding", ActiveProject.Name | |
); | |
} | |
} | |
/// <summary> | |
/// Interrogates the Visual Studio object model to locate references to | |
/// the currently-active Solution and Project. | |
/// </summary> | |
public void InitializeActiveProjectAndSolution() | |
{ | |
IServiceProvider serviceProvider = (IServiceProvider)this.Host; | |
this.DTE = serviceProvider.GetService(typeof(DTE)) as DTE; | |
ActiveSolution = this.DTE.Solution; | |
ActiveProject = GetActiveProject(); | |
} | |
//Generating Seperate Files | |
public void ProcessContent(string outputFileName, string content) | |
{ | |
if (string.IsNullOrWhiteSpace(outputFileName)) throw new ArgumentNullException(nameof(outputFileName)); | |
if (string.IsNullOrWhiteSpace(content)) throw new ArgumentNullException(nameof(content)); | |
var templateDirectory = Path.GetDirectoryName(Host.TemplateFile); | |
var outputFilePath = Path.Combine(templateDirectory, outputFileName); | |
var outputDirectoryPath = Path.GetDirectoryName(outputFilePath); | |
if(!Directory.Exists(outputDirectoryPath)) | |
Directory.CreateDirectory(outputDirectoryPath); | |
if (File.Exists(outputFilePath)) // always overwrite existing output | |
File.Delete(outputFilePath); | |
File.WriteAllText(outputFilePath, content); | |
IServiceProvider hostServiceProvider = (IServiceProvider)Host; | |
EnvDTE.DTE dte = (EnvDTE.DTE)hostServiceProvider.GetService(typeof(EnvDTE.DTE)); | |
ProjectItem containingProjectItem = dte.Solution.FindProjectItem(Host.TemplateFile); | |
containingProjectItem.ProjectItems.AddFromFile(outputFilePath); | |
} | |
public string GetDataSourceName(string efContext) | |
{ | |
if (string.IsNullOrWhiteSpace(efContext)) throw new ArgumentNullException(nameof(efContext)); | |
return efContext | |
.Replace("Entities", "") | |
.Replace("DbContext", "") | |
.Replace("Db", "") | |
.Replace("Context", ""); | |
} | |
public void CreateFile(string fileName) | |
{ | |
if (string.IsNullOrWhiteSpace(fileName)) throw new ArgumentNullException(nameof(fileName)); | |
ProcessContent(fileName, this.GenerationEnvironment.ToString().TrimStart()); | |
this.GenerationEnvironment.Remove(0, this.GenerationEnvironment.Length); | |
} | |
//Get Projects | |
public Project GetActiveProject() | |
{ | |
Project activeProject = null; | |
Array activeSolutionProjects = this.DTE.ActiveSolutionProjects as Array; | |
if (activeSolutionProjects != null && activeSolutionProjects.Length > 0) | |
activeProject = activeSolutionProjects.GetValue(0) as Project; | |
return activeProject; | |
} | |
public List<CodeClass> FindClasses(Project project, string ns, string className) | |
{ | |
List<CodeClass> result = new List<CodeClass>(); | |
FindClasses(project.CodeModel.CodeElements, className, ns, result, false); | |
return result; | |
} | |
private void FindClasses(CodeElements elements, string className, string searchNamespace, List<CodeClass> result, bool isNamespaceOk) | |
{ | |
if (elements == null) return; | |
foreach (CodeElement element in elements) | |
{ | |
if (element is CodeNamespace) | |
{ | |
CodeNamespace ns = element as CodeNamespace; | |
if (ns != null) | |
{ | |
if (ns.FullName == searchNamespace) | |
FindClasses(ns.Members, className, searchNamespace, result, true); | |
else | |
FindClasses(ns.Members, className, searchNamespace, result, false); | |
} | |
} | |
else if (element is CodeClass && isNamespaceOk) | |
{ | |
CodeClass c = element as CodeClass; | |
if (c != null) | |
{ | |
if (c.FullName.Contains(className)) | |
result.Add(c); | |
FindClasses(c.Members, className, searchNamespace, result, true); | |
} | |
} | |
} | |
} | |
//Naming | |
public string GetProperClassName(string className) | |
{ | |
string returnString = className; | |
returnString = returnString.Replace(" ", "_"); | |
returnString = returnString.Replace("_", " "); | |
//TextInfo ti = CultureInfo.CurrentCulture.TextInfo; | |
//returnString = ti.ToTitleCase(returnString); | |
returnString = returnString.Replace(" ", ""); | |
return returnString; | |
} | |
private static string CharToUpper(string input, int position) | |
{ | |
return input.First().ToString().ToUpper() + input.Substring(position+1); | |
} | |
#> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment