ListView caching in windows user interface?

I have the following problem: I have a Listview where each item is a custom control ("DisruptionIcon") that displays an icon, depending on the properties set for the custom control. An icon can have one shape out of many (here in the example it is simply β€œNo” and β€œSquare”). The problem is that if I scroll down in Listview and return after that, the icons are wrong. This leads to the following:

Before scrolling down:

Before scrolling down

After scrolling down and up:

enter image description here

Resources:

MainPage.xaml:

 <Page x:Class="App1.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:App1" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" Name="MainPg" > <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <ListView Width="200" ItemsSource="{Binding LineList, ElementName=MainPg}" > <ListView.ItemTemplate> <DataTemplate> <Grid> <local:DisruptionIcon DisplayName="{Binding DisplayName}" IconType="{Binding DisplayStyle}" Color1="{Binding Color1}" Color2="{Binding Color2}" /> <TextBlock Text="{Binding DisplayName}" Foreground="Red"/> </Grid> </DataTemplate> </ListView.ItemTemplate> </ListView> </Grid> 

MainPage.xaml.cs:

 public sealed partial class MainPage : Page { public MainPage() { LineList = new ObservableCollection<ServerLineDefinition>(); for (byte i = 0; i <= 250; i++) { var ds = new ServerLineDefinition("ID " + i, "FFFFFF", "000000", 1, i); LineList.Add(ds); } InitializeComponent(); } public ObservableCollection<ServerLineDefinition> LineList { get; set; } } 

My user control: DisruptionIcon.xaml:

 <UserControl x:Class="App1.DisruptionIcon" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:App1" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" Name="mainUc" mc:Ignorable="d" d:DesignHeight="34" d:DesignWidth="80" Width="80" Height="34" > <Grid> <local:DisplayIconTemplateSelector Content="{Binding IconType, ElementName=mainUc}"> <local:DisplayIconTemplateSelector.StyleNone> <DataTemplate> <Grid/> </DataTemplate> </local:DisplayIconTemplateSelector.StyleNone> <local:DisplayIconTemplateSelector.StyleSquare> <DataTemplate> <Rectangle Width="80" Height="32" Fill="{Binding Color2, ElementName=mainUc}" VerticalAlignment="Top" HorizontalAlignment="Left" StrokeThickness="2" Stroke="{Binding Color1, ElementName=mainUc}" /> </DataTemplate> </local:DisplayIconTemplateSelector.StyleSquare> </local:DisplayIconTemplateSelector> <Viewbox Stretch="Uniform" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="8,0" > <TextBlock Text="{Binding DisplayName, ElementName=mainUc}" Foreground="{Binding Color1, ElementName=mainUc}" Height="auto" VerticalAlignment="Top" TextWrapping="NoWrap" FontWeight="Bold" TextAlignment="Center" Margin="0,-2,0,0" /> </Viewbox> </Grid> 

