Activity Tracker – WPF DataBinding the TabControl with TreeView
In the first post about Activity Tracker, I have created a simple style for my application. In this post, I want to show DataBinding. With this feature, we can make a flexible connection between Model and View Layer. There is no need for code. Everything is constructed in XAML language, which is similar to the HTML Markup.
Simple Binding - DataTemplate
In the first example, we have a simple DataTemplate. It contains a structured Grid, Button and couple of TextBlocks. By assigning to the Text Property Binding logic we are telling the Template too look for a specific property in a Class, which will be used with the Template.
<DataTemplate x:Key="TaskTemplate">
<Grid Width="200">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5*"></ColumnDefinition>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="3*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Path=Title}"/>
<TextBlock Grid.Column="1" Text="{Binding Path=TimeSpent}"/>
<TextBlock Grid.Column="2" Text="{Binding Path=TimeEstimate}"/>
<Button Grid.Column="3">Start</Button>13 </Grid>14 </DataTemplate>
Template is looking for specified properties in a collection bound to the
ItemsSource Property. We can also do this by the DataContext, it’s more powerful concept for another Post.</div>
HierarchicalDataTemplate
Simple DataTemplate doesn’t have the ability to automatically create a hierarchy. I could make a code and implement a logic to get this effect but fortunately WPF have a HierarchicalDataTemplate. This special template is automatically making a hierarchical view used on the TreeViews.
<HierarchicalDataTemplate x:Key="TaskTemplate" ItemsSource="{Binding Childrens}" DataType="{x:Type data:Task}">
<Grid Width="200">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5*"></ColumnDefinition>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="3*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Path=Title}"/>
<TextBlock Grid.Column="1" Text="{Binding Path=TimeSpent}"/>
<TextBlock Grid.Column="2" Text="{Binding Path=TimeEstimate}"/>
<Button Grid.Column="3">Start</Button>
</Grid>
</HierarchicalDataTemplate>
Task class contains a list of Childrens which is used in the HierarchicalDataTemplate.
public class Task
{
.....
public List<Task> Childrens { get; set; }
.....
}
HierarchicalDataTemplate has a property ItemsSource which is binded to Children’s property. The Template will create the root element and also elements from the Children’s List. I don’t have to worry about how it’s working. Magic happens behind the scene.
Binding TabControl Content
Every TabControl contains a TreeView, which is filled with Task Items. I wanted another Template, which would automatically inject the TreeView into TabControl.
<DataTemplate x:Key="TabItemTemplate">
<Grid>
<TreeView Background="Transparent" ItemsSource="{Binding Content}" ItemTemplate="{StaticResource TaskTemplate}">
</TreeView>
</Grid>
</DataTemplate>
TabItem Template creates a Transparent TreeView. It’s bound to the Content property. This is a property of Project class which is used to create a new TabItems with children’s injected to the TreeView.
public class Project
{
public string Title { get; set; }
public List<Task> Content { get; set; }
}
Project class is a root for every Task set. It’s Title bound to the Header property of a TabItem.
<Grid Name="MainTree" Grid.Row="1" Grid.RowSpan="1">
<TabControl Name="MainTabControl" ContentTemplate="{StaticResource TabItemTemplate}"></TabControl>
</Grid>
In order to bind the Title from the Project class to the TabItem the header, I had to change a tab item style a bit. The Content Presenter was replaced by the TextBlock which Text Property is bound to The Title.
<Style TargetType="TabItem">
....
<Grid>
<StackPanel Orientation="Horizontal">
<Border Name="Border" Padding="2" BorderBrush="Black" BorderThickness="1,1,1,1"
CornerRadius="10,10,0,0" Background="DarkOrange">
<TextBlock Text="{Binding Path=Title}"></TextBlock>
</Border>
</StackPanel>
</Grid>
....
</Style>
When all is done I just need to create sample data.
List<Task> tasks =new List<Task>()
{
new Task("test",10,"I",DateTime.Now,8,"komentarz"){ TimeSpent ="0"},
new Task("test",10,"I",DateTime.Now,8,"komentarz"){ TimeSpent ="0"},
new Task("test",10,"I",DateTime.Now,8,"komentarz"){ TimeSpent ="0"},
new Task("test",10,"I",DateTime.Now,8,"komentarz"){ TimeSpent ="0"}
};
InitializeComponent();
tasks[0].Childrens =new List<Task>()
{
new Task("test",10,"I",DateTime.Now,8,"komentarz"){ TimeSpent ="0"}
};
List<Project> projects =new List<Project>()
{
new Project() { Content = tasks, Title ="Projekt1" },
new Project() { Title ="Projekt2" }
};
MainTabControl.ItemsSource = projects;
And the result is :
Next post Timers + activity tracker logic.