从非WPF代码访问WPF XAML资源

7
我目前正在构建一个应用程序,它由几个组件组成,每个组件本质上都是一个WPF用户控件,周围包含一些C#代码以使插件系统工作(使用MEF)。
我的问题是,每个组件都应该包含一个图标,并且为了美观起见,我将其定义为System.Windows.Media.Brush,因此我可以在那里使用从Design导出的DrawingBrush。现在我需要从非WPF C#中访问那段XAML,我目前的可怕解决方法是实例化用户控件并请求其资源:
private Brush CachedIcon = null;

public override Brush Icon
{
    get
    {
        if (CachedIcon == null)
        {
            CachedIcon = (Brush)(new BlahControl().TryFindResource("Icon"));
        }
        return CachedIcon;
    }
}

我找不到一种方法来读取那个资源(一个.xaml文件,并在自定义控件的ResourceDictionary中引用),在“普通”的C#类中。任何属于WPF的东西都有一个很好的TryFindResource方法,但是如果没有这个方法该怎么办呢?我不想让带有图标的XAML文件悬而未决地留在外面。

4个回答

2
在您的XAML代码中,请确保图标资源的构建选项设置为“Resource”,然后引用该资源,使其成为XAML静态资源。
<UserControl.Resources>
    <BitmapImage x:Key="icon1" UriSource="Resources/Icon1.ico" />
</UserControl.Resources>

在您的.NET 2.0代码中,您会在"{xamlName}.g.resource"流中找到该资源。

下面是加载XAML DLL中所有图标到字典中的示例代码:

using System.IO;
using System.Reflection;
using System.Collections;
using System.Resources;

...

var icons = new Dictionary<String, Bitmap>();
var externalBaml = Assembly.LoadFile(Path.Combine(Environment.CurrentDirectory, "MyXaml.dll"));
Stream resourceStream = externalBaml.GetManifestResourceStream(externalBaml.GetName().Name + ".g.resources");
using (ResourceReader resourceReader = new ResourceReader(resourceStream)) {
    foreach (DictionaryEntry resourceEntry in resourceReader) {
        if (resourceEntry.Key.ToString().ToUpper().EndsWith(".ICO")) {
            icons.Add(resourceEntry.Key.ToString(), Image.FromStream(resourceEntry.Value as Stream) as Bitmap);
        }
    }
}

似乎可以工作,但看起来非常hackish。依赖于自动生成资源的内部名称对我来说似乎不是很明智。 - Joey
额?资源有字符串名称,你无法改变这个事实!你在寻找什么? - TFD
我只是在想“.g.resources”这个东西会有多可靠。我已经读了足够多的Raymond Chen的博客,所以我不想使用任何可能因为实现方式而破坏的东西,因为它并不是文档化的行为。 - Joey
这是当前WPF标准的一部分。在WPF 4中可能会发生变化,但其他所有内容也可能会发生变化!您可以使用?externalBaml.GetManifestResourceNames()来解析所有资源字典。 - TFD

1

我的建议是:

  • 在控件上提供有关图标位置的元数据。您可以使用自己的自定义属性来实现这一点(请参见下面的示例1)。这些元数据将允许您在不创建控件实例的情况下加载图标。

  • 由于您正在使用MEF,因此可以在导出中使用元数据来实现与上述相同的功能。详情请参见此处。请参见下面的示例2。

  • 将您的图标视为ImageSource而不是Brush。您可以使用WPF的Image控件显示您的ImageSource,或者您可以使用ImageBrush进行绘制。

  • 使用TFD提供的技术读取元数据中指定名称的资源。不幸的是,WPF似乎没有提供类似于BamlReader的东西,这将使从非WPF上下文加载WPF资源变得更加清晰。

示例1:

[Icon("MyIconResourceName")]
public class BlahControl : Control
{
    ...
}

例子2:

[Export(typeof(IApplicationComponent))]
[ExportMetadata("IconResource", "MyIconResourceName")]
public class BlahControl : Control
{
    ...
}

0

嗯,现在无法使其正常工作,稍后再试。但这并不完全是解决问题的明智方案。然而,显然将XAML文件的构建操作设置为资源而不是页面时,它们会被嵌入为XAML。也许那样会是更好的选择。我会尝试一下。 - Joey

0
在应用程序级别中定义图标,而不是在控件中进行定义。可以通过在app.xaml文件或主资源字典xaml文件中定义,然后使用相同的TryFindResource方法,但不需要创建控件实例。

由于该项目不是一个WPF应用程序,只是用户控件,因此没有App.xaml。那么在只有ResourceDict的情况下,我应该在哪里调用TryFindResource?图标已经驻留在其中一个中。 - Joey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接