Tag: windows-universal-apps

Share a Generic XAML ListView, ItemsSource, and DataTemplate with UserControls in Windows Universal Apps

This post applies to Windows Universal Apps specifically. These techniques probably won’t work with WPF or Windows 8/8.1 Apps because the XAML hierarchy and namespaces are different.

The title is certainly a mouthful, but stick with me to understand where this scenario can be useful.

Let’s say you want to re-use a ListView without copy and pasting everything about it wherever you need it. Simple enough, right? Just put it in a UserControl! Not so fast. What if you want to define the ListView’s ItemsSource from one level above the UserControl? It gets a little more complicated. This is especially true if you want to take advantage of a VisualStateManager with the ListView’s ItemTemplate.

CustomListView

For example, I had a UserControl which allowed users to scroll left/right on a ListView by clicking buttons left/right instead of using a scrollbar. I wanted to share this ListView in multiple places,  but didn’t want to restrict the UserControl to a single ItemsSource by hard coding it in the UserControl itself. I wanted to “pass in” the ItemsSource and DataTemplate to define the layout of the items from one level above the UserControl. Additionally, I wanted the layout to change based on the width of the screen to work with phones, tablets, and wide layouts.

So here’s what I did after a few hours of struggling through wildly varying documentation across WPF, Windows 8/8.1, and Windows Universal Apps.

Creating the DataTemplate UserControl

We first need to define the lowest level of the chain to determine what and how the data will be displayed in each data bound ListViewItem. We put the XAML in a separate UserControl so that we can also attach a VisualStateManager to the ListViewItem and have it re-organize appropriately on different layout sizes.

This UserControl will then be referenced one level up in the DataTemplate of the main UserControl which contains the ListView and the left/right Buttons.

<UserControl
    x:Class="DotaDb.UWP.LiveLeagueGameTemplate"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:DotaDb.UWP"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300"
    d:DesignWidth="400">

    <StackPanel Name="StackPanelMain" VerticalAlignment="Top">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="VisualStateGroup">
                <VisualState x:Name="VisualStatePhone">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="0" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="ImageLeagueLogo.HorizontalAlignment" Value="Left" />
                        <Setter Target="TextBlockHype.FontSize" Value="12" />
                        <Setter Target="TextBlockHype.HorizontalAlignment" Value="Left" />
                        <Setter Target="StackPanelMain.Width" Value="120" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState x:Name="VisualStateDesktop">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="800" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>

        <Image Name="ImageLeagueLogo" Source="{x:Bind LiveLeagueGame.LeagueLogoPath}" />
        <TextBlock Name="TextBlockHype" TextWrapping="Wrap" FontSize="16" FontWeight="SemiLight">
            <Run Text="{x:Bind LiveLeagueGame.RadiantTeamName}"></Run>
            <Run Text="vs."></Run>
            <Run Text="{x:Bind LiveLeagueGame.DireTeamName}"></Run>
        </TextBlock>
    </StackPanel>
</UserControl>

Before we can go to the next step, we need to add some code to the code behind. The code above assumes that some property called “LiveLeagueGame” exists. Since this UserControl will be used in the DataTemplate of each ListViewItem, we need to bind that property to the UserControl’s DataContext. We also subscribe to the DataContextChanged event so that we can update all data bindings appropriately.

public sealed partial class LiveLeagueGameTemplate : UserControl
{
    public LiveLeagueGameFlattenedModel LiveLeagueGame { get { return this.DataContext as LiveLeagueGameFlattenedModel; } }

    public LiveLeagueGameTemplate()
    {
        this.InitializeComponent();

        this.DataContextChanged += (s, e) =Bindings.Update();
    }
}

Creating the Main UserControl

Next we need to create the UserControl which will contain the ListView and buttons to manipulate the scrolling of that ListView.… Read more