The problem is that the listview uses virtualization, and when the items change their appearance, they remain after list is refreshed. This is an answer from Microsoft engineers
This is caused by the virtualizing nature of ListView's default panel (ItemsStackPanel). It essentially reuses the same ListViewItem (containers) and when it reuses them it might be in a state that is not correct. When you use stack panel, there is no virtualization and every item gets a container (no reuse) which as you can imagine can get very time consuming if there are many items in your list. You can try using PrepareContainerForItemOverride, ClearContainerForItemOverride to cleanup the ListViewItem's state before they get reused. Even better would be to use the ContainerContentChanging event and do it there so you don't need a derived type.
Unfortunately I could not find an example for using the PrepareContainerForItemOverride, ClearContainerForItemOverride and ContainerContentChanging methods.
How can I Clear the ListViewItem's state in the ContainerContentChanging?
UPDATE:
UserControl:
<Grid>
<SwipeControl x:Name="ListViewSwipeContainer">
<Grid VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TextBlock
Margin="10,5,200,5"
HorizontalAlignment="Left"
VerticalAlignment="Center"
FontSize="18"
Text="{Binding ElementName=subsceneView, Path=Title}"
TextWrapping="Wrap"/>
<AppBarButton
Name="OpenFolderButton"
Grid.RowSpan="2"
MinWidth="75"
Margin="10,0,10,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Click="OpenFolderButton_Click"
Icon="OpenLocal"
IsTabStop="False"
Label="Open Folder"
Visibility="Collapsed"/>
<AppBarButton
Name="DownloadHoverButton"
Grid.RowSpan="2"
Margin="10,0,10,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Click="DownloadHoverButton_Click"
Icon="Download"
IsTabStop="False"
Label="Download"
Visibility="Collapsed"/>
</Grid>
</SwipeControl>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="HoveringStates">
<VisualState x:Name="HoverButtonsHidden"/>
<VisualState x:Name="HoverButtonsShown">
<VisualState.Setters>
<Setter Target="DownloadHoverButton.Visibility" Value="Visible"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
code behind:
public sealed partial class SubsceneUserControl : UserControl
{
#region DependencyProperty
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(SubsceneUserControl),
new PropertyMetadata(string.Empty));
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
#endregion
public SubsceneUserControl()
{
this.InitializeComponent();
}
private void UserControl_PointerEntered(object sender, PointerRoutedEventArgs e)
{
if (e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Mouse ||
e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Pen)
{
VisualStateManager.GoToState(sender as Control, "HoverButtonsShown", true);
}
}
private void UserControl_PointerExited(object sender, PointerRoutedEventArgs e)
{
VisualStateManager.GoToState(sender as Control, "HoverButtonsHidden", true);
}
private void OpenFolderButton_Click(object sender, RoutedEventArgs e)
{
}
private void DownloadHoverButton_Click(object sender, RoutedEventArgs e)
{
OpenFolderButton.Visibility = Visibility.Visible;
DownloadHoverButton.Visibility = Visibility.Collapsed;
}
}
and this is my listview
<ListView
x:Name="listv"
ItemsSource="{x:Bind Subtitles, Mode=OneWay}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:SubsceneDownloadModel">
<local:SubsceneUserControl Title="{x:Bind Title}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
private void AddItems()
{
Subtitles?.Clear();
for (int i = 0; i < 10; i )
{
Subtitles.Add(new SubsceneDownloadModel { Title = "Test " i });
}
}
CodePudding user response:
ListViewItem's state before they get reused
For this scenario, the better way is using mvvm binding and implement INotifyPropertyChanged
for model class. For more detail please refer to Data binding in depth.
For example
Model Class
public class Model : INotifyPropertyChanged
{
public Model(string name)
{
this.Name = name;
}
private string _name;
public string Name
{
get
{
return _name;
}
set
{
_name = value;
NotifyPropertyChanged();
}
}
private bool _visible;
public bool Visible
{
get => _visible;
set
{
_visible = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
// PropertyChanged is always null.
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Xaml
<ListView x:Name="MyList">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" />
<SymbolIcon
x:Name="MySbl"
HorizontalAlignment="Right"
Symbol="Accept"
Visibility="{Binding Visible}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Usage
MyList.ItemsSource = new ObservableCollection<Model> {
new Model("hello"),
new Model("hello1"),
new Model("hello2"),
new Model("hello3"),
new Model("hello4"),
new Model("hello5"),
new Model("hello6"),
};