Explicitly refresh DataTemplate from a DataTemplateSelector?

血红的双手。 提交于 2019-11-28 10:49:24

I'm not aware of any (non-kludgy) way to do this: the DataTemplateSelector is called when WPF needs to select the template, and that's a one-off decision as far as WPF is concerned. (You can kludge it by making WPF think the content has changed, e.g. by setting the content to null and then back again -- I think that would work but haven't tested it -- but this is pretty ugly!) If there is a nice way to do this I too would be interested to know!

However, there is an alternative way to change how content is displayed that does update in response to data changes, and that is through triggers. You can use DataTriggers in your DataTemplate.Triggers collection to show and hide elements depending on the content data. To change the entire display, you could e.g. set up two renderings in a Grid, and use triggers to control which one is visible. You could even make your data template a ContentControl, and use a trigger to change the ContentTemplate. Of course this depends on the criteria for changing the template being bindable properties, which may not always be the case.

Here's some brief discussion of selectors vs. triggers, albeit in a slightly different context.

Late to the party, I know. =)

When faced with this problem, I found it easiest to explicitly set a new TemplateSelector like

MyContentControl.ContentTemplateSelector =
     new MyDataTemplateSelector();
fraggy

I guess I am even later to the party, but for a different idea that may help someone...

You could also try using a ValueConverter on the ContentControls ContentTemplate property instead of a DataTemplateSelector.

Just have a property in your DataContext to bind, like ScreenNumber for example. Then in the ValueConverter return the DataTemplate that is associated with the ScreenNumber.

Example ValueConverter:

public class ValueDataTemplateConverter : IValueConverter
{
    public DataTemplate TemplateA { get; set; }
    public DataTemplate TemplateB { get; set; }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is ValueType valueType)
            switch (valueType)
            {
                case ValueType.TypeA:
                    return TemplateA;
                case ValueType.TypeB:
                    return TemplateB;
             }

        return null;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Example xaml resource:

<converters:ValueDataTemplateConverter x:Key="ValueDataTemplateConverter">
    <converters:ValueDataTemplateConverter.TemplateA>
        <DataTemplate>
            <TextBox Text="{Binding Value}" />
        </DataTemplate>
    </converters:ValueDataTemplateConverter.TemplateA>
    <converters:ValueDataTemplateConverter.TemplateB>
        <DataTemplate>
            <CheckBox IsChecked="{Binding Value}" />
        </DataTemplate>
    </converters:ValueDataTemplateConverter.TemplateB>
</converters:ValueDataTemplateConverter>

Similar to Jens answer, instead of creating a new instance , you can use the existing instance of the DataTemplateSelector.

var currentSelector = MyContentControl.ContentTemplateSelector;     
MyContentControl.ContentTemplateSelector = null;
MyContentControl.ContentTemplateSelector = currentSelector;
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!