Xamarin Forms Rowspan in CollectionView - xaml

I have a problem.
I created this CollectionView with a few dummy trades, that looks like this right now:
Now this is almost like I want it, except for 1 thing: I want the last column to get a rowspan over both the rows, like this:
Now if this was a regular Grid, I could do it with Grid.RowSpawn, but it is in a CollectionView, because I can have a lot of trades. The downside of the CollectionView is that each row is a different Grid, so they are actually not connected! Here is my code right now:
<CollectionView ItemsSource="{Binding agentOrderList}" Margin="0" HeightRequest="450">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid RowSpacing="0">
<StackLayout Orientation="Vertical">
<StackLayout HorizontalOptions="FillAndExpand" HeightRequest="1" BackgroundColor="White"/>
<Grid RowSpacing="0" Margin="5,0,5,0">
<Grid.RowDefinitions>
<RowDefinition Height="21" />
<RowDefinition Height="21" />
<RowDefinition Height="4" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="65" />
<ColumnDefinition Width="95" />
<ColumnDefinition Width="40" />
<ColumnDefinition Width="75" />
<ColumnDefinition Width="82" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.RowSpan="2" Grid.Column="0" Text="{Binding Date}" FontAttributes="Bold" TextColor="#00D8FF" FontSize="18" VerticalOptions="CenterAndExpand"/>
<Label Grid.Row="0" Grid.RowSpan="2" Grid.Column="1" Text="{Binding Action}" TextColor="White" FontSize="18" VerticalOptions="CenterAndExpand"/>
<Label Grid.Row="0" Grid.RowSpan="2" Grid.Column="2" Text="{Binding Coin}" TextColor="White" FontSize="18" VerticalOptions="CenterAndExpand"/>
<Label Grid.Row="0" Grid.RowSpan="2" Grid.Column="3" Text="{Binding Price}" TextColor="White" FontSize="18" VerticalOptions="CenterAndExpand" HorizontalTextAlignment="End"/>
<Label Grid.Row="0" Grid.Column="4" Text="{Binding ProfitUSDT}" TextColor="{Binding ProfitColor}" FontSize="18" VerticalOptions="CenterAndExpand" HorizontalTextAlignment="End"/>
<Label Grid.Row="1" Grid.Column="4" Text="{Binding ProfitPerc}" TextColor="{Binding ProfitColor}" FontSize="18" VerticalOptions="CenterAndExpand" HorizontalTextAlignment="End"/>
</Grid>
</StackLayout>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Now what is the best way to achieve this? Do I need to make a different view?
Please let me know!

You can't have data cross over between cells in a ListView, so I think you will need to make each list item have a two row grid, so then you can have the last column span two rows.
However, I am confused. Your XAML code does not seem to match the screen shot of one cell. Your XAML code only seems to assign 5 columns, but I see 6 in your screen shots?
IN any case, you are correct, you will need to use one grid to display the Buy and Sell data so that that last column can span 2 rows.
UPDATE: Based on comments below, it seems a DataTemplateSelector may be required here. See: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/templates/data-templates/selector
Partial extract from above link:
A data template selector enables scenarios such as a ListView binding
to a collection of objects, where the appearance of each object in the
ListView can be chosen at runtime by the data template selector
returning a particular DataTemplate.
Creating a DataTemplateSelector A data template selector is
implemented by creating a class that inherits from
DataTemplateSelector. The OnSelectTemplate method is then overridden
to return a particular DataTemplate, as shown in the following code
example:
public class PersonDataTemplateSelector : DataTemplateSelector
{
public DataTemplate ValidTemplate { get; set; }
public DataTemplate InvalidTemplate { get; set; }
protected override DataTemplate OnSelectTemplate (object item, BindableObject container)
{
return ((Person)item).DateOfBirth.Year >= 1980 ? ValidTemplate : InvalidTemplate;
}
}
The OnSelectTemplate method returns the
appropriate template based on the value of the DateOfBirth property.
The template to return is the value of the ValidTemplate property or
the InvalidTemplate property, which are set when consuming the
PersonDataTemplateSelector.
An instance of the data template selector class can then be assigned
to Xamarin.Forms control properties such as ListView.ItemTemplate. For
a list of valid properties, see Creating a DataTemplate.
Limitations
DataTemplateSelector instances have the following limitations:
The DataTemplateSelector subclass must always return the same template
for the same data if queried multiple times.
The DataTemplateSelector subclass must not return another DataTemplateSelector subclass.
The DataTemplateSelector subclass must not return new instances of a
DataTemplate on each call. Instead, the same instance must be
returned. Failure to do so will create a memory leak and will disable
virtualization.
On Android, there can be no more than 20 different
data templates per ListView.
Consuming a DataTemplateSelector in XAML
In XAML, the PersonDataTemplateSelector can be instantiated by
declaring it as a resource, as shown in the following code example:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:Selector;assembly=Selector" x:Class="Selector.HomePage">
<ContentPage.Resources>
<ResourceDictionary>
<DataTemplate x:Key="validPersonTemplate">
<ViewCell>
...
</ViewCell>
</DataTemplate>
<DataTemplate x:Key="invalidPersonTemplate">
<ViewCell>
...
</ViewCell>
</DataTemplate>
<local:PersonDataTemplateSelector x:Key="personDataTemplateSelector"
ValidTemplate="{StaticResource validPersonTemplate}"
InvalidTemplate="{StaticResource invalidPersonTemplate}" />
</ResourceDictionary>
</ContentPage.Resources>
...
</ContentPage>
This page level ResourceDictionary defines two DataTemplate instances and a
PersonDataTemplateSelector instance. The PersonDataTemplateSelector
instance sets its ValidTemplate and InvalidTemplate properties to the
appropriate DataTemplate instances by using the StaticResource markup
extension. Note that while the resources are defined in the page's
ResourceDictionary, they could also be defined at the control level or
application level.
The PersonDataTemplateSelector instance is consumed by assigning it to
the ListView.ItemTemplate property, as shown in the following code
example:
<ListView x:Name="listView" ItemTemplate="{StaticResource personDataTemplateSelector}" />
At runtime, the ListView calls the
PersonDataTemplateSelector.OnSelectTemplate method for each of the
items in the underlying collection, with the call passing the data
object as the item parameter. The DataTemplate that is returned by the
method is then applied to that object.

