返回首页

简介
用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}。

回答