Friday, 21 October 2011

One way to find debugging information in Wix is to show it on the setup dialog boxes. For instance, we can show the product code on the welcome dialog box if we find any previous version of the product. We can do this as follows:

Monday, 10 October 2011

In the example shown in Part 3, we saw how to show a tree, with depth of 3.

But what about scenarios, where we do not really know the depth of the tree? Best example is Windows Explorer, it shows a tree with folders, sub-folders, more sub-folders and so on. How do build such a tree?

Let’s try to implement this. We create a class Folder:

publicclass Folder

{

publicstring Name

{

get

{

if (!String.IsNullOrEmpty(Path))

{

return io.Path.GetFileName(Path);

}

returnnull;

}

}

publicstring Path

{ get; set; }

public List<Folder> Folders

{ get; set; }

public Folder()

{

Folders = new List<Folder>();

}

publicstatic Folder CreateFolderTree(string rootFolder)

{

Folder fld = new Folder { Path = rootFolder };

foreach (var item in io.Directory.GetDirectories(rootFolder))

{

fld.Folders.Add(CreateFolderTree(item));

}

return fld;

}

}

XAML:

<TreeViewName="tv"ItemsSource="{Binding}"Background="Beige">

<TreeView.ItemTemplate>

<HierarchicalDataTemplateItemsSource="{Binding Path=Folders}">

<BorderBorderBrush="BurlyWood"BorderThickness="1">

<TextBlockText="{Binding Path=Name}"/>

</Border>

</HierarchicalDataTemplate>

</TreeView.ItemTemplate>

</TreeView>

Loading data (in Window.cs):

List<Folder> folders = new List<Folder>();

folders.Add(Folder.CreateFolderTree(@"C:\WPFTest"));

tv.DataContext = folders;

Output:

In Windows explorer:

So what changed here? The code generating business class is more or less same, the major change is in XAML. You will notice:

> We are using only a single HierarchicalDataTemplate. The display logic for each item uses the property – “Name”.> We do specify the ItemsSource property but do not specify an ItemTemplate. This is the way of telling WPF that - the children of this TreeViewItem are in the ‘Folders’ property, however as I have not specified the ItemTemplate, please reuse the one I am using. > We can use ‘Folders’ and ‘Name’ property without any problem because all the objects in the tree view are of same type – Folder.

And voila! We get to use the same HierarchicalDataTemplate for both the parent and its children.

Now the question is, what if we want to use different property for display – in parent and children? For instance, how do we show full folder path in the parent node, but just the folder name in the child?

As with other list controls, TreeView also uses the default ToString() to show the data. Next, we use an ItemTemplate to show the relevant data.

Updated XAML:

<TreeViewName="tv"ItemsSource="{Binding}">

<TreeView.ItemTemplate>

<DataTemplate>

<TextBlockText="{Binding Path=Name}"/>

</DataTemplate>

</TreeView.ItemTemplate>

</TreeView>

Output:

Well ok, so now we do get the Category name but what about the items we added in these categories? Let’s update the DataTemplate and use the ItemList property of the class Category:

<TreeViewName="tv"ItemsSource="{Binding}">

<TreeView.ItemTemplate>

<DataTemplate>

<BorderBorderBrush="BurlyWood"BorderThickness="2">

<StackPanelOrientation="Vertical">

<TextBlockText="{Binding Path=Name}"/>

<ListBoxItemsSource="{Binding Path=ItemList}"></ListBox>

</StackPanel>

</Border>

</DataTemplate>

</TreeView.ItemTemplate>

</TreeView>

Result:

We see the items in each Category, but this is not what we expect from a TreeView. What about the ‘+’ signs? To show them, we need to use a different kind of ItemTemplate – HierarchicalDataTemplate.

The HierarchicalDataTemplate needs two things:- it needs to know how to display the item data (which is just like a DataTemplate) and- the Binding path of its children and the template to use to show them

You will notice we used HierarchicalDataTemplate for the first level (where we just show the category name), for the second level (showing the items in the category) we use the DataTemplate. When we are sure that the data is the last level in the hierarchy, we use the DataTemplate.

Let’s look at another often used control in business applications – TreeView. It is a great control to visualise hierarchical data. Take an example – of customer, their orders and order details. A lot of data without using TreeView – it can get little bit complex to show the relationship between the data.

Just like we have ListBox and its ListBoxItem where we can include any control – not just text; we have TreeView has TreeViewItem.

The XAML goes as:

<TreeViewMargin="10,10">

<TreeViewItemHeader="Sony">

<TreeViewItemHeader="Order-123">

<TreeViewItemHeader="LCD TV"/>

<TreeViewItemHeader="Wii"/>

<TreeViewItemHeader="Vaio"/>

</TreeViewItem>

</TreeViewItem>

<TreeViewItemHeader="Apple">

