将DataTemplate在模板选择器中设置为动态资源

3
我有一个控件,需要根据不同的条件设置数据模板,因此我决定使用DataTemplateSelector,从被分配的控件资源中选择模板。这样做是有效的,但有个问题:当文件系统发生更改并重新加载这些资源时,我需要使用新模板更新已经呈现的控件。如果我使用DynamicResource而不是选择器,则可以实现此功能。 选择器大致如下:
public override DataTemplate SelectTemplate(object item, DependencyObject container) {
  //complex rules that select the template are here
  //this unfortunately sets the template statically - if it changes, it won't get updated
  return template;
}

如果资源发生变化,使用DynamicResource会自动重新评估选择器,但是如果不使用DynamicResource,选择器将永远不会被重新评估。

我有一个解决办法:在ViewModel中选择模板,这样当资源发生变化时,我可以更新DataTemplate属性。

以下是我的ViewModel尝试(简化示例,已正确实现INotifyPropertyChange):

class MyViewModel {
  public DataTemplate DataTemplate {get;set;}

  public MyModel Model {
    get {return _model;}
    set {
      if(_model != value) {
        _model = value;
        //Select new template here
        //DUH: how do I access the resources as I would in DataTemplateSelector, when I don't have access to the container parameter?
      }
    }
  }
}

我相信我正在错误地进行这个操作,但正确的方法是什么?出于各种原因,我不想从一些硬编码的静态位置访问资源。我真的需要在它被分配到的容器中找到它们。

我知道我的问题很令人困惑,所以请随意提问,我会尽力澄清。

1个回答

2

经过长时间使用各种hackish方法来尝试解决这个问题后,结果发现这是一个非常容易解决的问题。

我们在视图模型中设置我们的数据模板(实际上只是数据模板的关键),然后在简单的附加属性中应用该模板。

XAML:

<ContentControl Content="{Binding Content}" local:ContentTemplate.ContentTemplateKey="{Binding TemplateKey}">
    <!-- Some other stuff -->
</ContentControl>

附加属性:

public static class ContentTemplate
{

    public static object GetContentTemplateKey(DependencyObject obj)
    {
        return (object)obj.GetValue(ContentTemplateKeyProperty);
    }

    public static void SetContentTemplateKey(DependencyObject obj, object value)
    {
        obj.SetValue(ContentTemplateKeyProperty, value);
    }

    public static readonly DependencyProperty ContentTemplateKeyProperty = DependencyProperty.RegisterAttached("ContentTemplateKey", typeof(object), typeof(ContentTemplate), new UIPropertyMetadata(null, OnContentTemplateKeyChanged));

    private static void OnContentTemplateKeyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var key = e.NewValue;

        var element = d as FrameworkElement;
        if (element == null)
            return;

        element.SetResourceReference(ContentControl.ContentTemplateProperty, key);
    }
}

如果资源使用x:Key="ResourceName",则需要绑定对象:

new
{
    Content = something,
    TemplateKey = "ResourceName",
}

如果资源使用 TargetType="{x:Type Person}",则绑定对象如下:

new
{
    Content = something,
    TemplateKey = new DataTemplateKey(typeof(Person)),
}

当然,绑定对象应该实现INotifyPropertyChange接口,这样模板可以实时更新。

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