Related

Can't use ObservableCollection in a Grid?

I am currently working with a Xamarin app for reading RFID temperature tags and updating their values in real time. The current setup uses two Horizontal StackLayouts, one with a ListView showing a list of tags and their values, and the other with a Grid of Images of the human body that I wish to display the values on as well (in specific places).
In the ListView side, I can successfully call an object called TagInfoList, which is an ObservableCollection in the Class I call from the BasePage initialization. However, on the Grid side, I've tried multiple methods of using the TagInfoList, but it does not work. I've tried BindableLayouts, DataTemplates + ViewCells, and none allow me to get TagInfoList usable within my second StackLayout.
Here is the XAML code for the two horizontal StackLayouts:
<StackLayout Orientation="Horizontal" HorizontalOptions="StartAndExpand">
<ListView x:Name="liewViewTagData" ItemsSource="{Binding TagInfoList}" SelectedItem="{Binding objItemSelected, Mode=TwoWay}">
<ListView.Header>
<StackLayout BackgroundColor="#cccccc">
<Grid>
<!-- Grid + Label code irrelevant to my issue-->
</Grid>
</StackLayout>
</ListView.Header>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Vertical">
<Grid>
<!-- Grid + Label code irrelevant to my issue-->
</Grid>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout> <!-- RFID Tag Section -->
<StackLayout Orientation="Horizontal" HorizontalOptions="EndAndExpand"> <!-- Body Model Section -->
<!-- HERE: WHAT TO PUT FOR TagInfoList TO BE USABLE? -->
<Grid VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" WidthRequest="400" ColumnSpacing="0">
<!-- Grid + Label + Images I want to use TagInfoList information in -->
</Grid>
<!-- HERE: WHAT TO PUT FOR TagInfoList TO BE USABLE? -->
</StackLayout> <!-- Body Model Section -->
</StackLayout>```
I've deleted the bulk of inside Grids since it's not relevant. TagInfoList is an ObservableCollection of a class which contains all the data I need (strings and ints). How do I utilize TagInfoList within the second StackLayout? If I do it the same method as the first StackLayout, I get an error that I've called TagInfoList twice (through the 'liewViewTagData' item). Here is that code in the .xaml.cs file:
liewViewTagData.ItemSelected += (sender, e) => {
if (e.SelectedItem == null) return; // Don't do anything if we just de-selected the row
((ListView)sender).SelectedItem = null; // De-select the row
};
My sole goal is to use the TagInfoList ObservableCollection within both StackLayouts, but I'm not sure how to do that.
Thanks!
If I do it the same method as the first StackLayout, I get an error
that I've called TagInfoList twice (through the 'liewViewTagData'
item).
You can do it in the same way as the first StackLayout.And you can change property x:Name of the two listviews to different Name.
You can refer to the following code:
<ScrollView Orientation="Horizontal">
<StackLayout Orientation="Horizontal" HorizontalOptions="StartAndExpand">
<StackLayout Orientation="Horizontal" HorizontalOptions="EndAndExpand">
<ListView x:Name="liewViewTagData" RowHeight="60" ItemsSource="{ Binding TagInfoList}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<!--other code-->
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
<StackLayout Orientation="Horizontal" HorizontalOptions="EndAndExpand">
<ListView x:Name="liewView2" RowHeight="60" ItemsSource="{ Binding TagInfoList}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<!--other code-->
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</StackLayout>
</ScrollView>
Note:
If you run out of horizontal space, I suggest you add a ScrollView to the outer layer.

Resume and modify some graphic elements present in another page

I have created a ControlTemplate in the app.xaml, defining the graphic part, only in the cs I would like to take some elements, to which I have given the name, to assign them the click event, but it signals me that they do not exist in the current context .
Someone who could kindly tell me how to please?
Someone, if possible, modify the source of the webview present in the main page, directly from app.xaml.cs
My code in the template is this:`
<Application.Resources>
<ResourceDictionary>
<ControlTemplate x:Key="HeaderFooterTemplate"><ContentPresenter />
<StackLayout>
<BoxView HorizontalOptions="FillAndExpand" BackgroundColor="#DDDDDD" HeightRequest="2" Margin="0" />
<ScrollView Orientation="Horizontal">
<Grid RowSpacing="0">
<StackLayout Background="#c9ced6" HeightRequest="120" MinimumHeightRequest="120" Orientation="Horizontal" Grid.Row="0" Spacing="2">
<Grid WidthRequest="100" MinimumHeightRequest="120" BackgroundColor="#373B53" Padding="10" x:Name="Dashboard">
<Image Source="homeArancione" x:Name="imgDashboard" HorizontalOptions="Center" VerticalOptions="Center" WidthRequest="30" HeightRequest="30" Grid.Row="0" />
<Label Text="DASHBOARD" TextColor="#c9ced6" FontAttributes="Bold" HorizontalOptions="Center" Grid.Row="1" />
</Grid></Grid>
And in the reference page I recall it like this:
ControlTemplate="{StaticResource HeaderFooterTemplate}"
Graphically it works, but in the cs is I can't modify the texts to the labels or assign click events to the elements because it tells me that they don't exist in the current context
If you want to change the text or trigger the click event of the label in ControlTemplate, you could use the GestureRecognizers. The x:Name could not be found in the ControlTemplate directly.
<ControlTemplate x:Key="HeaderFooterTemplate">
<!--<ContentPresenter />-->
<StackLayout>
<BoxView HorizontalOptions="FillAndExpand" BackgroundColor="#DDDDDD" HeightRequest="2" Margin="0" />
<ScrollView Orientation="Horizontal">
<Grid RowSpacing="0">
<StackLayout Background="#c9ced6" HeightRequest="120" MinimumHeightRequest="120" Orientation="Horizontal" Grid.Row="0" Spacing="2">
<Grid WidthRequest="100" MinimumHeightRequest="120" BackgroundColor="#373B53" Padding="10" x:Name="Dashboard">
<Image Source="homeArancione" x:Name="imgDashboard" HorizontalOptions="Center" VerticalOptions="Center" WidthRequest="30" HeightRequest="30" Grid.Row="0" />
<Label Text="DASHBOARD" TextColor="#c9ced6" FontAttributes="Bold" HorizontalOptions="Center" Grid.Row="1">
<Label.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"></TapGestureRecognizer>
</Label.GestureRecognizers>
</Label>
</Grid>
</StackLayout>
</Grid>
</ScrollView>
</StackLayout>
</ControlTemplate>
Code behind: App.xaml.cs
private void TapGestureRecognizer_Tapped(object sender, EventArgs e)
{
Console.WriteLine("-----------");
var label = sender as Label;
label.Text = "Hello";
}
Update:
update the text of the label as soon as the page loads, without
having to click
Xamarin provide GetTemplateChild to get a named element from a template. The GetTemplateChild method should only be called after the OnApplyTemplate method has been called. If you want to call the OnApplyTemplate method, this template should be added for the contentpage.
You could refer to the thread i done before.
Xamarin SwipeView Open Left Item In ControlTemplate

