MarkupExtensions,Constructor和Intellisense

sve*_*vit 9 .net c# wpf xaml markup-extensions

我正在尝试创建自己的MarkupExtension以进行本地化.想法是将资源的名称(例如"保存")传递给标记扩展,返回将是本地化值(例如,en-US中的"Save",de-de中的"Speichern"等) .

这工作得很好,但是我无法使用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);
    }
}
Run Code Online (Sandbox Code Playgroud)

和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 -->
Run Code Online (Sandbox Code Playgroud)

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

Evk*_*Evk 3

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在您可能认为使用 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");
        }#>
    }
}
Run Code Online (Sandbox Code Playgroud)

此模板假设相对于模板本身的“Properties”文件夹下有“Resources.resx”文件(可以通过“添加”>“新项目”>“文本模板”创建模板)。运行时 - 它将检查 resx 文件中的所有资源并LocResourceKey为您生成类。

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

<TextBlock Text="{my:MyMarkup {x:Static my:LocResourceKey.Save}}" />
Run Code Online (Sandbox Code Playgroud)