This is the code for DisruptionIcon in disruptionicon.xaml.cs , where basically the properties are simply bound by DependencyProperties and the Colors will be converted to SolidColorBrush if they are presented as a string. This is what I need because of the design of the application:

 public sealed partial class DisruptionIcon { public DisruptionIcon() { Color1 = new SolidColorBrush(Colors.White); Color2 = new SolidColorBrush(Colors.Black); IconType = DisplayStyle.None; DisplayName = ""; InitializeComponent(); } #region Color1 /// <summary> /// Used for the View of multiple Icons. Contains an IconId and the text to show for every Entry /// </summary> public object Color1 { get { return (object)GetValue(DisruptionColor1Property); } set { SetValue(DisruptionColor1Property, value); } } // Using a DependencyProperty as the backing store for ItemSource. This enables animation, styling, binding, etc... public static readonly DependencyProperty DisruptionColor1Property = DependencyProperty.Register("Color1", typeof(object), typeof(DisruptionIcon), new PropertyMetadata(null, DisruptionColor1PropertyCallback)); public static void DisruptionColor1PropertyCallback(DependencyObject dp, DependencyPropertyChangedEventArgs args) { var iconColor1 = ((DisruptionIcon)dp).Color1; if (iconColor1 == null || iconColor1 is SolidColorBrush) return; //Convert Colors if (iconColor1 is string) iconColor1 = ConvertToSolidColorBrush((string)iconColor1); ((DisruptionIcon)dp).Color1 = iconColor1; } #endregion #region Color2 /// <summary> /// Used for the View of multiple Icons. Contains an IconId and the text to show for every Entry /// </summary> public object Color2 { get { return (object)GetValue(DisruptionColor2Property); } set { SetValue(DisruptionColor2Property, value); } } // Using a DependencyProperty as the backing store for ItemSource. This enables animation, styling, binding, etc... public static readonly DependencyProperty DisruptionColor2Property = DependencyProperty.Register("Color2", typeof(object), typeof(DisruptionIcon), new PropertyMetadata(null, DisruptionColor2PropertyCallback)); public static void DisruptionColor2PropertyCallback(DependencyObject dp, DependencyPropertyChangedEventArgs args) { var iconColor2 = ((DisruptionIcon)dp).Color2; if (iconColor2 == null || iconColor2 is SolidColorBrush) return; //Convert Colors if (iconColor2 is string) iconColor2 = ConvertToSolidColorBrush((string)iconColor2); ((DisruptionIcon)dp).Color2 = iconColor2; } #endregion #region IconType /// <summary> /// Used for the View of multiple Icons. Contains an IconId and the text to show for every Entry /// </summary> public object IconType { get { return (DisplayStyle)GetValue(DisruptionDisplayStyleProperty); } set { SetValue(DisruptionDisplayStyleProperty, value); } } // Using a DependencyProperty as the backing store for ItemSource. This enables animation, styling, binding, etc... public static readonly DependencyProperty DisruptionDisplayStyleProperty = DependencyProperty.Register("IconType", typeof(object), typeof(DisruptionIcon), new PropertyMetadata(DisplayStyle.None, DisruptionDisplayStylePropertyCallback)); public static void DisruptionDisplayStylePropertyCallback(DependencyObject dp, DependencyPropertyChangedEventArgs args) { var iconDisplayStyle = ((DisruptionIcon)dp).IconType; if (args.NewValue is Int32) { ((DisruptionIcon)dp).IconType = (DisplayStyle)args.NewValue; } } #endregion #region DisplayName /// <summary> /// Used for the View of multiple Icons. Contains an IconId and the text to show for every Entry /// </summary> public string DisplayName { get { return (string)GetValue(DisruptionDisplayNameProperty); } set { SetValue(DisruptionDisplayNameProperty, value); } } // Using a DependencyProperty as the backing store for ItemSource. This enables animation, styling, binding, etc... public static readonly DependencyProperty DisruptionDisplayNameProperty = DependencyProperty.Register("DisplayName", typeof(string), typeof(DisruptionIcon), new PropertyMetadata(null, DisruptionDisplayNamePropertyCallback)); public static void DisruptionDisplayNamePropertyCallback(DependencyObject dp, DependencyPropertyChangedEventArgs args) { var iconDisplayName = ((DisruptionIcon)dp).DisplayName; if (iconDisplayName == null) return; ((DisruptionIcon)dp).DisplayName = iconDisplayName; } #endregion /// <summary> /// Converts a ColorCode (ie FF8899) to SolidColorBrush /// </summary> /// <param name="colorCode">Six-Digit Hex-Code of the Color</param> /// <returns></returns> private static SolidColorBrush ConvertToSolidColorBrush(string colorCode) { if (colorCode != null && colorCode.Length == 6) { return new SolidColorBrush(Color.FromArgb(255, Convert.ToByte(colorCode.Substring(0, 2), 16), Convert.ToByte(colorCode.Substring(2, 2), 16), Convert.ToByte(colorCode.Substring(4), 16))); } return new SolidColorBrush(Colors.Black); } } 

My TemplateSelector DisplayIconTemplateSelector.cs:

 public class DisplayIconTemplateSelector : ContentControl { protected override void OnContentChanged(object oldContent, object newContent) { base.OnContentChanged(oldContent, newContent); ContentTemplate = SelectTemplate(newContent, this); } public DataTemplate StyleNone { get; set; } public DataTemplate StyleSquare { get; set; } public DataTemplate SelectTemplate(object item, DependencyObject container) { var quoteItem = (DisplayStyle)item; switch (quoteItem) { default: return StyleNone; case DisplayStyle.Square: return StyleSquare; } } } 

And finally, my ServerLineDefinition-Class:

 public class ServerLineDefinition : INotifyPropertyChanged { public ServerLineDefinition() { } public ServerLineDefinition( string displayName, string backgroundColor, string foregroundColor, int displayStyle, int id) { DisplayName = displayName; Color2 = backgroundColor; Color1 = foregroundColor; DisplayStyle = displayStyle; Id = id; } public int Id { get; set; } public string DisplayName { get; set; } public int DisplayStyle { get; set; } /// <summary> /// RGB-Value for BackgroundColor /// </summary> public string Color2 { get; set; } /// <summary> /// RGB-Value for ForegroundColor /// </summary> public string Color1 { get; set; } #region PropertyChanged public event PropertyChangedEventHandler PropertyChanged; //To Update Content on the Form /// <summary> /// Helper for Triggering PropertyChanged /// </summary> /// <param name="triggerControl">The Name of the Property to update</param> private void RaisePropertyChanged(string triggerControl) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(triggerControl)); } } #endregion } 

It seems to me that there is a problem with Listview caching. If I replaced Listview with ItemsControl , which was damaged in ScrollViewer , the problem does not exist, but it uses much more memory and takes more time to load. In addition, if you uncomment the TextBlock , which is added as a comment in MainPage.xaml , you see in each line the correct identifier, but the wrong image, as shown in the screenshots.

Edit : If I put any control in a ScrollViewer , then it works slowly, but it works. It also works if I put the entire Code of DisruptionIcon.xaml directly into the MainPage location referenced by DirutpionIcon.

+4
source share
1 answer

Try using this style for ItemsControl , the keyword "Virtualization":

  <Setter Property="ItemsPanel"> <Setter.Value> <ItemsPanelTemplate> <ItemsStackPanel Orientation="Vertical"/> </ItemsPanelTemplate> </Setter.Value> </Setter> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ItemsControl"> <Border x:Name="LayoutRoot"> <ScrollViewer> <ItemsPresenter/> </ScrollViewer> </Border> </ControlTemplate> </Setter.Value> </Setter> 

And in DependencyProperty for DisplayName update:

 public static void DisruptionDisplayNamePropertyCallback(DependencyObject dp, DependencyPropertyChangedEventArgs args) { var iconDisplayName = (string)e.NewValue; ((DisruptionIcon)dp).DisplayName = iconDisplayName; } 
0
source

All Articles