这是我试图使用WPF实现的。一个文本块作为标题和wrappanel中的按钮,问题在于需要滚动等。
我已经使用ItemsControl和每个组的绑定(bind)实现了此目的。
我有一个ItemsControl,它具有一个stackpanel作为paneltemplate,并且它的itemtemplate是一个textblock和wrappanel。

它可以工作,但是当项目很多时,在速度慢的intel gma + atom机器上实例化速度很慢。似乎渲染不是问题,而是创建可视树。
所以我在这里唯一的选择是创建一个带有虚拟化的自定义面板,我猜呢?

这是我所做的。
http://pastebin.com/u8C7ddP0
在某些机器上,上述解决方案很慢。

我正在寻找一种解决方案,在较慢的计算机上创建该过程最多需要100毫秒。
谢谢

更新

 public class PreferenceCheckedConvertor : IMultiValueConverter
    {


    public object Convert(object[] values, Type targetType,
            object parameter, System.Globalization.CultureInfo culture)
    {

        var preference = values[0] as OrderItemPreference;
        var items = values[1] as ObservableCollection<OrderItemPreference>;

        var found = items.FirstOrDefault(item => item.Preference.Id == preference.Preference.Id);
        if (found == null)
        {
            return false;
        }
        return true;

    }
    public object[] ConvertBack(object value, Type[] targetTypes,
           object parameter, System.Globalization.CultureInfo culture)
    {
        try
        {
            return null;
        }
        catch (Exception e)
        {
            return null;
        }
    }


}

ff
public class PreferenceConvertor : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType,
                object parameter, System.Globalization.CultureInfo culture)
        {
            var preferences=values[0] as IEnumerable<Preference>;
            var items=values[1] as ObservableCollection<OrderItemPreference>;

            var newList = new List<OrderItemPreference>(preferences.Count());



            foreach (var preference in preferences)
            {
                var curItem = items.FirstOrDefault(item => item.Preference.Id == preference.Id);

                if (curItem == null)
                {
                    newList.Add(new OrderItemPreference()
                    {
                        Preference = preference
                    });
                }
                else
                {
                    newList.Add(curItem);
                }

            }

            return newList;







        }
        public object[] ConvertBack(object value, Type[] targetTypes,
               object parameter, System.Globalization.CultureInfo culture)
        {
            try
            {
                return null;
            }
            catch (Exception e)
            {
                return null;
            }
        }}

最佳答案

