C# MarkupExtensions、构造函数和Intellisense

C# MarkupExtensions、构造函数和Intellisense,c#,.net,wpf,xaml,markup-extensions,C#,.net,Wpf,Xaml,Markup Extensions,我正在尝试为本地化创建自己的MarkupExtension。其思想是将资源的名称(例如“Save”)传递给标记扩展,返回的是本地化值(例如en-US中的“Save”,de-de中的“speichen”,等等) 这很好,但我无法让它与intellisense一起工作 这是我的简化MarkupExtension类: public class MyMarkupExtension : MarkupExtension { private readonly string _input; p

我正在尝试为本地化创建自己的MarkupExtension。其思想是将资源的名称(例如“Save”)传递给标记扩展,返回的是本地化值(例如en-US中的“Save”,de-de中的“speichen”,等等)

这很好,但我无法让它与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中使用什么吗?这样标记扩展的输入将是文本字符串(“Save”,在我的示例中是一个资源名称,而不是本地化的值),并且intellisense可以工作?

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

现在,您可能会认为要用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");
        }#>
    }
}
首先需要做的只有一件事,那就是将资源文件的自定义工具切换到属性窗格中的
PublicResXFileCodeGenerator
。这将确保资源类和属性具有公共可见性,而不是内部可见性,这是
x:Static
工作所需的

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

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

拯救
然后你可以像这样使用它

internal static string Save {
    get {
        return ResourceManager.GetString("Save", resourceCulture);
    }
}
<TextBox Text="{m:MyMarkup {x:StaticResource Save}}">

您肯定会得到intellisense支持。Intellisense将为您搜索字符串类型对象保留的所有资源键


第二种方法是更改标记扩展的实现以直接处理资源字符串。但这取决于您如何定义资源字符串,我不能给出任何进一步的建议。

您所说的“让它工作”是什么意思?它现在怎么不起作用?@grek40:“让智能感知工作”——现在我必须知道资源的名称,例如“Save”。我也可以输入错误并编写“Svae”,并且不会得到任何编译器错误。但是,如果您使用x:Static,那么我可以编写'properties',intellisense会自动提供所有可能的值,包括'Save'。最简单的解决方案当然是简单地设置
Text=“{x:Static properties:Resources.Save}”
。这可以通过一些策略性的命名来缩短,但是定制标记扩展似乎完全没有必要。有关详细信息,请参阅我的答案。另外,不管你喜欢哪种解决方案,你都应该在奖金到期之前奖励某人。如果你想实现本地化,为什么不使用已经发明的东西呢?
<TextBox Text="{x:Static p:SR.Save}" />
<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 -->
internal static string Save {
    get {
        return ResourceManager.GetString("Save", resourceCulture);
    }
}
<ResourceDictionary xmlns:sys="clr-namespace:System;assembly=mscorlib">
    <sys:String x:Key="Save">Save</sys:String>
</ResourceDictionary>
<TextBox Text="{m:MyMarkup {x:StaticResource Save}}">