1
votes

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 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>
1
That's because you shouldn't be changing the ContentTemplate... It's got all the hooks for the drag-n-drop to work and you replaced it with a data template... - Justin XL
But @JustinXL I need to include some style and other info to the ListView Item. How can I make it without ContentTemplate? - Vitor Norton
If you want different styling, do it in the Style of the listview item. - Justin XL
I don`t think it is possible to insert a button or right click menu context from the Style. I get your point, but this will not work for me. Gonna try again to change it to DataTemplate, but every time I add a new item id did not follow the DataTemplate I had set. - Vitor Norton
Both sound possible to me. But if you really want your own control template, you should consider building your own drag n drop logic. - Justin XL

1 Answers

0
votes

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

Update

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).