为了使WPF布局更快,您需要启用虚拟化。在您的代码中:

  • 删除包裹所有控件的ScrollViewer
  • ItemsControl替换顶级ListBox:
    <ListBox Name="items" HorizontalContentAlignment="Stretch"
             ScrollViewer.HorizontalScrollBarVisibility="Disabled" ... >
    
  • StackPanel替换ListBoxItemsPanel中的VirtualizingStackPanel:
    <VirtualizingStackPanel Orientation="Vertical" ScrollUnit="Pixel"
                            VirtualizationMode="Recycling"/>
    

  • 这将为顶级项目启用虚拟化。在我的计算机上,这允许在1秒钟内显示100,000个项目。

    N.B .:
  • 当您认为瓶颈是WPF布局时,您可能错了,因为您尚未分析应用程序。因此,尽管这回答了您的问题,但实际上可能无法解决窗口工作缓慢的问题。探查器不仅可以分析您的代码,还可以分析框架代码。他们分析调用,内存等,而不是您的来源。它们是提高性能的好工具,并且是找到性能问题根源的唯一真实方法。
  • 对于所有神圣的事物,请阅读http://sscce.org!如果您不尝试使示例简短,独立且可编译,则您将没有足够的声誉来解决所有代码问题。为了运行您的示例,我必须创建自己的 View 模型,摆脱所有不相关的代码,简化绑定(bind),更不用说所有您自己描述的转换器,控件和绑定(bind)了。

  • 更新为支持.NET 4.0
    public static class PixelBasedScrollingBehavior
    {
        public static bool GetIsEnabled (DependencyObject obj)
        {
            return (bool)obj.GetValue(IsEnabledProperty);
        }
    
        public static void SetIsEnabled (DependencyObject obj, bool value)
        {
            obj.SetValue(IsEnabledProperty, value);
        }
    
        public static readonly DependencyProperty IsEnabledProperty =
            DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(PixelBasedScrollingBehavior),
                new UIPropertyMetadata(false, IsEnabledChanged));
    
        private static void IsEnabledChanged (DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var isEnabled = (bool)e.NewValue;
    
            if (d is VirtualizingPanel) {
                if (TrySetScrollUnit(d, isEnabled))
                    return;
                if (!TrySetIsPixelBased(d, isEnabled))
                    throw new InvalidOperationException("Failed to set IsPixelBased or ScrollUnit property.");
            }
            if (d is ItemsControl) {
                TrySetScrollUnit(d, isEnabled);
            }
        }
    
        private static bool TrySetScrollUnit (DependencyObject ctl, bool isEnabled)
        {
            // .NET 4.5: ctl.SetValue(VirtualizingPanel.ScrollUnitProperty, isEnabled ? ScrollUnit.Pixel : ScrollUnit.Item);
    
            var propScrollUnit = typeof(VirtualizingPanel).GetField("ScrollUnitProperty", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
            if (propScrollUnit == null)
                return false;
            var dpScrollUnit = (DependencyProperty)propScrollUnit.GetValue(null);
    
            var assemblyPresentationFramework = typeof(Window).Assembly;
            var typeScrollUnit = assemblyPresentationFramework.GetType("System.Windows.Controls.ScrollUnit");
            if (typeScrollUnit == null)
                return false;
            var valueScrollUnit = Enum.Parse(typeScrollUnit, isEnabled ? "Pixel" : "Item");
    
            ctl.SetValue(dpScrollUnit, valueScrollUnit);
            return true;
        }
    
        private static bool TrySetIsPixelBased (DependencyObject ctl, bool isEnabled)
        {
            // .NET 4.0: ctl.IsPixelBased = isEnabled;
    
            var propIsPixelBased = ctl.GetType().GetProperty("IsPixelBased", BindingFlags.NonPublic | BindingFlags.Instance);
            if (propIsPixelBased == null)
                return false;
    
            propIsPixelBased.SetValue(ctl, isEnabled, null);
            return true;
        }
    }
    

    必须在local:PixelBasedScrollingBehavior.IsEnabled="True"ListBox上同时设置VirtualizingStackPanel,否则滚动将在项目模式下起作用。该代码在.NET 4.0中编译。如果安装了.NET 4.5,它将使用新属性。

    工作示例:

    MainWindow.xaml

    <Window x:Class="So17371439ItemsLayoutBounty.MainWindow" x:Name="root"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:So17371439ItemsLayoutBounty"
            Title="MainWindow">
    
        <Window.Resources>
            <Style x:Key="OrderRadioButton" TargetType="{x:Type RadioButton}"></Style>
            <Style x:Key="OrderCheckboxButton" TargetType="{x:Type ToggleButton}"></Style>
            <Style x:Key="OrderProductButton" TargetType="{x:Type Button}"></Style>
        </Window.Resources>
    
        <ListBox Name="items" ItemsSource="{Binding PreferenceGroups, ElementName=root}" HorizontalContentAlignment="Stretch" ScrollViewer.HorizontalScrollBarVisibility="Disabled" local:PixelBasedScrollingBehavior.IsEnabled="True">
            <ItemsControl.Resources>
                <ItemsPanelTemplate x:Key="wrapPanel">
                    <WrapPanel/>
                </ItemsPanelTemplate>
    
                <DataTemplate x:Key="SoloSelection" DataType="local:PreferenceGroup">
                    <ItemsControl ItemsSource="{Binding Preferences}" ItemsPanel="{StaticResource wrapPanel}">
                        <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <RadioButton Width="146" Height="58" Margin="0,0,4,4" GroupName="{Binding GroupId}" Style="{StaticResource OrderRadioButton}">
                                    <TextBlock Margin="4,0,3,0" VerticalAlignment="Center" TextWrapping="Wrap" Text="{Binding Name}"/>
                                </RadioButton>
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>
                </DataTemplate>
    
                <DataTemplate x:Key="MultiSelection" DataType="local:PreferenceGroup">
                    <ItemsControl ItemsSource="{Binding Preferences}" ItemsPanel="{StaticResource wrapPanel}">
                        <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <ToggleButton Width="146" Height="58" Margin="0,0,4,4" Style="{StaticResource OrderCheckboxButton}">
                                    <TextBlock Margin="4,0,3,0" VerticalAlignment="Center" TextWrapping="Wrap" Text="{Binding Name}"/>
                                </ToggleButton>
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>
                </DataTemplate>
    
                <DataTemplate x:Key="MultiQuantitySelection" DataType="local:PreferenceGroup">
                    <ItemsControl ItemsSource="{Binding Preferences}" ItemsPanel="{StaticResource wrapPanel}">
                        <ItemsControl.ItemTemplate>
                            <DataTemplate>
                                <Grid Width="146" Height="58" Margin="0,0,4,4">
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="Auto"/>
                                        <ColumnDefinition Width="*"/>
                                    </Grid.ColumnDefinitions>
                                    <Button Name="quantity" Background="White" Width="45" Style="{StaticResource OrderProductButton}">
                                        <TextBlock Text="{Binding Quantity}"/>
                                    </Button>
                                    <Button Margin="-1,0,0,0" Grid.Column="1" HorizontalAlignment="Stretch" HorizontalContentAlignment="Left" Style="{StaticResource OrderProductButton}">
                                        <TextBlock TextWrapping="Wrap" HorizontalAlignment="Left" TextTrimming="CharacterEllipsis" Text="{Binding Name}"/>
                                    </Button>
                                </Grid>
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>
                </DataTemplate>
    
            </ItemsControl.Resources>
    
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <StackPanel>
                        <TextBlock FontSize="25" FontWeight="Light" Margin="0,8,0,5" Text="{Binding Name}"/>
                        <ContentControl Content="{Binding}" Name="items"/>
                    </StackPanel>
    
                    <DataTemplate.Triggers>
                        <DataTrigger Binding="{Binding SelectionMode}" Value="1">
                            <Setter TargetName="items" Property="ContentTemplate" Value="{StaticResource SoloSelection}"/>
                        </DataTrigger>
                        <DataTrigger Binding="{Binding SelectionMode}" Value="2">
                            <Setter TargetName="items" Property="ContentTemplate" Value="{StaticResource MultiSelection}"/>
                        </DataTrigger>
                        <DataTrigger Binding="{Binding SelectionMode}" Value="3">
                            <Setter TargetName="items" Property="ContentTemplate" Value="{StaticResource MultiQuantitySelection}"/>
                        </DataTrigger>
                    </DataTemplate.Triggers>
    
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <VirtualizingStackPanel x:Name="panel" Orientation="Vertical" VirtualizationMode="Recycling" local:PixelBasedScrollingBehavior.IsEnabled="True"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
    
        </ListBox>
    
    </Window>
    

    MainWindow.xaml.cs
    using System;
    using System.Collections.ObjectModel;
    using System.Reflection;
    using System.Windows;
    using System.Windows.Controls;
    
    namespace So17371439ItemsLayoutBounty
    {
        public partial class MainWindow
        {
            public ObservableCollection<PreferenceGroup> PreferenceGroups { get; private set; }
    
            public MainWindow ()
            {
                var rnd = new Random();
                PreferenceGroups = new ObservableCollection<PreferenceGroup>();
                for (int i = 0; i < 100000; i++) {
                    var group = new PreferenceGroup { Name = string.Format("Group {0}", i), SelectionMode = rnd.Next(1, 4) };
                    int nprefs = rnd.Next(5, 40);
                    for (int j = 0; j < nprefs; j++)
                        group.Preferences.Add(new Preference { Name = string.Format("Pref {0}", j), Quantity = rnd.Next(100) });
                    PreferenceGroups.Add(group);
                }
                InitializeComponent();
            }
        }
    
        public class PreferenceGroup
        {
            public string Name { get; set; }
            public int SelectionMode { get; set; }
            public ObservableCollection<Preference> Preferences { get; private set; }
    
            public PreferenceGroup ()
            {
                Preferences = new ObservableCollection<Preference>();
            }
        }
    
        public class Preference
        {
            public string Name { get; set; }
            public string GroupId { get; set; }
            public int Quantity { get; set; }
        }
    
        public static class PixelBasedScrollingBehavior
        {
            public static bool GetIsEnabled (DependencyObject obj)
            {
                return (bool)obj.GetValue(IsEnabledProperty);
            }
    
            public static void SetIsEnabled (DependencyObject obj, bool value)
            {
                obj.SetValue(IsEnabledProperty, value);
            }
    
            public static readonly DependencyProperty IsEnabledProperty =
                DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(PixelBasedScrollingBehavior),
                    new UIPropertyMetadata(false, IsEnabledChanged));
    
            private static void IsEnabledChanged (DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                var isEnabled = (bool)e.NewValue;
    
                if (d is VirtualizingPanel) {
                    if (TrySetScrollUnit(d, isEnabled))
                        return;
                    if (!TrySetIsPixelBased(d, isEnabled))
                        throw new InvalidOperationException("Failed to set IsPixelBased or ScrollUnit property.");
                }
                if (d is ItemsControl) {
                    TrySetScrollUnit(d, isEnabled);
                }
            }
    
            private static bool TrySetScrollUnit (DependencyObject ctl, bool isEnabled)
            {
                // .NET 4.5: ctl.SetValue(VirtualizingPanel.ScrollUnitProperty, isEnabled ? ScrollUnit.Pixel : ScrollUnit.Item);
    
                var propScrollUnit = typeof(VirtualizingPanel).GetField("ScrollUnitProperty", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
                if (propScrollUnit == null)
                    return false;
                var dpScrollUnit = (DependencyProperty)propScrollUnit.GetValue(null);
    
                var assemblyPresentationFramework = typeof(Window).Assembly;
                var typeScrollUnit = assemblyPresentationFramework.GetType("System.Windows.Controls.ScrollUnit");
                if (typeScrollUnit == null)
                    return false;
                var valueScrollUnit = Enum.Parse(typeScrollUnit, isEnabled ? "Pixel" : "Item");
    
                ctl.SetValue(dpScrollUnit, valueScrollUnit);
                return true;
            }
    
            private static bool TrySetIsPixelBased (DependencyObject ctl, bool isEnabled)
            {
                // .NET 4.0: ctl.IsPixelBased = isEnabled;
    
                var propIsPixelBased = ctl.GetType().GetProperty("IsPixelBased", BindingFlags.NonPublic | BindingFlags.Instance);
                if (propIsPixelBased == null)
                    return false;
    
                propIsPixelBased.SetValue(ctl, isEnabled, null);
                return true;
            }
        }
    }
    

    关于c# - WPF自定义布局/虚拟化,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/17371439/

    10-10 11:58