TreeView Control with Windows Apps

My book Professional C# 7 and .NET Core 2.0 contains a sample to show different cultures and their information in a tree view using a Universal Windows Platform (UWP) application. Because UWP didn’t include a TreeView control, I was using a TreeView sample from Microsoft for this app. Now, with Windows 10 April 2018 Update, version 1803, the TreeView control is included with the Windows Runtime, and I could update the sample code.

A view of a tree

Sample Application

The sample application is from chapter 27, Localization from the book Professional C# 7 and .NET Core 2.0. It shows a list of available cultures in a tree-view. The data displayed in the tree is defined with the CultureData class. This class contains CultureInfo and RegionInfo from the namespace System.Globalization, as well as properties to display sample data for the date, the time, and a number:


public class CultureData
{
public CultureInfo CultureInfo { get; set; }
public IList<CultureData> SubCultures { get; set; }
private double _numberSample = 9876543.21;
public string NumberSample => _numberSample.ToString("N", CultureInfo);
public string DateSample => DateTime.Today.ToString("D", CultureInfo);
public string TimeSample => DateTime.Now.ToString("T", CultureInfo);
public RegionInfo RegionInfo
{
get
{
try
{
return new RegionInfo(CultureInfo.Name);
}
catch (ArgumentException)
{
// with some neutral cultures regions are not available
return null;
}
}
}
}

view raw

CultureData.cs

hosted with ❤ by GitHub

The class CulturesViewModel defines the property RootCultures that contains a list of CultureData objects. This list contains the cultures that have the invariant culture as parent, and the SubCultures property of the CultureData object contains the child cultures:


public class CulturesViewModel : INotifyPropertyChanged
{
public CulturesViewModel() => SetupCultures();
// … INotifyPropertyChanged Implementation
private void SetupCultures()
{
var cultureDataDict = CultureInfo.GetCultures(CultureTypes.AllCultures)
.OrderBy(c => c.Name)
.Select(c => new CultureData
{
CultureInfo = c,
SubCultures = new List<CultureData>()
})
.ToDictionary(c => c.CultureInfo.Name);
var rootCultures = new List<CultureData>();
foreach (var cd in cultureDataDict.Values)
{
if (cd.CultureInfo.Parent.LCID == 0x7f) // check for invariant culture
{
rootCultures.Add(cd);
}
else // add to parent culture
{
if (cultureDataDict.TryGetValue(cd.CultureInfo.Parent.Name, out CultureData parentCultureData))
{
parentCultureData.SubCultures.Add(cd);
}
else
{
throw new InvalidOperationException("parent culture not found");
}
}
}
foreach (var rootCulture in rootCultures.OrderBy(cd => cd.CultureInfo.EnglishName))
{
RootCultures.Add(rootCulture);
}
}
public IList<CultureData> RootCultures { get; } = new List<CultureData>();
private CultureData _selectedCulture;
public CultureData SelectedCulture
{
get => _selectedCulture;
set => SetProperty(ref _selectedCulture, value);
}
}

TreeView Control

To use the new TreeView control, the project setting needs to be changed to only support Windows 10 April 2018 Update (version 1803).

Project Settings - Windows 10 April 2018 Update

The new TreeView control is directly used in the MainPage.xaml to display the tree of cultures in the left side, and detail information (using a user control) on the right side:


<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TreeView Style="{StaticResource TreeViewStyle1}" x:Name="treeView1"
ItemInvoked="{x:Bind OnSelectionChanged, Mode=OneTime}"
SelectionMode="Single">
</TreeView>
<local:CultureDetailUC Grid.Column="1" CultureData="{x:Bind ViewModel.SelectedCulture, Mode=OneWay}" />
</Grid>

view raw

MainPage.xaml

hosted with ❤ by GitHub

Fill The TreeView Control with Data