<TreeViewItemHeader="Order-1">

<TreeViewItemHeader="iPod"/>

<TreeViewItemHeader="iPhone"/>

<TreeViewItemHeader="iPad"/>

</TreeViewItem>

<TreeViewItemHeader="Order-2">

<TreeViewItemHeader="Mac"/>

</TreeViewItem>

</TreeViewItem>

</TreeView>

Not the best data, but I hope good enough to use in a TreeView.

The output is:

How do we add items in code? We have instantiate TreeViewItem objects and add it to the “Items” collection. For e.g.:

TreeViewItem item = new TreeViewItem();

item.Header = "1";

item.Items.Add(new TreeViewItem { Header = "10" });

item.Items.Add(new TreeViewItem { Header = "11" });

item.Items.Add(new TreeViewItem { Header = "12" });

TreeViewItem item2 = new TreeViewItem();

item2.Header = "2";

item2.Items.Add(new TreeViewItem { Header = "20" });

item2.Items.Add(new TreeViewItem { Header = "21" });

item2.Items.Add(new TreeViewItem { Header = "22" });

tv.Items.Add(item);

tv.Items.Add(item2);

Now we have set the Foreground colour of Window class to Orange – but we do not get Orange on the TreeView text Why?

You will see that when a TreeViewItem is selected, its foreground colour turns to White and background to blue.

So if the ‘White’ foreground colour inherits down to the other TreeViewItems – then their fore colour will also change to White – making then invisible.

To test this, let’s add a TextBlock as a child without using TreeViewItem:

<TreeViewItemHeader="Order-1">

<TreeViewItemHeader="iPod"/>

<TreeViewItemHeader="iPhone"/>

<TreeViewItemHeader="iPad"/>

<TextBlockText="Apple TV"/>

</TreeViewItem>

As you can see here, a TextBlock inherits its foreground colour from its parent – ‘Order-1’ TreeViewItem – and so appears to be hidden. This means that each TreeViewItem uses system settings for their colour settings – when selected. So:

- When we are using TreeViewItem, the property values are not inherited from parent- When using controls directly, property values are inherited

If you test the WPF Task Manager application we have been developing in last few posts, you will notice that we experience a lag when we scroll the process grid – or click the grid to open its row details containing the threads.

There is a concept of ‘virtualization’ in WPF. It basically means the UI object which is not shown on the screen is virtualized or to say taken out of memory. This reduces the no. of objects which is supposed to improve performance.

Grid has two properties which change the virtualization - EnableRowVirtualization and EnableColumnVirtualization.

By default: EnableRowVirtualization is true and EnableColumnVirtualization is false. This is why when you scroll down or up quite quickly you notice a lag – the virtualized objects are being brought to life when they become visible.

In WPF task manager if we change the value EnableRowVirtualization to false (it is true be default) you will notice a considerable improvement in performance. We can set this on inner grid as well.

I think WPF is still doing some operations (could be lazy load) – because if you click a row for the first time and its row details open up, and then if you do it again the row details show up much faster compared to first time.

Continuing with our WPF binding series with WPF Task Manager – today we will work with row details in DataGrid control.

We were able to see all the processes in the grid:

You will note an additional column – ThreadCount which indicates the no. of threads active in that process.

Each process can have many threads, so the Process object which we are using to fill our List<Process> collection – has a property Threads which is ProcessThreadCollection containing objects of type ProcessThread.

We will modify our grid to show details of these threads for a process. For this design using RowDetails would be an ideal solution.

In XAML file just below the data grid columns definition, we add the <DataGrid.RowDetailsTemplate> tag to define the row detail. This is just like ItemTemplate in ListBox and we can use any control or layout here. But since we are showing a collection (of threads) in row details why not use another grid here. The XAML is:

The grid needs some colour. To make alternating rows appear differently we use the attributes - AlternationCount="2" AlternatingRowBackground="LightBlue" for outer grid and RowBackground=”Beige” for inner grid.

How do we display an error or warning message on the ccnet project’s main page?

We generally write ECHO messages:

<echo message="Problem with your build: ${error.code}."/> But this will log the message only to the ‘NAnt Output’ log.

To display the message on the project’s home page, we need to add the Level attribute with an appropriate setting: Error or Warning.<echo message="Problem with your build: ${exit.code}." level="Error"/>

Tuesday, 4 October 2011

We saw in the last post how we can change the UI of our WPF Task Manager to show all the processes in a grid layout using a ListView control. ListView is useful when we are restricted to .NET 3.5, because in .NET 4.0 framework we have the DataGrid control. There are certain shortcomings in ListView – one being it doesn’t allow automatic sorting of rows. Even if we implement sorting it doesn’t give a visual hint on the column we clicked to sort data. DataGrid is a complete control with all the important features. So let’s modify our application to use the DataGrid control.