C# WPF自定义布局/虚拟化
这就是我试图通过WPF实现的目标。一个文本块作为C# WPF自定义布局/虚拟化,c#,.net,wpf,virtualization,C#,.net,Wpf,Virtualization,这就是我试图通过WPF实现的目标。一个文本块作为wrappanel中的标题和下方按钮。问题是这需要滚动等。 我为每个组使用ItemsControl和binding实现了这一点。 我有一个ItemsControl,它有一个stackpanel作为paneltemplate,它的itemtemplate是一个textblock和一个wrappanel 它可以工作,但当项目很多时,在运行缓慢的intel gma+atom机器上实例化时速度很慢。看起来渲染不是问题,而是视觉树的创建。 所以我在这里唯一的
wrappanel
中的标题和下方按钮。问题是这需要滚动等。
我为每个组使用ItemsControl和binding实现了这一点。
我有一个ItemsControl,它有一个stackpanel作为paneltemplate,它的itemtemplate是一个textblock和一个wrappanel
它可以工作,但当项目很多时,在运行缓慢的intel gma+atom机器上实例化时速度很慢。看起来渲染不是问题,而是视觉树的创建。
所以我在这里唯一的赌注就是用虚拟化创建一个自定义面板,我想是吧
这就是我所做的。
上述解决方案在某些机器上运行缓慢 我正在寻找一个解决方案,它将需要最大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;
}
}
}
公共类首选项CheckedConverter:IMultiValueConverter
{
公共对象转换(对象[]值,类型targetType,
对象参数,System.Globalization.CultureInfo(区域性)
{
var preference=值[0]作为OrderItemPreference;
var项目=作为可观察集合的值[1];
var found=items.FirstOrDefault(item=>item.Preference.Id==Preference.Preference.Id);
if(found==null)
{
返回false;
}
返回true;
}
公共对象[]转换回(对象值,类型[]targetTypes,
对象参数,System.Globalization.CultureInfo(区域性)
{
尝试
{
返回null;
}
捕获(例外e)
{
返回null;
}
}
}
ff
公共类首选项转换器:IMultiValueConverter
{
公共对象转换(对象[]值,类型targetType,
对象参数,System.Globalization.CultureInfo(区域性)
{
变量首选项=值[0]为IEnumerable;
var项目=作为可观察集合的值[1];
var newList=新列表(preferences.Count());
foreach(首选项中的var首选项)
{
var curItem=items.FirstOrDefault(item=>item.Preference.Id==Preference.Id);
if(curItem==null)
{
添加(新的OrderItemPreference()
{
偏好=偏好
});
}
其他的
{
newList.Add(curItem);
}
}
返回newList;
}
公共对象[]转换回(对象值,类型[]targetTypes,
对象参数,System.Globalization.CultureInfo(区域性)
{
尝试
{
返回null;
}
捕获(例外e)
{
返回null;
}
}}
要使WPF布局更快,您需要启用虚拟化。在代码中:
ScrollViewer
项控件
替换为列表框
:
<ListBox Name="items" HorizontalContentAlignment="Stretch"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" ... >
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;
}
}
必须同时在列表框
和虚拟化StackPanel
上设置本地:基于像素的ScrollingBehavior.IsEnabled=“True”
,否则滚动将在项目模式下工作。代码在.NET4.0中编译。如果安装了.NET 4.5,它将使用新属性
工作示例:
main window.xaml
<VirtualizingStackPanel Orientation="Vertical" ScrollUnit="Pixel"
VirtualizationMode="Recycling"/>
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;
}
}
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;
}
}
}