3

I have a control in which I need to set data template based on various conditions so I decided to use a DataTemplateSelector which selects templates from resources of the control its being assigned to.

This works, but here is a catch: I am reloading these resources from file (when there is file system change) and I need to update already rendered controls with the new template. This would work if I simply used DynamicResource instead of selector.

Selector looks something like this:

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

So if the resources change, the selector is never reevaluated as it would be if I used DynamicResource.

I had an idea to solve this: select the template in ViewModel, so that when resources change, I can update my DataTemplate property.

My attempt of ViewModel (simplified example, it implements INotifyPropertyChange properly):

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

I am pretty sure that I am doing this the wrong way, but how to do it properly? I don't want to access the resources from some hard-coded static location for various reasons. I really need to find them in the container it is being assigned to.

I know the question is confusing, so feel free to ask and I will try to clarify.

4

1 回答 1

2

因此,经过长时间尝试使用各种骇人听闻的方法来解决这个问题,事实证明这是一个非常容易解决的问题。

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

xml:

<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 以便模板即时更新。

于 2013-07-04T23:09:07.373 回答