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

时间:2013-07-02 17:20:58

标签: c# wpf xaml

我有一个控件,我需要根据各种条件设置数据模板,所以我决定使用DataTemplateSelector从其分配的控件资源中选择模板。

这是有效的,但这是一个问题:我正在从文件重新加载这些资源(当文件系统发生更改时),我需要使用新模板更新已经渲染的控件。如果我只使用DynamicResource而不是选择器,那么这将有效。

Selector看起来像这样:

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?
      }
    }
  }
}

我很确定我这样做是错误的,但如何正确地做到这一点?出于各种原因,我不想从某些硬编码的静态位置访问资源。我真的需要在分配给它的容器中找到它们。

我知道问题很混乱,所以请随意提出,我会尽力澄清。

1 个答案:

答案 0 :(得分: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,以便模板即时更新。