The TreeView control is filled in the code-behind file of the MainPage. On navigation to the page (using the OnNavigatedTo overridden method), the RootCultures property of the view-model is accessed to create TreeViewNode objects for every root culture. The Content property of the TreeViewNode contains the CultureData object. All the root TreeViewNode objects are added to the TreeView control using the RootNodes property. Sub-cultures are added using the local function AddSubNode. This method is invoked recursively to fill sub-cultures using the Children property of the TreeViewNode. On selection of an item in the tree-view, the OnSelectionChanged method is invokedt that changes the current selection in the associated view-model class:


public sealed partial class MainPage : Page
{
public MainPage() => InitializeComponent();
private void OnSelectionChanged(TreeView sender, TreeViewItemInvokedEventArgs args)
{
if (args.InvokedItem is TreeViewNode node && node.Content is CultureData cd)
{
ViewModel.SelectedCulture = cd;
}
}
public CulturesViewModel ViewModel { get; } = new CulturesViewModel();
protected override void OnNavigatedTo(NavigationEventArgs e)
{
void AddSubNodes(TreeViewNode parent)
{
if (parent.Content is CultureData cd && cd.SubCultures != null)
{
foreach (var culture in cd.SubCultures)
{
var node = new TreeViewNode
{
Content = culture
};
parent.Children.Add(node);
foreach (var subCulture in culture.SubCultures)
{
AddSubNodes(node);
}
}
}
}
base.OnNavigatedTo(e);
var rootNodes = ViewModel.RootCultures.Select(cd => new TreeViewNode
{
Content = cd
});
foreach (var node in rootNodes)
{
treeView1.RootNodes.Add(node);
AddSubNodes(node);
}
}
}

Binding to an ItemsSource is (currently?) not possible with the TreeView control.

Displaying items

Adding CultureData objects to the TreeViewNode Content property, a DataTemplate can be created for display. This template just displays the enlish name of the culture:


<DataTemplate x:Key="CultureItemDataTemplate">
<StackPanel Orientation="Horizontal" Height="40">
<TextBlock Text="{Binding Content.CultureInfo.EnglishName}" VerticalAlignment="Center" />
</StackPanel>
</DataTemplate>

view raw

MainPage.xaml

hosted with ❤ by GitHub

For using item templates with the TreeView control, a control template is required. The control template contains the TreeViewList where the ItemTemplate property references the data template. Most parts of the control template are just default values as created using the Edit Template feature with Visual Studio 2017.


<Style x:Key="TreeViewStyle1" TargetType="TreeView">
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TreeView">
<TreeViewList x:Name="ListControl" AllowDrop="False"
CanReorderItems="False"
CanDragItems="False"
ItemContainerStyle="{StaticResource TreeViewItemStyle}"
ItemTemplate="{StaticResource CultureItemDataTemplate}">
<TreeViewList.ItemContainerTransitions>
<TransitionCollection>
<ContentThemeTransition/>
<ReorderThemeTransition/>
<EntranceThemeTransition IsStaggeringEnabled="False"/>
</TransitionCollection>
</TreeViewList.ItemContainerTransitions>
</TreeViewList>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

view raw

MainPage.xaml

hosted with ❤ by GitHub

Running the App

Running the app, the cultures are shown in a tree:

CultureDemo App UI with TreeView

Summary

There have been some changes with the TreeView control from the Microsoft samples to the new TreeView control available with Windows 10 April 2018 Update. The code using the control has been simplified, it was possible to get rid of expanded and collapsed glyph, and the indentation calculation. It’s not as simple as using the WPF TreeView control yet (for this code, check out *Professional C# 6 and .NET Core 1.0) as an items source is not directly available with the UWP TreeView, and the item template cannot be directly assigned (yet?). Anyway, the new TreeView control is of practical use.

Get the complete source code of the code sample in the GitHub repository for Professional C# 7.

Enjoy coding and my new book Professional C# 7 and .NET Core 2.0

Christian

3 thoughts on “TreeView Control with Windows Apps

  1. I’m trying to create a UWP(newbie) app the does a api fetch then based on the data recreate the tree view for the life of me I can’t figure out how to implement this was; wonder if you had a quick example? Thank you.

    Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.