WPF自定义布局/虚拟化

这就是我想用WPF实现的目标。 一个文本块作为标题和下面的按钮在一个wrappanel 。问题是,这需要滚动等我使用ItemsControl和绑定为每个组实现了这一点。 我有一个ItemsControl,它有一个stackpanel作为paneltemplate,它的itemtemplate是一个textblock和一个wrappanel。

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

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

我正在寻找一种解决方案,它可以在慢速机器上创建最长100毫秒。 谢谢

UPDATE

  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; 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; var items=values[1] as ObservableCollection; var newList = new List(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布局更快,您需要启用虚拟化。 在你的代码中:

  1. 删除包装所有控件的ScrollViewer
  2. ListBox替换顶级ItemsControl

      
  3. 使用VirtualizingStackPanel替换ListBoxItemsPanel

      

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

注意:

  1. 虽然您认为瓶颈是WPF布局,但您可能错了,因为您尚未分析您的应用程序。 因此,虽然这回答了您的问题,但实际上可能无法解决窗口工作缓慢的问题。 Profilers不仅可以分析您的代码,还可以分析框架代码。 他们分析电话,记忆等,而不是你的来源。 它们是提高性能的绝佳工具,也是找到性能问题根源的唯一真正方法。

  2. 为了所有圣洁的爱,请阅读http://sscce.org ! 如果您不尝试使您的示例简短,自包含且可编译,那么您将没有足够的声誉来解决所有代码问题。 只是为了运行你的例子,我必须创建自己的视图模型,摆脱所有不相关的代码,简化绑定,更不用说你自己的转换器,控件和绑定,这些都没有描述。

更新以支持.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; } } 

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

工作范例:

MainWindow.xaml

                                                                             

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 PreferenceGroups { get; private set; } public MainWindow () { var rnd = new Random(); PreferenceGroups = new ObservableCollection(); 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 Preferences { get; private set; } public PreferenceGroup () { Preferences = new ObservableCollection(); } } 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; } } }