简介
我用T4(文本模板转换工具箱)生成项目元数据,数据的实验以来,有几个明显的机会,以扩大其范围。一个这样的机会已经使用T4来生成静态属性代表的XAML键。这有助于减少的字符串常量的引用资源的依赖时。我后来增强了我的MetadataGeneration.tt的模板来做到这一点。X:关键属性生成
为了证明,我已经更新了我以前的文章中提供的示例应用程序,并雇佣了一对夫妇ResourceDictionaries展示我们如何可以参考"默认??字典使用的常量名,以及如何我们可以跨一个辅助基准ResourceDictionary的,压倒一切的资源使用的常量名值。
以下摘录中,我们看到一个按钮,使用资源,其关键是在生成的类的静态属性定义定义有其背景。<Button
Background="{StaticResource
{x:Static Keys:MainDictionaryXamlMetadata.ButtonBackgroundKey}}"
Margin="0,5,0,0" Content="Change"
HorizontalAlignment="Left" Click="Button_ChangeClick"/>
这是有用的,因为这意味着,如果我们修改了在ResourceDictionary的背景刷的名称和忘记更新引用它,我们将在编译时提醒,而不是在运行时。
MetadataGeneration.tt模板擦洗您的项目寻找XAML文件,然后为他们生成的类,包含所有的x:Key属性,静态属性表示。正如我们可以看到在下面的摘录,ButtonBackGround关键是定义为一个在MainDictionary.xaml LinearGradientBrush。MainDictionary.xaml
能够从另一个是有用的参考之一的ResourceDictionary。如果我们采取另一种的ResourceDictionary,重新定义的第一资源,我们能够做一个更安全的方式,表达我们的意图,一个专门的属性和使用非文字字符串键从MainDictionary.xaml派生的名称。SecondaryDictionary.xaml<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Metadata="clr-namespace:CSharpDesktopClrDemo.XamlMetadata.Folder1.Metadata">
<LinearGradientBrush x:Key=
"{x:Static Metadata:MainDictionaryXamlMetadata.ButtonBackgroundKey}">
<GradientStop Color="AliceBlue" Offset="0" />
<GradientStop Color="Blue" Offset=".7" />
</LinearGradientBrush>
<SolidColorBrush x:Key="{x:Static
Metadata:MainDictionaryXamlMetadata.WindowForegroundBrushKey}" Color="Azure"/>
</ResourceDictionary>
因此,我们可以定义我们的资源,无论我们喜欢;例如在一个单独的程序集,但我们仍然保留资源键引用的编译时间验证。App.xaml中
实施<Application x:Class="DanielVaughan.MetaGen.Demo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Window1.xaml">
<Application.Resources>
<ResourceDictionary Source="pack://application:,,,/
DanielVaughan.MetaGen.Demo;component/Folder1/MainDictionary.xaml"/>
<!--<ResourceDictionary Source="pack://application:,,,/
DanielVaughan.MetaGen.Demo;component/Folder1/SecondaryDictionary.xaml"/>-->
</Application.Resources>
</Application>
要完成的XAML文件和相关密钥的发现,以及随后生成的元数据类,在项目的遍历,我们必须做两件事:检测项目时,项目是一个XAML文件,并跟踪当前项目目录。现在完成第一是容易的。检测当前项目是一个项目文件夹时,另一方面,原来是值得下锅,你会发现在下面的摘录。string processingDirectory = string.Empty;
public void ProcessProjectItem(ProjectItem projectItem,
Dictionary<string, NamespaceBuilder> namespaceBuilders, string activeNamespace)
{
FileCodeModel fileCodeModel = projectItem.FileCodeModel;
if (fileCodeModel != null)
{
foreach (CodeElement codeElement in fileCodeModel.CodeElements)
{
WalkElements(codeElement, null, null, namespaceBuilders);
}
}
string activeNamespaceCopy = activeNamespace;
if (string.IsNullOrEmpty(activeNamespaceCopy))
{
if (string.IsNullOrEmpty(xamlRootNamespace))
{
activeNamespaceCopy = rootNamespace;
}
else
{
activeNamespaceCopy = string.Format("{0}.{1}",
rootNamespace, xamlRootNamespace);
}
}
if (projectItem.ProjectItems != null
&& projectItem.ProjectItems.Count > 0)
{
/* This is a hack to determine if we have a directory.
If you know the proper way for doing this, please let me know. */
try
{
var foo = projectItem.Document;
}
catch (Exception ex)
{
string newNamespace = projectItem.Name.Replace(" ", string.Empty);
activeNamespaceCopy += "." + newNamespace;
}
}
string itemName = projectItem.Name;
if (generateXamlKeys && itemName.EndsWith
(".xaml", true, CultureInfo.InvariantCulture))
{
/* Retrieve or create the namespace builder. */
NamespaceBuilder namespaceBuilder;
if (!namespaceBuilders.TryGetValue(activeNamespaceCopy, out namespaceBuilder))
{
namespaceBuilder = new NamespaceBuilder(activeNamespaceCopy, null, 0);
namespaceBuilders[activeNamespaceCopy] = namespaceBuilder;
}
string fileName = projectItem.get_FileNames(0);
string text = System.IO.File.ReadAllText(fileName);
MatchCollection matches = xClassRegex.Matches(text);
if (matches.Count > 0)
{
string xamlMetadataClassName = ConvertProjectItemNameToTypeOrMemberName
(itemName.Substring(0, itemName.Length - 4));
var classComments = new List<string>
{string.Format("/// <summary>Metadata for XAML {0}
</summary>", itemName)};
XamlBuilder xamlBuiler = new XamlBuilder(xamlMetadataClassName,
classComments, 1);
namespaceBuilder.AddChild(xamlBuiler);
foreach (Match match in matches)
{
Group keyGroup = match.Groups["KeyName"];
string keyName = keyGroup.Value;
var keyComments = new List<string>
{string.Format("/// <summary>Represents x:Key=\"{0}\"/>
</summary>", keyName)};
xamlBuiler.AddChild(new XamlKeyBuilder(keyName, keyComments));
}
}
}
if (projectItem.ProjectItems != null)
{
foreach (ProjectItem childItem in projectItem.ProjectItems)
{
ProcessProjectItem(childItem, namespaceBuilders, activeNamespaceCopy);
}
}
}
我们可以看到生成的XAML元数据类和接口的元数据生成相同的方式工作,我们所代表的XAML文件中使用XamlBuilder,并作为XamlKeyBuilders代表在XAML文件中的键。
生成XAML的元数据类的命名空间
为了避免与类型名称和生成的命名空间碰撞,我提供了一个可定制的xamlRootNamespace配置变量。这个变量是用来构造下面的例子说明的XAML生成的元数据类的命名空间名称:
如果我们有一个名为Window1.xaml中的XAML文件,它会被命名类代表generatedClassPrefix] Window1的[generatedXamlClassSuffix] [generatedClassSuffix]。结论
XAML资源键,通常使用魔术字符串引用,我们已经看到如何使用生成的类型和文件元数据,可以消除。
我什么是能够实现相结合,T4和DTE的仍然相当高兴。 Visual Studio 2010将看到T4移动到一个更醒目的位置,在IDE。这一点,连同在VS2010新功能的T4,必将使其不可缺少的工具。
要下载的模板源和演示应用程序,请访问{A}。