У меня проблема: У меня есть Listview
, где каждый элемент является настраиваемым элементом управления («DisruptionIcon»), который показывает значок, в зависимости от свойств, заданных для настраиваемого элемента управления. Значок может иметь одну форму из многих (здесь в примере просто «Нет» и «Квадрат»). Проблема в том, что если я прокручу вниз в ListView
и после этого вернусь, значки ошибочны. Это приводит к следующим образом:ListView Caching в пользовательском интерфейсе Windows?
Перед прокрутки вниз:
После прокрутки вниз и вверх:
ресурсы являются:
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.
I протестировали вокруг некоторых других приложений-схем и выяснили, что проблема не существует, если я реализую пользовательский контроль непосредственно в MainPage .xaml. Что может быть причиной этого? – Hunv
В программах WPF я пишу. Я предпочитаю использовать модель MVVM. I my View, первая задача конструктора - вызвать InitializeComponent() - После этого я установил DataContext в мою модель просмотра. В моей модели просмотра у меня есть ObservableCollection, что я заполняю данными, которые хочу отобразить. Возможно, InitializeComponent() должен быть первым вызовом для настройки всех элементов пользовательского интерфейса из XAML? – togocoder