如何在MVVM视图模型中指定资源?

12

假设我想展示一个对象列表,其中每个对象都应该有一个名称和一个合适的图片(例如带图标的菜单项或带文本和图像的按钮)。

所有的例子和程序都将图像在viewmodel中展示为PNG文件的路径,然后将ImageSource绑定到它。但是如果我想使用矢量图像(例如作为本地ResourceDictionary中的DrawingImage),怎么办呢?从viewmodel中公开DrawingImage似乎不好,因为我必须存储对应用程序/窗口/用户控件的引用(而且建议不要从视图模型中公开此类XAML对象)。

因此,更好的方法是在viewmodel中使用字符串标识符,然后选择适当的资源。如果该标识符是资源键,则以下代码片段看起来很有吸引力,但它无法工作:

<Image Source="{StaticResource {Binding Icon}}"/>

我找到了两种解决方法,尽管它们对我没有起作用。

  1. 第一种方法是使用一个转换器,在图标上进行普通的绑定,并在 Application.Current 中查找资源。我认为如果资源存储在其他地方,则此方法不起作用(最初遇到此问题的情况是由窗口选择启动应用程序!)。

  2. 第二种解决办法 是使用从 StaticResourceExtension 派生出来的标记扩展来获取其通过绑定传递的 ResourceKey

<Image Source="{local:BindableStaticResource {Binding Icon}"/>

这个看起来很不错,因为它可以使用本地资源,也可以用于其他事情。但是当我使用它时,总是会出现异常(“无法找到名为{FooIcon}的资源”,显示了正确的XAML文件和扩展的位置)。甚至仅从StaticResourceExtension派生的空资源扩展,只是将资源键传递给基础构造函数也没有起作用,我无法解释原因。直接使用StaticResourceExtension就可以正常工作。

有什么想法可以修复第二种方法,或者更好的解决方案吗?

编辑

我注意到直接像这样使用它是可以的:

<Window>
    <Window.Resources>
        <DrawingImage x:Key="SomeIcon"/>
    </Window.Resources>
    <Image Source="{BindableStaticResource {Binding Icon}}"/>
</Window>

但是在DataTemplate中失败了。尽管正常的StaticResourceExtension在这两种情况下都可以工作,所以我很困惑出了什么问题。

2个回答

3
你提到的第一个解决方法可以在这里找到:将数据上下文字符串属性绑定到静态资源键
我尝试使用你提到的第二个解决方法(http://sweux.com/blogs/psampaio/index.php/2009/06/16/using-data-binding-with-static-resources-in-wpf/),但我从未成功过。它会抛出一个 ArgumentNullException,因为数据上下文为空。我认为这可能与我正在使用 DataTemplate 从 ViewModel 创建我的 View 有关,而某种方式 DataContext 在 ProvideValue 方法被调用之前没有设置(在那个页面的示例中,DataContext 是在 .xaml.vb 类中设置的)。
所以,我开始寻找解决方法,并找到了一种也涉及转换器的解决方法,但是这个解决方法通过 FrameworkElement 方法而不是在 Application.Current 中查找资源。我找到的解决方法详见下面: http://drwpf.com/blog/2007/08/18/can-my-value-converter-access-the-target-of-the-binding/ 我会在这里复制相关信息:
它涉及实现 IMultiValueConverter 接口的 ValueConverter,以便访问设置绑定的控件。
Convert 方法的代码如下:
public object Convert(object[] values, Type targetType, 
    object parameter, CultureInfo culture)
{
    FrameworkElement targetObject = values[0] as FrameworkElement;

    if (targetObject == null)
    {
        return DependencyProperty.UnsetValue;
    }
    return targetObject.TryFindResource(values[1]);
}

一个内容控件的XAML如下:

<ContentControl>
  <ContentControl.Content>
    <MultiBinding Converter="{StaticResource Converter}">
      <MultiBinding.Bindings>
        <Binding RelativeSource="{RelativeSource Self}" />
        <Binding Path="ResourceKey" />
      </MultiBinding.Bindings>
    </MultiBinding>
  </ContentControl.Content>
</ContentControl>

一个图像的XAML如下:

<Image Height="16" Width="16">
    <Image.Source>
        <MultiBinding Converter="{StaticResource Converter}">
            <MultiBinding.Bindings>
                <Binding RelativeSource="{RelativeSource Self}" />
                <Binding Path="ResourceKey" />
            </MultiBinding.Bindings>
        </MultiBinding>
    </Image.Source>
</Image>

运作得像魔法一样 :D

1

自定义MarkupExtensions中存在一个错误,不允许您像那样在属性中使用它们。

解决方法1:将属性声明为元素,如下所示:

<Image>
    <Image.Source>
        <local:BindableStaticResource Binding={Binding Icon}" />
    </Image.Source>
</Image>

解决方法2:如果您通过将其放入另一个程序集并引用它来预编译MarkupExtension,则它将再次正常工作。这可能是您在主窗口中看到它正常工作,但在DataTemplate中未能正常工作的原因。


我已经调查了你所提到的错误,但我发现唯一的问题是编译时错误。这在这里不是问题,代码可以编译通过,但在运行时找不到资源。将自定义的MarkupExtension从属性改为元素也无法解决这个问题。 - gix

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