How to make FlexLayout's height adjust to its content?

In my Xamarin Forms application, I have a list of strings (minimum 1, maximum 4) that I want to display evenly in a column layout. Each column needs to have the same width and the content should expand so that the whole text is wrapped and visible. I know how I can do it using the Grid control using Width="*" on its columns.
I want to achieve the same result using FlexLayout, so I can bind the list of strings to BindableLayout and easily add and remove columns (I will not be displaying strings but a more complex layout in each column).
Using FlexLayout.Grow and FlexLayout.Basis I can get the FlexLayout to display evenly sized columns. The problem is making the FlexLayout's height fit all the displayed labels. Only the first row of text is displayed.
Both the Grid and FlexLayout are wrapped in a StackLayout:
<StackLayout>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Text="First label" Grid.Column="0" BackgroundColor="Aqua"
LineBreakMode="WordWrap" />
<Label Text="Second label" Grid.Column="1" BackgroundColor="Red"
LineBreakMode="WordWrap" />
<Label Text="Third label but with longer text" Grid.Column="2" BackgroundColor="Aqua"
LineBreakMode="WordWrap"/>
<Label Text="Fourth label" BackgroundColor="Red"
Grid.Column="3" LineBreakMode="WordWrap" />
</Grid>
<BoxView BackgroundColor="Blue"></BoxView>
<FlexLayout AlignItems="Stretch">
<Label Text="First label"
FlexLayout.Grow="1"
FlexLayout.Basis="0"
BackgroundColor="Aqua"
LineBreakMode="WordWrap" />
<Label Text="Second label"
FlexLayout.Grow="1"
FlexLayout.Basis="0"
BackgroundColor="Red"
LineBreakMode="WordWrap" />
<Label Text="Third label but with longer text"
FlexLayout.Grow="1"
FlexLayout.Basis="0"
BackgroundColor="Aqua"
VerticalOptions="FillAndExpand"
LineBreakMode="WordWrap" />
<Label Text="Fourth label"
FlexLayout.Grow="1"
FlexLayout.Basis="0"
BackgroundColor="Red"
LineBreakMode="WordWrap" />
</FlexLayout>
<BoxView BackgroundColor="Blue"></BoxView>
</StackLayout>
Grid and FlexLayout displayed
I figured out that when setting the HeightRequest of the FlexLayout to a specific number (e.g. 150), everything works as expected - the row has a height of 150 and all the labels stretch out to fit that. So what I need is somehow specify HeightRequest="Auto" so that the row fits all the column's content without being set to a specific value
Is there a way to achieve this?
FlexLayout will cut its child Elements . So in your case use Grid is the best solution .
so I can bind the list of strings to BindableLayout and easily add and remove columns
If you want to display a collection of data I suggest that you could use ListView or CollectionView(if you want to let the collection scroll in Horizontal or display multi Columns in the same row) .
<ContentPage.Resources>
<ResourceDictionary>
<local:WidthConverter x:Key="WidthConverter" />
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout x:Name="stack">
<CollectionView x:Name="list" >
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Horizontal" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid WidthRequest="{Binding Source={x:Reference stack},Path=Width,Converter={StaticResource WidthConverter}}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Text="111111" BackgroundColor="LightBlue" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
in code behind
public class WidthConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var width = (double)value;
if(width>0)
{
return width * 0.25;
}
return 100;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return 0;
}
}
For more details you could check https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/collectionview/layout#horizontal-list

