MarkupExtensions, Constructor and Intellisense



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

这很好用,但我无法让它与智能感知一起工作。

这是我简化的 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",这是一个资源名称,而不是本地化值)并且智能感知可以工作吗?

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

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("rntt");
#>public static readonly LocResourceKey <#= enumerator.Key #> = new LocResourceKey("<#= enumerator.Key #>");<#              
}
Write("rn");
}#>
}
}

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

毕竟,您可以以类型安全的方式使用您的密钥,如果您键入错误的内容,则可以借助智能感知和可见错误:

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

关于您的第一个,没有简单的方法(直接方法)可以使 Intellisense 支持自定义标记扩展作为内置扩展。如果需要智能感知显示资源名称,则必须编写 VS 扩展来执行搜索并为智能感知提供结果。在我看来,这不是一件容易的事情。如果您真的想尝试一下,演练:显示语句完成可能是您的开始。

<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("Save",resourceCulture)。实际上,自动生成的 Resources.Save 代码就是这样。

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}}">

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

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

<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 -->

简单地用{x:Static p:Resources.Save}替换{m:MyMarkup Save}有什么问题吗? 这应该是等效的,它将为你提供开箱即用的智能感知支持。

除了增加一点冗长之外,我能看到的唯一区别是您调用GetString(name)而不是GetString(name, resourceCulture),但默认情况下resourceCulture为 null,因此应该没有区别。

请注意,包括Microsoft在内的一些商店使用缩写名称SR("字符串资源"的缩写)代替Resources,因此您可以从他们的书中获取一页并稍微缩短标记:

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

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

最新更新