2015-08-04 5 views
4

У меня проблема: У меня есть Listview, где каждый элемент является настраиваемым элементом управления («DisruptionIcon»), который показывает значок, в зависимости от свойств, заданных для настраиваемого элемента управления. Значок может иметь одну форму из многих (здесь в примере просто «Нет» и «Квадрат»). Проблема в том, что если я прокручу вниз в ListView и после этого вернусь, значки ошибочны. Это приводит к следующим образом:ListView Caching в пользовательском интерфейсе Windows?

Перед прокрутки вниз:

Before Scrolling down

После прокрутки вниз и вверх:

enter image description here

ресурсы являются:

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; } 
} 

Мой заказ управления заключается в следующем: 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> 

Это код позади для DisruptionIcon в disruptionicon.xaml.cs где в основном Propertie s просто привязаны DependencyProperties, и Цвета будут преобразованы в SolidColorBrush, если они предоставлены в виде строки. Это, что мне нужно из приложения-дизайн:

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 (i.e. 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); 

    } 
} 

Моего 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; 
     } 
    } 
} 

И наконец мой ServerLineDefinition-класс:

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 
} 

Для меня это похоже, что существует проблема с кэшированием ListView. Если я заменил ListView на ItemsControl, который был поврежден в ScrollViewer, проблема не существует, но использует гораздо больше памяти и требует больше времени для загрузки. Кроме того, если вы раскомментируете TextBlock, который добавляется как комментарий в MainPage.xaml, вы видите в каждой строке правильный идентификатор, но неправильное изображение, как показано на снимках экрана.

Edit: Если я помещайте управления в ScrollViewer, то это медленно, но работает. Он также работает, если я поместил весь Code of DisruptionIcon.xaml прямо в место MainPage, на которое ссылается DirutpionIcon.

+0

I протестировали вокруг некоторых других приложений-схем и выяснили, что проблема не существует, если я реализую пользовательский контроль непосредственно в MainPage .xaml. Что может быть причиной этого? – Hunv

+0

В программах WPF я пишу. Я предпочитаю использовать модель MVVM. I my View, первая задача конструктора - вызвать InitializeComponent() - После этого я установил DataContext в мою модель просмотра. В моей модели просмотра у меня есть ObservableCollection, что я заполняю данными, которые хочу отобразить. Возможно, InitializeComponent() должен быть первым вызовом для настройки всех элементов пользовательского интерфейса из XAML? – togocoder

ответ

0

Попробуйте с этим стилем сеттеров для ItemsControl, ключевое слово "виртуализация":

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

И на вашем DependencyProperty для DisplayName, обновление к этому:

public static void DisruptionDisplayNamePropertyCallback(DependencyObject dp, DependencyPropertyChangedEventArgs args) 
{ 
    var iconDisplayName = (string)e.NewValue; 
    ((DisruptionIcon)dp).DisplayName = iconDisplayName; 
} 
Смежные вопросы