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.
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
} | |
} |
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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).
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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> |
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<DataTemplate x:Key="CultureItemDataTemplate"> | |
<StackPanel Orientation="Horizontal" Height="40"> | |
<TextBlock Text="{Binding Content.CultureInfo.EnglishName}" VerticalAlignment="Center" /> | |
</StackPanel> | |
</DataTemplate> |
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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> |
Running the App
Running the app, the cultures are shown in a tree:
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
Hi,
please, is posible include an icon fith text in TreeViewNode item?
Thanks
njk
LikeLike
Yes, using the ItemTemplate you can define how the node item should look like 🙂
Christian
LikeLike
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.
LikeLike