2
votes

I have a re-usable usercontrol with a viewmodel behind it. I'm trying to switch between different views of the same data. Currently trying to use a Mode property on the VM to accomplish this.

I've created a DataTemplateSelector like so:

<UserControl x:Class="MyUserControl">
    <UserControl.Resources>
        <DataTemplate x:Key="ColumnTemplate">
            <StackPanel>
                <Label Text="{Binding Name}"></Label>
                <Label Text="{Binding Address}"></Label>
                <Label Text="{Binding Occupation}"></Label>
            </StackPanel>
        </DataTemplate>
        <DataTemplate x:Key="AvatarTemplate">
            <StackPanel>
                <Image Source="{Binding ProfilePicture}"></Image>
                <Label Text="{Binding Name}"></Label>
            </StackPanel>
        </DataTemplate>
    <local:DisplayTemplateSelector ColumnTemplate="{StaticResource ColumnTemplate}" AvatarTemplate="{StaticResource AvatarTemplate}" x:Key="displayTemplateSelector" />
</UserControl.Resources>
<Grid>
    <ContentControl Name="cpDisplay" Content="{Binding}" ContentTemplateSelector="{StaticResource displayTemplateSelector}" />
</Grid>
</UserControl>

With the class:

class DisplayTemplateSelector : DataTemplateSelector
{

    public DataTemplate ColumnTemplate {get;set;}

    public DataTemplate AvatarTemplate {get;set;}

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        MainViewModel vm = (MainViewModel)item;

        switch (vm.Mode)
        {
            case MainViewModel.DisplayMode.Column:
                return ColumnTemplate;
            case MainViewModel.DisplayMode.Avatar:
                return AvatarTemplate;
            default:
                return AvatarTemplate;
        }
    }

}

This usercontrol sits in MyWindow:

<Grid>
    <controls:MyUserControl x:Name="MyUserControl" DataContext="{Binding}" Margin="0"/>
</Grid>

Which is instantiated with my viewmodel:

MyWindow w = new MyWindow(_vm);
w.Show();

The problem I have is that item is null during MainViewModel vm = (MainViewModel)item. It's like I'm trying to set the datatemplate based on data, before the data is bound?

Is there anyway to choose the desired datatemplate not based on the dataobject - but as a property or similar on the usercontrol?

3

3 Answers

2
votes

There are many ways, but here are a couple:

<!-- assumes you have a data template selector implementation available as resource MyContentSelector -->
<ContentControl Content="{StaticResource MainViewModel}" ContentTemplateSelector="{StaticResource MyContentSelector}"/>

or:

<!-- assumes you have appropriate boolean properties on your VM -->
<Grid>
    <ContentControl Content="{StaticResource MainViewModel}" ContentTemplate="{StaticResource column}" Visibility="{Binding IsColumnVisible, Converter={StaticResource BooleanToVisibilityConverter}}"/>
    <ContentControl Content="{StaticResource MainViewModel}" ContentTemplate="{StaticResource avatar}" Visibility="{Binding IsAvatarVisible, Converter={StaticResource BooleanToVisibilityConverter}}"/>
</Grid>
0
votes
0
votes

Ok, finally got this to work how I needed by using a property on the usercontrol and some code behind:

    public enum DisplayMode
    {
        Column,
        Avatar
    }

    public DisplayMode Mode { get; set; }


    public MyUserControl()
    {
        InitializeComponent();
        Mode = DisplayMode.Avatar;
    }

    private void MyUserControl_Loaded(object sender, RoutedEventArgs e)
    {
        switch (Mode)
        {
            case DisplayMode.Column:
                cpDisplay.ContentTemplate = (DataTemplate)this.Resources["ColumnTemplate"];
                cpDisplay.ApplyTemplate();
                break;
            case DisplayMode.Avatar:
                cpDisplay.ContentTemplate = (DataTemplate)this.Resources["AvatarTemplate"];
                cpDisplay.ApplyTemplate();
                break;
        }
    }

I removed the DataTemplateSelector code and simply defined the datatemplates and used:

    <ContentPresenter Name="cpDisplay" Content="{Binding}" />