WPF 自定义布局/虚拟化

2024-01-09

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

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

这是我所做的。http://pastebin.com/u8C7ddP0 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<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布局更快,您需要启用虚拟化。在你的代码中:

  1. Remove ScrollViewer它包含了你所有的控件。
  2. 替换顶层ItemsControl with ListBox:

    <ListBox Name="items" HorizontalContentAlignment="Stretch"
             ScrollViewer.HorizontalScrollBarVisibility="Disabled" ... >
    
  3. Replace StackPanel in the ListBox's ItemsPanel with VirtualizingStackPanel:

    <VirtualizingStackPanel Orientation="Vertical" ScrollUnit="Pixel"
                            VirtualizationMode="Recycling"/>
    

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

N.B.:

  1. 虽然您认为瓶颈是 WPF 布局,但您可能是错的,因为您还没有分析您的应用程序。因此,虽然这回答了您的问题,但它实际上可能无法解决窗口运行缓慢的问题。分析器不仅可以分析您的代码,还可以分析框架代码。他们分析通话、内存等,而不是你的来源。它们是提高绩效的好工具,也是找到绩效问题根源的唯一真正方法。

  2. 为了爱所有神圣的事物,请阅读http://sscce.org 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;
    }
}

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

工作示例:

主窗口.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;
        }
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

WPF 自定义布局/虚拟化 的相关文章

  • 宏可以按参数数量重载吗?

    如何this https stackoverflow com q 9183993 153285工作 如何实现 C99 C 11 可变参数宏以仅根据为其提供多少个参数来扩展到不同的事物 编辑 请参阅末尾以获得现成的解决方案 要获得重载的宏 首
  • 将 Python 控制台集成到 GUI C++ 应用程序中

    I m going to add a python console widget into a C GUI below some other controls 许多类将暴露给 python 代码 包括一些对 GUI 的访问 也许我会考虑 P
  • 获取代码中的绑定结果

    我可能正在以错误的方式寻找这个 但是 有没有办法通过代码获取绑定的结果值 可能是一些显而易见的东西 但我就是找不到它 您只需致电ProvideValue的绑定方法 困难的部分是你需要通过有效的IServiceProvider到方法 编辑 实
  • 函数参数评估顺序[重复]

    这个问题在这里已经有答案了 在 C 和 C 中 函数参数的求值是否有固定的顺序 我的意思是 标准怎么说 是吗left to right or right to left 我从书中得到的信息令人困惑 是否有必要function call应该使
  • 使用 INotifyPropertyChanged

    有人可以解释一下为什么在 wpf 中使用绑定时需要使用 INotifyPropertyChanged 的 实现吗 我可以在不实现此接口的情况下绑定属性吗 例如我有代码 public class StudentData INotifyProp
  • printf() 使用字符串表“解码器环”调试库

    我写这封信是想看看你们中是否有人见过或听说过我即将描述的想法的实现 我有兴趣为嵌入式目标开发 printf 风格的调试库 目标非常遥远 并且我和目标之间的通信带宽预算非常紧张 因此我希望能够以非常有效的格式获取调试消息 通常 调试语句如下所
  • 该组件没有由 uri 标识的资源

    我想创建一个通用数据网格以在我的所有视图 用户控件上使用 这是我的结构 Class Library called Core Class called ViewBase public class ViewBase UserControl pu
  • EWS - 给予预约,获取预约的所有者副本

    在 EWS 中进行预约后 是否可以获得所有者的副本 例如 如果我登录为user1 我有user1创建的约会的副本user2 我有冒充权 我要编辑user2预约的副本 我怎样才能获得user2 s copy 您可以使用 PidLidClean
  • 从 exit() 和 fork() 返回的结果奇怪地发生了位移

    我有一个 C 代码 有时会自行分叉 每个分叉都会执行一些操作 然后返回一个错误代码 目前 每个子进程返回其 ID 0 n void other int numero exit numero int main for int i 0 i lt
  • 为什么我不能对普通变量进行多态?

    我是一名Java程序员 最近开始学习C 我对某事感到困惑 据我了解 在 C 中 要实现多态行为 您必须使用指针或引用 例如 考虑一个类Shape与实施的方法getArea 它有几个子类 每个子类都以不同的方式重写 getArea 然后考虑以
  • 修改代码以从 Windows 中的 PE 可执行文件检索双重签名信息?

    我已经挣扎了一段时间想要修改这段代码示例 https support microsoft com en us help 323809 how to get information from authenticode signed execu
  • 如何在给定点停止线程?

    我试图停止一些线程 阅读一些有关优雅地执行此操作的正确方法的内容 但我一定做错了什么 因为它根本不起作用 起初我尝试不使用lock with IsRunning不稳定 然后尝试使用锁 这是我所拥有的 private volatile boo
  • OpenGL 计算着色器调用

    我有一个与新计算着色器相关的问题 我目前正在研究粒子系统 我将所有粒子存储在着色器存储缓冲区中 以便在计算着色器中访问它们 然后我派遣一个一维工作组 define WORK GROUP SIZE 128 shaderManager gt u
  • 在 C# WinForms 中预览文档(Word、Excel、PDF、文本文件等)?

    我正在开发一个 C WinForms 应用程序 我希望能够 预览 其中的各种文档类型 也就是说 当用户从列表中选择文件名时 它会在下面以相同的形式显示所选文件的预览 这很像 Outlook 允许您无需双击即可预览选定邮件的方式 有没有什么方
  • 获取会议组织者邮件地址 EWS API

    我想使用 EWS API 获取会议组织者的邮件地址 目前 我刚刚获得约会项目的一些属性 我听说你可以设置你想要获取哪些属性 我的代码看起来像这样 CalendarView cview new CalendarView start end c
  • 清理堆分配对象的良好实践或约定?

    我正在学习C 我有 C C ObjC 背景 相当高级的语言 在 C 或 ObjC 上 作为函数或方法的结果返回堆分配的对象是很简单的 因为对象的清理是受管理的 按照惯例 会在适当的时候销毁 但我不知道在 C 中应该如何处理这个问题 例如 s
  • 扔掉挥发物安全吗?

    大多数时候 我都是这样做的 class a public a i 100 OK delete int j Compiler happy But is it safe The following code will lead compilat
  • 如何在没有 Visual Studio 的情况下将新文件添加到 .csproj 文件

    如何添加新文件到 csproj从命令提示符 我认为没有任何工具可以响应命令行上的 add project 命令来执行此操作 但我认为您可以幸运地创建一个程序 脚本来直接操作 csproj 文件的 XML 内容 csproj 文件的结构如下所
  • C# amo 获取角色完整

    我正在开发一个 SSAS 项目 其中除其他事项外 我需要获取 C 中表格多维数据集的完整用户列表 目前我让它以这样的方式工作 我可以获得角色 但数据不完整 当我调用 Server Database Roles 为了便于阅读而简化 属性并枚举
  • 如何在用户空间程序中使用内核 libcrc32c (或相同的函数)?

    我想在我自己的用户空间程序中进行一些 CRC 检查 我发现内核加密库已经在系统中 并且支持 SSE4 2 我尝试直接 include

随机推荐