MarkupExtensions,Constructor和Intellisense

时间:2018-01-17 08:19:09

标签: c# .net wpf xaml markup-extensions

我正在尝试创建自己的MarkupExtension以进行本地化。我们的想法是将资源名称(例如' Save')传递给标记扩展名,返回值将是本地化值(例如' Save' in en-US,& #39; Speichern'在de-de等等。)

这非常好用,但我无法使用intellisense。

这是我简化的MarkupExtension类:

public class MyMarkupExtension : MarkupExtension
{
    private readonly string _input;

    public MyMarkupExtension(string input)
    {
        _input = input;
    }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        // Here the actual value from the resources will be returned, for example for input 'Save':
        //  'Save' for Thread.CurrentThread.CurrentUICulture="en-US"
        //  'Speichern' for Thread.CurrentThread.CurrentUICulture="de-de"
        //  ...
        return Resources.ResourceManager.GetString(_input);
    }
}

和xaml:

    <TextBox Text="{m:MyMarkup Save}"></TextBox> <!-- No Intellisense, but it works. -->
    <TextBox Text="{m:MyMarkup {x:Static properties:Resources.Save}}"></TextBox> <!-- Intellisense works, but the input parameter for markup extension is already localized string -->

知道在xaml中使用什么,以便标记扩展的输入是文字字符串(&#39;在我的例子中保存&#39;这是一个资源名称,而不是本地化的值)并且intellisense会工作?

3 个答案:

答案 0 :(得分:2)

首先,您可以使用特殊类型而不是字符串,它将代表您的资源键。这样你就可以使你的扩展类型安全(不允许在那里传递任意字符串):

public class LocResourceKey {
    // constructor is private
    private LocResourceKey(string key) {
        Key = key;
    }

    public string Key { get; }
    // the only way to get an instance of this type is
    // through below properties
    public static readonly LocResourceKey Load = new LocResourceKey("Load");
    public static readonly LocResourceKey Save = new LocResourceKey("Save");
}

public class MyMarkupExtension : MarkupExtension {
    private readonly string _input;

    public MyMarkupExtension(LocResourceKey input) {
        _input = input.Key;
    }

    public override object ProvideValue(IServiceProvider serviceProvider) {
        return Resources.ResourceManager.GetString(_input);
    }
}

现在您可能认为使用resx文件中的所有资源键来维护此类需要付出很多努力,这是真的。但您可以使用T4模板为您生成它。例如:

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Windows.Forms" #>
<#@ output extension=".cs" #>

namespace WpfApplication1 {
    public class LocResourceKey {
        private LocResourceKey(string key) {
            Key = key;
        }

        public string Key { get; }  
        <#using (var reader = new System.Resources.ResXResourceReader(this.Host.ResolvePath("Properties\\Resources.resx"))) {
            var enumerator = reader.GetEnumerator();
            while (enumerator.MoveNext()) {
                Write("\r\n\t\t");
                #>public static readonly LocResourceKey <#= enumerator.Key #> = new LocResourceKey("<#= enumerator.Key #>");<#              
            }
            Write("\r\n");
        }#>
    }
}

此模板假设有&#34; Resources.resx&#34;文件在&#34;属性&#34;文件夹相对于模板本身(模板可以通过添加&gt;新项目&gt;文本模板)创建。运行时 - 它将检查resx文件中的所有资源并为您生成LocResourceKey类。

毕竟,如果输错了,你可以在类型安全的方式中使用你的密钥,在智能感知和可见错误的帮助下:

<TextBlock Text="{my:MyMarkup {x:Static my:LocResourceKey.Save}}" />

答案 1 :(得分:2)

<TextBox Text="{m:MyMarkup Save}"></TextBox> <!-- No Intellisense, but it works. -->

关于你的第一个,并不是简单的方法(直接方式)使intellisense支持自定义标记扩展作为内置扩展。如果您需要intellisense显示资源名称,则必须编写VS扩展来进行搜索并提供intellisense的结果。在我看来,这不是一件容易的事。如果您真的想尝试一下,Walkthrough: Displaying Statement Completion可能就是您的开始。

<TextBox Text="{m:MyMarkup {x:Static properties:Resources.Save}}"></TextBox> <!-- Intellisense works, but the input parameter for markup extension is already localized string -->

关于你的第二个,因为StaticExtension提供静态成员保持的值,所以你肯定得到了Resources.Save中包含的内容,它应该是ResourceManager.GetString(&#34; Save&#34;,resourceCulture)。实际上,Resources.Save的自动生成代码就是这样。

internal static string Save {
    get {
        return ResourceManager.GetString("Save", resourceCulture);
    }
}

修复它的第一种方法是编写一个提供资源名称的ResourceDictionary。

<ResourceDictionary xmlns:sys="clr-namespace:System;assembly=mscorlib">
    <sys:String x:Key="Save">Save</sys:String>
</ResourceDictionary>

然后你可以像这样使用它。

<TextBox Text="{m:MyMarkup {x:StaticResource Save}}">

你肯定会获得智能感知支持。 Intellisense将搜索由字符串类型对象保存的所有资源键。

第二种方法是更改​​标记扩展的工具以直接处理资源字符串。但这取决于您如何定义资源字符串,我无法提供任何进一步的建议。

答案 2 :(得分:1)

<TextBox Text="{m:MyMarkup Save}"></TextBox> <!-- No Intellisense, but it works. -->
<TextBox Text="{m:MyMarkup {x:Static properties:Resources.Save}}"></TextBox> <!-- Intellisense works, but the input parameter for markup extension is already localized string -->

{m:MyMarkup Save}替换为{x:Static p:Resources.Save}是否有问题?这个应该是等价的,它将为您提供开箱即用的智能感知支持。

除了一些额外的冗长之外,我能看到的唯一区别是你拨打GetString(name)而不是GetString(name, resourceCulture),但默认情况下resourceCulture为空,所以应该有没有区别。

请注意,某些商店(包括Microsoft)使用缩写名称SR(“string resource [s]”的缩写)代替Resources,因此您可以从他们的书中获取一页稍微缩短标记:

<TextBox Text="{x:Static p:SR.Save}" />

首先需要做的一件事就是将资源文件的自定义工具切换到属性窗格中的PublicResXFileCodeGenerator。这将确保资源类和属性具有公共可见性,而不是内部,您需要x:Static才能工作。