我有一个控件,我需要根据各种条件设置数据模板,所以我决定使用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?
}
}
}
}
我很确定我这样做是错误的,但如何正确地做到这一点?出于各种原因,我不想从某些硬编码的静态位置访问资源。我真的需要在分配给它的容器中找到它们。
我知道问题很混乱,所以请随意提出,我会尽力澄清。
答案 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,以便模板即时更新。