Hierarchical Template Formatting Problems / Questions

I have a couple of questions about hierarchical template formatting for a TreeView. This image will illustrate:
I want to remove the extra space between the top of the type and the border.
I want to center the icon between the two lines of type
I want to add a thousands comma. I've tried this but there is a problem with adding a comma with a bound data.
Here is the XAML code for the third level:
<HierarchicalDataTemplate
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
ItemsSource="{Binding XPath=Unit}"
>
<Grid Height="42" Width="auto" >
<Grid Height="41" HorizontalAlignment="Left" Margin="0,0,0,0" Name="grid1" VerticalAlignment="Top" Width="auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Image Source= "{Binding XPath=UnitIcon}" Grid.Column="1" Grid.RowSpan="2" VerticalAlignment="Center" HorizontalAlignment="Left" Stretch="None" OpacityMask="White"></Image>
<Label Content="{Binding XPath=UnitName}" Height="54" HorizontalAlignment="Left" Name="label4" VerticalAlignment="Top" FontFamily="Smythe" FontSize="18" Margin="0,0,0,0" Grid.RowSpan="3" Grid.Column="2" Grid.ColumnSpan="3"/>
<Label Content="Strength:" FontFamily="Amaltea WF" FontSize="12" HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Center" Grid.Column="2" Grid.Row="2"/>
<TextBlock Text="{Binding XPath=UnitStrength, ConverterParameter=N0}" Margin="0,0,0,0" FontFamily="BauderieScriptSSK Bold" FontSize="18" HorizontalAlignment="Left" VerticalAlignment="Center" Grid.Column="3" Grid.Row="2"/>
</Grid>
<Line X1='0'
X2='200'
Y1='0'
Y2='0'
Stroke="Gray"
StrokeThickness='1' />
</Grid>
</HierarchicalDataTemplate>
Thanks in advance for the help!
Take the fixed height off the UnitName label. You've got grid cells, you don't want fixed heights. Part of that gap may be the line height from your font. Temporarily set Background="LightSkyBlue" on the label to see how much space the label itself is actually taking up.
Looks like VerticalAlignment="Center" on the image isn't having the desired effect because you've put conflicting fixed heights on everything. Your grid1 is fixed at 41 units high, but the unit name within it is 54 units high. The layout engine is doing its best to comply with the contradictory orders you're giving it.
Delete every fixed height in your XAML. Every one, no exceptions. Let things size themselves. If you absolutely must impose a fixed height on a control, consider putting its contents in a ViewBox, so the contents can dynamically size themselves without overflowing the container. Or not; that can look weird. But first get your relative layout working, and then start working on cramming it down into whatever limited space you've got for it.
When you're having trouble with XAML layout, the naive impulse is to add stuff. And worst of all, to add random stuff -- "I don't know what this property means or what its value means, but maybe if I add it on this control, it'll fix what's wrong with the other one!" -- at best, the stuff you add will be harmless.
Don't do that. Remove stuff instead, then build back up. Add one thing at a time and see what it does. And add nothing without first reading the documentation on it. Adding six random properties from Intellisense seems to take less time than looking up one property on MSDN, but that turns out not to be the case in practice, because the first approach is always guaranteed to be a total waste of time. It's like driving by closing your eyes and trying to steer by the feel of the obstacles you crash into.
You're assigning the right format string to the wrong property. Try this:
<TextBlock Text="{Binding XPath=UnitStrength, StringFormat=N0}"
Except whoops LOL ha ha that doesn't work with Binding.XPath, so I'm talking nonsense. And neither does this:
<Label Content="{Binding XPath=UnitStrength}" ContentStringFormat="N0" />
I suspect they're failing because you're giving them a string rather than an integer, but that's just a guess.
But this works.
public class IntToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
int n = 0;
if (Int32.TryParse((string)value, out n))
{
value = n.ToString((String)parameter);
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
XAML. YOUR_NAMESPACE_HERE is the C# namespace where you defined the IntToStringConverter class. That doesn't necessarily have to be right there; it could be on the parent tag or any containing tag in this XAML file, including the root tag (Window or UserControl or ResourceDictionary). Putting it here makes the example more self-contained.
<HierarchicalDataTemplate
xmlns:local="clr-namespace:YOUR_NAMESPACE_HERE"
>
<HierarchicalDataTemplate.Resources>
<local:IntToStringConverter
x:Key="IntToString"
/>
</HierarchicalDataTemplate.Resources>
<!-- blah blah -->
<TextBlock
Text="{Binding XPath=UnitStrength, Converter={StaticResource IntToString}, ConverterParameter=N0}"
/>
Update
<Window.Resources>
<!-- stuff -->
<HierarchicalDataTemplate
x:Key="UnitTemplate"
ItemsSource="{Binding XPath=Unit}"
>
<Grid Width="auto">
<!-- stuff -->
</Grid>
</HierarchicalDataTemplate>
<!-- stuff -->
</Window.Resources>
And for the TreeView:
<TreeView
...
ItemTemplate="{StaticResource UnitTemplate}"
...
/>
But this works too, if a template is going to be used in only one place:
<TreeView
...
>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate
ItemsSource="{Binding XPath=Unit}"
>
<Grid Width="auto">
<!-- stuff -->
</Grid>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Another Upate
Or finally, if you want to put all your data templates in a file of their own, you want to create a resource dictionary:
<Window.Resources>
<!-- If you're doing the merged thing, you have to explicitly have the
ResourceDictionary tag here.
-->
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="DataTemplates.xaml" />
</ResourceDictionary.MergedDictionaries>
<!-- other resources maybe -->
</ResourceDictionary>
</Window.Resources>
DataTemplate.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ScenarioEditor"
>
<local:IntToStringConverter
x:Key="IntToString"
/>
<HierarchicalDataTemplate
x:Key="UnitTemplate"
ItemsSource="{Binding XPath=Unit}"
>
<Grid Width="auto">
<!-- stuff -->
</Grid>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate
x:Key="SomeOtherTemplate"
>
<Grid Width="auto">
<!-- different stuff -->
</Grid>
</HierarchicalDataTemplate>
</ResourceDictionary>
Yet Another Update
So the tree we're looking at has multiple levels, with a different template for each level. There are two ways to do this, at least: If we had a tree of .NET classes with different child types, we could define "implicit templates" in a resource dictionary. They'd have a DataType attribute rather than x:Key, with the result that (for example) the template with DataType="{x:Type local:HQ}" would automatically be used to display any class instance of that type.
But you've got XML so that's not going to work. What you can do instead is give each HierarchicalDataTemplate its own ItemTemplate. For clarity, the following example omits ItemsSource and much else -- it only illustrates how we set up those parent/child template relationships.
<HierarchicalDataTemplate
x:Key="UnitTemplate"
>
<Grid>
<!-- Unit stuff -->
</Grid>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate
x:Key="CommanderTemplate"
ItemTemplate="{StaticResource UnitTemplate}"
>
<Grid>
<!-- Commander stuff -->
</Grid>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate
x:Key="HQTemplate"
ItemTemplate="{StaticResource CommanderTemplate}"
>
<Grid>
<!-- HQ stuff -->
</Grid>
</HierarchicalDataTemplate>
HQTemplate will be the treeview's ItemTemplate
<TreeView
...
ItemTemplate="{StaticResource HQTemplate}"

Windows StoreApp XAML: Changing ItemTemplate in GridView based on Data type

I am working on developing Windows Store App using C#/XAML. I have experience primarily in iOS and to some extent Android app development, but not yet comfortable with C#/XAML world.
Here is my issue in GridView based page (Based on the nice template VS2012 generates).
I have a gridview and its collection is bound to a data retrieved from network and it works fine.
But I want to change the grid item depending on the data.
For example: I have files and folders that I would like to show using different grid view items.
My Question: How would I use a different DataTemplate for the ItemTemplate depending on the data? For example, for "Folders", I will have only one textblock which is vertically centered and for File, I will have the 2 textblocks and visually different.
Am I going the right path or should I be doing completely different?
The XAML Portion is
<GridView
x:Name="itemGridView"
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Grouped Items"
Grid.RowSpan="3"
Padding="116,137,40,46"
ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
ItemTemplate="{StaticResource FileEntriesTemplate}"
ItemClick="ItemView_ItemClick"
IsItemClickEnabled="True"
SelectionMode="None"
IsSwipeEnabled="false">
The Template is
<DataTemplate x:Key="FileEntriesTemplate">
<Grid HorizontalAlignment="Left" Width="400" Height="80" Background="Beige">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Source="{Binding Image}" Stretch="Uniform" Grid.Column="0" Margin="10,0,0,0" AutomationProperties.Name="{Binding Title}"/>
<StackPanel Orientation="Vertical" Grid.Column="1" Background="Transparent">
<TextBlock Text="{Binding Title}" Foreground="Black" Style="{StaticResource LargeTitleTextStyle}" Margin="20,20,10,0"/>
<TextBlock Text="{Binding Subtitle}" Foreground="gray" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap" Margin="20,10,0,30"/>
</StackPanel>
</Grid>
GridView exposes this through the ItemTemplateSelector property which is a class you can create that inherits from DataTemplateSelector. An example would be that I have a GridView that has Issues and Repositories bound to it and want to use different data templates for each.
My data template selector looks like:
public class IssueSummaryTemplateSelector : DataTemplateSelector
{
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
return item is IssueGroupViewModel ? IssueTemplate : RepositoryTemplate;
}
public DataTemplate RepositoryTemplate
{
get;
set;
}
public DataTemplate IssueTemplate
{
get;
set;
}
}
I then declare the selector as a Resource in xaml assigning the two templates I want to use for Repository and Issues.
<selectors:IssueSummaryTemplateSelector x:Key="IssueSummarySelector"
IssueTemplate="{StaticResource IssueGridZoomedOutTemplate}"
RepositoryTemplate="{StaticResource IssueGridRepositoryZoomedOutTemplate}"/>
You can then use it on your GridView.
<GridView ItemTemplateSelector="{StaticResource IssueSummarySelector}" />