以编程方式将转换器添加到ResourceDictionary

时间:2017-06-21 18:33:35

标签: wpf xaml resourcedictionary staticresource converters

为了节省必须在每个控件的XAML的Resources部分中编写语法<converters:MyConverter x:Key="myConverter"/>我想将转换器用作静态资源,我有想法尝试在该控件的范围内添加每个转换器以编程方式访问其资源字典。

我所做的是创建自己的自定义ControlBase类,该类派生自UserControl,然后我将继承我的任何自定义控件,而不是UserControlControlBase负责将其自己的程序集和MediaLibrary中的任何资源(转换器,图像,模板选择器等)加载到其自身的Resources属性中。它还有一个名为LoadCallerResources(ResourceTypes resourceTypes)的受保护方法,它将派生控件中指定类型的任何资源加载到Resources字典中。

所有这些功能的核心方法如下。请注意,我在此代码中使用了几种自己的扩展方法和补充方法,例如ResourceTypes标志枚举中的“AsEnumerable”,“ThrowArgumentNullExceptionIfNull”,“ToCamelCase”和“GetResourceTypePlurals”用于获取每种资源类型的复数名称提供的枚举(对应于该类型的资源预期所在的目录)。

/// <summary>
/// Loads all .g.resources files found within the provided assembly that meet the requirements for the specified resource type into the <see cref="FrameworkElement.Resources"/> collection.
/// </summary>
/// <param name="resourceTypes">The type of data in the .g.resources files to load.</param>
/// <param name="resourceAssembly">The assembly to load the resources from.</param>
/// <exception cref="ArgumentNullException">Thrown if resourceAssembly is null.</exception>
private void LoadAssemblyResources(ResourceTypes resourceTypes, Assembly resourceAssembly)
{
    Stream manifestResourceStream = resourceAssembly
        .ThrowArgumentNullExceptionIfNull(nameof(resourceAssembly))
        .GetManifestResourceStream(resourceAssembly.GetName().Name + ".g.resources");

    if (manifestResourceStream != null)
        using (ResourceReader resourceReader = new ResourceReader(manifestResourceStream))
            foreach (DictionaryEntry relevantDictionaryEntry in resourceReader
                .Cast<DictionaryEntry>()
                .Where(dictionaryEntry =>
                {
                    string resourceFilePath = dictionaryEntry.Key.ToString();
                    string resourceDirectory = Path.GetDirectoryName(dictionaryEntry.Key.ToString());
                    string resourceFileName = Path.GetFileName(resourceFilePath);

                    return string.IsNullOrEmpty(resourceDirectory) && string.Equals(Path.GetExtension(resourceFileName), ".baml", StringComparison.OrdinalIgnoreCase) && resourceTypes.AsEnumerable().Any(resourceType => resourceFileName.StartsWith(Enum.GetName(typeof(ResourceTypes), resourceType), StringComparison.OrdinalIgnoreCase))
                        || GetResourceTypesPlurals(resourceTypes).Any(resourceTypePlural => resourceDirectory.Contains(resourceTypePlural, CompareInfo.GetCompareInfo(CultureInfo.InvariantCulture.Name), false));
                }))
                    Resources.Add(Path.GetFileNameWithoutExtension(relevantDictionaryEntry.Key.ToString()).ToCamelCase(), relevantDictionaryEntry.Value);
}

我对此代码有一些问题:

  1. 资源以全小写形式出现,而源文件以Pascal Case命名(我想将其转换为Camel Case)
  2. StaticResource绑定到转换器,即使拼写为所有-lowercase字体,也会给编译器错误“资源”myconvertername“无法解析。”
  3. 为了在.g.resources中找到转换器(.cs文件),必须将其构建操作从“编译”更改为“资源” - 这不意味着代码在使用时会有任何意义反正作为静态资源?
  4. 我的最终目标是能够在XAML中引用静态资源,而无需在控件的字典中明确声明它们。我该怎么做?

1 个答案:

答案 0 :(得分:2)

  
    

保存必须在XAML的Resources部分中编写语法

  

我喜欢这种情况的方法是让我的转换器实现MarkupExtension。这样做使得您不需要将其声明为Xaml中的资源。

  public class InvertedBooleanConverter : MarkupExtension, IValueConverter
  {
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
      return null; //put logic here
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
      return null; //put logic here
    }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
      return this;
    }
  }

然后您可以在xaml中使用它:

IsEnabled="{Binding someValue, Converter={yourNamespace:InvertedBooleanConverter}}"