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

本文关键字:设置 动态 资源 DataTemplate 选择器 | 更新日期: 2023-09-27 18:20:41

我有一个控件,需要根据各种条件设置数据模板,所以我决定使用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那样重新评估。

我有一个解决这个问题的想法:在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?
      }
    }
  }
}

我很确定我做这件事的方式不对,但如何正确地做呢?由于各种原因,我不想从一些硬编码的静态位置访问资源。我真的需要在它被分配到的容器中找到它们。

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

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

因此,经过长时间的尝试,使用各种技巧性的方法解决了这个问题,这是一个非常容易解决的问题。

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

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,这样模板就会动态更新。