Reputation: 13
I have set up a ListView to manage tabs on my app. So users can drag and drop tabs to new instances of the app and reorder tabs. All goes great until I set up a ControlTemplate for the Itens at the ListView.
I used ControlTemplate because when I add a ListView.ItemTemplate directly it won`t work (it does not affect the listview item). So before adding a new item I use this:
tab.Template = (ControlTemplate)this.Resources["listViewItemTemplate"];
So the tabs is looking great, but all the functions that used to work (like drag and drop) isn't working anymore. When I remove the ControlTemplate the ListView show only one little string.
This is what is happening
My ListView Code:
<ListView x:Name="TabsListView"
CanDragItems="True"
AllowDrop="True"
DragEnter="TabsListView_DragEnter"
Drop="TabsListView_Drop"
ScrollViewer.VerticalScrollBarVisibility="Disabled
CanReorderItems="True"
DragItemsStarting="TabsListView_DragItemsStarting"
ItemClick="TabsListView_ItemClick" >
I know for sure that the issue isn't on the drag and drop method.
To create a new item I just use Add. Of course I checked every single item inside ControlTemplate to know that it is not blocking anything.
The control template is:
<ControlTemplate x:Key="listViewItemTemplate" >
<ListViewItem>
<Grid Tapped="Grid_Tapped" Width="180" Margin="-12,-12,-12,0">
<TextBlock Text="{Binding Name}" Margin="5,6,0,0"></TextBlock>
</Grid>
</ListViewItem>
</ControlTemplate>
Upvotes: 1
Views: 321
Reputation: 39072
The reason is that you are using ControlTemplate
and embedding a ListViewItem
in there. ListViewItems
are created automatically by the control to contain the items, and ItemTemplate
is a template of the contents of this container. So it should look like this:
<DataTemplate x:Key="listViewItemTemplate" >
<Grid Tapped="Grid_Tapped" Width="180" Margin="-12,-12,-12,0">
<TextBlock Text="{Binding Name}" Margin="5,6,0,0"></TextBlock>
</Grid>
</ControlTemplate>
<ListView ...
ItemTemplate="{ThemeResource listViewItemTemplate}">
...
</ListView>
Furthermore, if you want to style the ListViewItem
(the container), you can create a style with TargetType="ListViewItem"
and set it as ListView.ItemContainerStyle
.
<Style TargetType="ListViewItem" x:Key="TabListViewItemContainerStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<Grid x:Name="ContentBorder">
... some template with ContentPresenter:
<ContentPresenter x:Name="ContentPresenter" ... />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ListView ...
ItemContainerStyle="{StaticResource TabListViewItemContainerStyle}">
...
</ListView>
You can find the default style in: C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\10.0.15063.0\Generic
I have checked the code and found the problem. What you are actually doing is adding ListViewItems
directly in code:
ListViewItem tab = new ListViewItem();
DataModel model = new DataModel();
model.Name = $"NewTab {counter}";
tab.DataContext = model;
tab.Content = model.ToString();
TabsListView.Items.Add(tab);
Unfortunately this does not work, because when ListView
sees an item that is of type ListViewItem
, it just adds it to the list, but ItemTemplate doesn't apply to it, because that is applied only when the items are not UIElement
but rather a custom class. If you use DataModel
directly, ItemTemplate
will be applied as expected:
DataModel model = new DataModel();
model.Name = $"NewTab {counter}";
TabsListView.Items.Add(model);
ListView
encounters DataModel
, knows that just a class not a UIElement
, so it internally creates a ListViewItem
, applies ItemContainerStyle
to it and then uses the ItemTemplate
to create the UI that will be displayed inside the ListViewItem's ContentPresenter
. It is better to use this approach as it gives you better decoupling as you don't have to create UI-based classes in the code-behind and you get more control (as you can change both the style of the item as well as the container).
Upvotes: 0