Saturday 5 July 2008

Selecting the Detail Level to View at Runtime in WPF - An alternate way?

I recently read Josh Smith's post on codeproject which explained how to use a Slider control to dynamically apply a WPF data template a runtime. If you haven't read it, I suggest you read it before continuing.

So I have come up with an alternative solution which is done in pure XAML. This is a technique which I have used on previous projects and it involves creating a "surrogate" data template which simply passes control to another data template via a content presenter. Because a data template is used to do this, it has access to the inheritance context so does not require any freezable hacks to find the slider or other data templates.

Note that in order to make this a pure xaml solution I replaced the data source (which was originally in code) with an XmlDataProvider nested in the XAML document. Hence that lovely Pam girl does not appear in my version, which will probably disappoint most people...

Anyway check it out below, simple copy and paste in XamlPad/Kaxaml to see it working!

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <DockPanel>  
    <DockPanel.Resources>
    
      <XmlDataProvider x:Key="Data" XPath="/people">
        <x:XData>
          <people xmlns="">
            <person name="Neil" age="29" gender="M" />
            <person name="Jane" age="40" gender="F" />
            <person name="Jack" age="23" gender="M" />
          </people>
        </x:XData>
      </XmlDataProvider>        
    
      <DataTemplate x:Key="LowTemplate">
        <TextBlock Text="{Binding XPath=@name}" />
      </DataTemplate>
    
      <DataTemplate x:Key="MediumTemplate">
        <TextBlock>
            <TextBlock Text="{Binding XPath=@name}" />
            <Run>(</Run>
            <TextBlock Text="{Binding XPath=@age}" Margin="-4,0" />
            <Run>)</Run>
          </TextBlock>
      </DataTemplate>
    
      <DataTemplate x:Key="HighTemplate">
        <TextBlock>
          <TextBlock Text="{Binding XPath=@name}" />
          <Run>(</Run>
          <TextBlock Text="{Binding XPath=@age}" Margin="-4,0" />
          <Run>) -</Run>
          <TextBlock Text="{Binding XPath=@gender}" />
        </TextBlock>
      </DataTemplate>
      
      <DataTemplate x:Key="SelectorTemplate">
        <Grid>
          <ContentPresenter x:Name="lowPresenter" 
                Content="{Binding}" ContentTemplate="{StaticResource LowTemplate}" 
                Visibility="Collapsed" />
          <ContentPresenter x:Name="mediumPresenter" 
                Content="{Binding}" ContentTemplate="{StaticResource MediumTemplate}" 
                Visibility="Collapsed" />
          <ContentPresenter x:Name="highPresenter" 
                Content="{Binding}" ContentTemplate="{StaticResource HighTemplate}" 
                Visibility="Collapsed" />
        </Grid>
        <DataTemplate.Triggers>
          <DataTrigger 
                Binding="{Binding ElementName=detailLevelSlider, Path=Value}" 
                Value="1">
            <Setter TargetName="lowPresenter" 
                       Property="Visibility" 
                       Value="Visible" />
          </DataTrigger>
          <DataTrigger 
                Binding="{Binding ElementName=detailLevelSlider, Path=Value}" 
                Value="2">
            <Setter TargetName="mediumPresenter" 
                       Property="Visibility" 
                       Value="Visible" />
          </DataTrigger>
          <DataTrigger 
                Binding="{Binding ElementName=detailLevelSlider, Path=Value}" 
                Value="3">
            <Setter TargetName="highPresenter" 
                       Property="Visibility" 
                       Value="Visible" />
          </DataTrigger>
        </DataTemplate.Triggers>
      </DataTemplate>
      
    </DockPanel.Resources>
      
    <StackPanel 
      DockPanel.Dock="Bottom" 
      Background="LightGray"
      Margin="4" 
      Orientation="Horizontal"
      >
      <TextBlock 
        Margin="2,0,4,0" 
        Text="Detail Level:" 
        VerticalAlignment="Center" 
        />
      <Slider 
        x:Name="detailLevelSlider"
        DockPanel.Dock="Bottom" 
        Minimum="1" Maximum="3" 
        SmallChange="1" LargeChange="1" 
        IsSnapToTickEnabled="True" TickFrequency="1"
        Value="0" 
        Width="120" 
        />
    </StackPanel>
    
    <ScrollViewer>
      <ItemsControl
        ItemsSource="{Binding Source={StaticResource Data}, XPath=person}"
        ItemTemplate="{StaticResource SelectorTemplate}"
        />
    </ScrollViewer>
    
  </DockPanel>
</Page>

2 comments:

Unknown said...

I miss Pam already!! :D

Great post, Neil.

-Josh

Grant Watts said...

Grreat blog post