【问题标题】:ListView Caching in Windows UI?Windows UI 中的 ListView 缓存?
【发布时间】:2015-08-04 18:53:21
【问题描述】:

我有以下问题: 我有一个Listview,其中每个项目都是一个自定义控件(“DisruptionIcon”),它显示一个图标,具体取决于为自定义控件设置的属性。图标可以是多种形状中的一种(在此示例中只有“无”和“方形”)。现在的问题是,如果我在ListView 中向下滚动,然后再备份,图标是错误的。结果如下:

向下滚动之前:

向下和向上滚动后:

资源是:

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.xaml.cs 中的DisruptionIcon 背后的代码,其中基本上属性只是由 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-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
}

对我来说,ListView 的缓存似乎存在问题。如果我将ListView 替换为ItemsControl 包裹在ScrollViewer 中,则问题不存在,但会使用更多内存并需要更多时间来加载。 此外,如果您取消注释 TextBlock(作为注释添加到 MainPage.xaml 中),您会在每一行中看到正确的 ID,但屏幕截图中显示的是错误的图像。

编辑: 如果我将任何控件放在ScrollViewer 中,那么它很慢,但可以工作。 如果我将 DisruptionIcon.xaml 的整个代码直接放在 MainPage 中引用 DirutpionIcon 的位置,它也可以工作。

【问题讨论】:

  • 我测试了其他一些应用程序设计并发现问题不存在,如果我将自定义控件直接实现到 MainPage.xaml。这可能是什么原因?
  • 在我正在编写的 WPF 程序中。我更喜欢使用 MVVM 模型。在我的 View 中,构造函数的第一项工作是调用 InitializeComponent() - 在此之后,我将 DataContext 设置为我的 View Model。在我的视图模型中,我有 ObservableCollection,我用我想要显示的数据填充。也许 InitializeComponent() 必须是从 XAML 设置所有 UI 元素的第一个调用?

标签: c# xaml listview windows-store-apps


【解决方案1】:

尝试使用 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>

在您的 DisplayNameDependencyProperty 上,更新为:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-20
    • 2015-02-23
    • 1970-01-01
    • 2016-03-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多