none
switch items DataTemplate

    Question

  • Say I have the following 2 DataTemplates defined -

        <DataTemplate x:Key="DataTemplateA">
          <StackPanel Orientation="Horizontal">
            <Button>swap</Button>
            <TextBlock Text="this is template a"/>
          </StackPanel>
        </DataTemplate>
       
        <DataTemplate x:Key="DataTemplateB">
          <StackPanel Orientation="Horizontal">
            <Button>swap</Button>
            <TextBlock Text="this is template b"/>
          </StackPanel>
        </DataTemplate>

    Is it possible to make the button 'swap' one DataTemplate for another? I want the user to be able to hit a button to change the rendering of an Item in an ItemsControl but I only want to switch that item - not the entire ItemsControl template. Do I need to make a single DataTemplate that internally switches between the 2 modes via a property of the Item itself?


    Thursday, August 30, 2007 8:48 PM

Answers

  • Good point. The property on the item is fine for originally selecting the template, but not for invalidating the template.  You could do that in code, but then what's the point?

     

    Sorry for the bum steer.  A much better approach is to have a single DataTemplate and do something like this:

     

    Code Snippet

     

    <DataTemplate>
      <Control x:Name="theControl" IsFocusable="False"
          Template="{StaticResource TemplateA}" />
      <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding Path=UseTemplateB}" Value="True">
          <Setter TargetName="theControl" Property="Template"
              Value="{StaticResource TemplateB}" />
        </DataTrigger>
      </DataTemplate.Triggers>
    </DataTemplate>


     

     

    For this to work, you will need to change TemplateA and TemplateB from data templates to control templates:

     

    Code Snippet

     

        <ControlTemplate x:Key="TemplateA" TargetType="{x:Type Control}">
          <StackPanel Orientation="Horizontal">
            <Button>swap</Button>
            <TextBlock Text="this is template a"/>
          </StackPanel>
        </ControlTemplate>
        
        <ControlTemplate x:Key="TemplateB" TargetType="{x:Type Control}">
          <StackPanel Orientation="Horizontal">
            <Button>swap</Button>
            <TextBlock Text="this is template b"/>
          </StackPanel>
        </ControlTemplate>

     

     

    Thursday, August 30, 2007 10:39 PM

All replies

  • Check out the ItemTemplateSelector property on ItemsControl.  There's a sample in the docs.

     

    As you have suggested, adding a property to the item will make the work of the template selector much easier.  But it's not a requirement.  You could just as easily maintain a collection of the items that should use DataTemplateB.  If the item is in the collection, you return B; otherwise return A.

     

    Thursday, August 30, 2007 9:20 PM
  • Thanks. If I change a property on my data which would then require a different DataTemplate for that item, how do I invalidate the current DataTemplate so my ItemTemplateSelector gets called again? Just changing the property and calling PropertyChanged() doesn't seem to be enought.
    Thursday, August 30, 2007 9:59 PM
  • Good point. The property on the item is fine for originally selecting the template, but not for invalidating the template.  You could do that in code, but then what's the point?

     

    Sorry for the bum steer.  A much better approach is to have a single DataTemplate and do something like this:

     

    Code Snippet

     

    <DataTemplate>
      <Control x:Name="theControl" IsFocusable="False"
          Template="{StaticResource TemplateA}" />
      <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding Path=UseTemplateB}" Value="True">
          <Setter TargetName="theControl" Property="Template"
              Value="{StaticResource TemplateB}" />
        </DataTrigger>
      </DataTemplate.Triggers>
    </DataTemplate>


     

     

    For this to work, you will need to change TemplateA and TemplateB from data templates to control templates:

     

    Code Snippet

     

        <ControlTemplate x:Key="TemplateA" TargetType="{x:Type Control}">
          <StackPanel Orientation="Horizontal">
            <Button>swap</Button>
            <TextBlock Text="this is template a"/>
          </StackPanel>
        </ControlTemplate>
        
        <ControlTemplate x:Key="TemplateB" TargetType="{x:Type Control}">
          <StackPanel Orientation="Horizontal">
            <Button>swap</Button>
            <TextBlock Text="this is template b"/>
          </StackPanel>
        </ControlTemplate>

     

     

    Thursday, August 30, 2007 10:39 PM
  • That works perfectly. One more question - is there a way from inside the ControlTemplate to set a property on the object the ControlTemplate is bound to? Right now I have a MouseDown handler on the StackPanel and then inside the handler of that I get the DataContext, cast it to the right type and set my property. Of course I'd like to do this all from within XAML since it seems like it's just a presentation issue.
    Thursday, August 30, 2007 11:05 PM
  • No, you can only set properties on elements within the template tree. 

     

    You could use a two-way binding between a property of the control and the data item, but you are wanting to toggle the property when an event occurs.  Unfortunately, you can't use XAML to make the logical comparison: "If property is true, set it to false; otherwise, set it to true."  That requires code.

     

    You're correct that it is mainly a presentation issue, but when you're dealing with user interaction that requires logic, you have to drop to code.

     

    Of course, if you made your button a toggle button and moved it out of the control template and into the data template, you could do something like the following.  In this case, the toggle button is now tracking state for you.

     

    Code Snippet

     

        <ControlTemplate x:Key="TemplateA" TargetType="{x:Type Control}">
          <StackPanel Orientation="Horizontal">
            <TextBlock Text="this is template a"/>
          </StackPanel>
        </ControlTemplate>
       
        <ControlTemplate x:Key="TemplateB" TargetType="{x:Type Control}">
          <StackPanel Orientation="Horizontal">
            <TextBlock Text="this is template b"/>
          </StackPanel>
        </ControlTemplate>

     

        <DataTemplate x:Key="MyItemTemplate">
          <StackPanel Orientation="Horizontal">
            <ToggleButton x:Name="TemplateSelector">swap</ToggleButton>
            <Control x:Name="theControl" Focusable="False"
                Template="{StaticResource TemplateA}" />
          </StackPanel>
          <DataTemplate.Triggers>
            <Trigger SourceName="TemplateSelector" Property="IsChecked"
                 Value="True">
              <Setter TargetName="theControl" Property="Template"
                  Value="{StaticResource TemplateB}" />
            </Trigger>
          </DataTemplate.Triggers>
        </DataTemplate>

     

     

    Of course, if you don't want the toggled appearance, you would need to retemplate the toggle button to look like a regular button.  Basically, you'd have to copy the default ToggleButton style and remove the IsChecked trigger.
    Friday, August 31, 2007 12:20 AM
  • I also found setting the ItemTemplateSelector to null and then back to the DataTemplateSelector forces a refresh of the listbox. 

    1// The following code forces a refresh of the item's template using the 
    2// DataTemplateSelector.  Call after you change a property that would affect the  
    3// selected data template. 
    4DataTemplateSelector tempSel = ListBox1.ItemTemplateSelector; 
    5ListBox1.ItemTemplateSelector = null
    6ListBox1.ItemTemplateSelector = tempSel; 

    Wednesday, July 09, 2008 12:51 AM
  • Hi

    This Solution is for  those who are searching for Template swap.

    It is simple hope it helps you. Please point out any mistakes.

    Just use this code for changing the Template on Button Click.

         private void GridButton_Click(object sender, RoutedEventArgs e)
            {
                DataTemplate Temp;
                Temp = (DataTemplate)this.FindResource("TemplateYouHaveCreated");
                listView1.ItemTemplate = Temp;
            }

    refer this link

    http://developingfor.net/2009/01/09/dynamically-switch-wpf-datatemplate/

    Friday, January 10, 2014 6:36 PM