在一个窗口中有25个WPF日历,需要5秒钟才能打开窗口。

4

我是WPF的新手,想要了解它到底有多慢。我在Visual Studio 2010 (.NET 4)中创建了一个新的WPF应用程序,并创建了以下XAML:

<Window x:Class="CalendarTest1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="800" Width="1000">
    <WrapPanel>
        <Calendar />
        <Calendar />
        <Calendar />
...repeats for a total of 25 calendar objects...
    </WrapPanel>
</Window>

当我运行我的应用程序时,在IDE中或不在IDE中打开窗口需要5秒钟。一旦打开,它的重绘速度很快(我调整大小时)而且一切都很流畅。
我的电脑不是最快的:AMD双核2.3GHz、2GB RAM、XP 32位操作系统,集成显卡。
我可以放置25个按钮代替日历,它加载时间不到1秒。
我正在尝试创建类似于MS Outlook日历中的日视图中的小月份日历,像这样:
所以我想我可以使用WrapPanel,在调整大小时添加/删除日历控件。我可能不需要25个,但即使是9或12,速度也比我想象的慢(我有一个遗留的Win32应用程序,显示18个这样的日历,不到1秒)。
我的问题是:
- 日历控件是否由于某种设计 -- 要么是错误的设计,要么只是未为此用法设计,或者仅仅因为它试图显示大量数据/控件/信息而变慢? - 如果我费心去创建自己的控件,假设我使用了一个好的设计(欢迎一般性的想法),它会快得多,还是这只是WPF的"典型"? - 是否有什么我可以做来使默认的日历控件对于这种用法更快?

你是第一次测量还是每次都需要5秒钟? - Sriram Sakthivel
即使我直接从IDE运行EXE,它也总是需要5秒钟。我的电脑不是特别快...但它可以很好地处理VS 2010,所以我期望这个小例子非常快。 - eselk
日历控件是一个本地控件,而且相当复杂。因此,它肯定不是最快的。这使得您的屏幕非常糟糕。它甚至与您的最终目标相似吗? - H H
2
想要了解它可能会慢多少 - 相对于什么?WinForms吗?请... - Federico Berasategui
是的,它已经接近我的最终目标了。我已经成功删除了下一个/上一个月的按钮,并且还能够保持所有日历的“同步”(当一个更改时,它们全部都会更改)...所以它的工作方式基本上就像Outlook。 - eselk
显示剩余7条评论
3个回答

6

虽然不是一个答案,但将此代码添加到Window.Resources中可以在我的机器上减少50%的加载时间(现在25个日历的加载时间不到1秒)

    <Window.Resources>
        <Style TargetType="CalendarDayButton">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="CalendarDayButton">
                        <ContentPresenter ContentSource="Content" Margin="2"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <Style TargetType="Calendar">
            <Setter Property="CalendarDayButtonStyle" Value="{StaticResource {x:Type CalendarDayButton}}"/>
        </Style>
    </Window.Resources>

编辑:

这种视觉变化不会影响控件的功能。所有日历的日期项目仍然是可选的,您可以绑定Calendar.SelectedDate属性。

只是没有视觉指示表明项目已被选中,因此单击日期似乎没有任何作用。

只需将此添加到您的ControlTemplate中:

                <ControlTemplate TargetType="CalendarDayButton">
                    <ContentPresenter ContentSource="Content" Margin="2"/>

                    <ControlTemplate.Triggers>
                        <!-- Set The FontWeight to "Bold" when Selected -->
                        <Trigger Property="IsSelected" Value="True">
                            <Setter Property="FontWeight" Value="Bold"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>

您可以调整这个ControlTemplate及其Triggers,以添加视觉效果。但请注意,模板越复杂,加载时间就越长。

这里也需要50%的优化,将时间降低到约2秒。用户需要能够单击每一天。我目前正在研究您建议的ItemsControl,并且我认为“答案”实际上是不使用此控件,而是我将能够创建自己的版本,并且它将非常快速,因此不必完全放弃WPF。 - eselk
哦,非常好。你认为我应该继续这种优化(修改模板),还是最好从头开始创建一个新的日历控件? - eselk
如果加载时间能够令您满意,您可以使用这个。否则,您将不得不开始考虑创建自定义的东西并添加“虚拟化”功能。例如,将“WrapPanel”替换为“VirtualizingStackPanel”将改善性能,但您将只看到一个日历列。 - Federico Berasategui
1
@eselk 旁注:一旦你学会了WPF+MVVM,你就再也不想回头了。 - Federico Berasategui
我认为学习MVVM是值得的。我在Web世界中使用过Knockout JS。 - eselk
显示剩余3条评论

3
这将让您了解使用Snoop时,日历与按钮的区别。是的,我们确实期望许多日历需要更多的渲染时间。如果您需要使用大量日历,我建议尝试创建一个更简单和/或虚拟化它们的自己的日历。
这里有一个按钮,有3个子元素...
这是一个日历,有522个子元素。请注意,我没有展开的每个CalendarButton仍然有9个子元素。

好的,这正是我猜测的,但不知道如何确定(还没有学习这些工具/等等)。我假设可以创建一个像日历控件一样的控件,它没有那么多元素,这会使它更快吗?我猜想在WPF之前使用1个自绘窗口/控件相当于使用每月的每一天的控件。 - eselk
是的,没有那么多元素,它会呈现得更快。所有这些 CalendarDayButton 都是日历控件中可单击的方块。所有这些都包含网格和矩形。如果你不需要这个特性,可以通过剪切它们来节省一些时间。总体而言,日历不是很简单,因为几乎每个人只会使用一个,而按钮很简单,因为通常你会有很多个。日历的本质也使它成为更复杂的控件。 - jamesSampica
我确实需要它们都可点击(用户可以单击任何一天以转到该日期),但我想这将涉及速度与编码的易用性之间的权衡。不使用单独的对象,而是使用一个对象,然后根据鼠标的X、Y坐标计算单击了哪一天。感觉就像Win32一样。我以为在WPF中可能会有所不同,因为这些对象中没有一个是窗口句柄,可能开销会更小。 - eselk
@eselk,你的目标是哪个 .Net 版本?你应该在 Windows 7 机器上测试。XP 已经过时了。 - Federico Berasategui
我接受这个答案。虽然它没有给我任何有关创建自己控件的起点,但它确实表明使用内置控件不是正确的方法。同时,在我看来,这也表明WPF并不慢,而只是这个控件过于臃肿。因此,我认为我应该花时间创建自己优化的控件,并且它将工作得很好,我不需要避免使用WPF,只是因为一个控件不能满足我的需求。 - eselk

0

我会在有时间完成这个新控件时更新它,但到目前为止,我已经证明我可以在窗口中拥有大量的控件并且仍然能够快速加载。在这种情况下,我有1050个TextBlocks(7列* 6行*我的用户控件的25个实例),而我的窗口在不到1秒钟内加载。

这是我目前的简单用户控件:

<UserControl x:Class="FastCalendar.UserControl1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:FastCalendar"
             xmlns:vm="clr-namespace:FastCalendar.ViewModels"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300" Margin="5">
    <UserControl.Resources>
        <vm:MainViewModel x:Key="ViewModel" />
    </UserControl.Resources>
    <ItemsControl Width="180" Height="170" ItemsSource="{Binding Path=Days, Source={StaticResource ViewModel}}">
        <ItemsControl.ItemContainerStyle>
            <Style>
                <Setter Property="Grid.Row" Value="{Binding Path=Row}" />
                <Setter Property="Grid.Column" Value="{Binding Path=Column}" />
            </Style>
        </ItemsControl.ItemContainerStyle>

        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" Text="{Binding Path=Day}" />
            </DataTemplate>
        </ItemsControl.ItemTemplate>

        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Grid ShowGridLines="True">
                    <Grid.RowDefinitions>
                        <RowDefinition />
                        <RowDefinition />
                        <RowDefinition />
                        <RowDefinition />
                        <RowDefinition />
                        <RowDefinition />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition />
                        <ColumnDefinition />
                        <ColumnDefinition />
                        <ColumnDefinition />
                        <ColumnDefinition />
                        <ColumnDefinition />
                        <ColumnDefinition />
                    </Grid.ColumnDefinitions>
                </Grid>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>

    </ItemsControl>
</UserControl>

我在WrapPanel中放置了25个控件,加载时间不到1秒。虽然我仍需要添加更多的控件和样式到我的用户控件中,但我认为它仍然比内置的日历控件快得多(希望如此)。

p.s.-即使窗口上有50个我的用户控件副本,它也只需约1秒钟即可加载。


一旦我开始为每个单元格/日期设置基本属性,比如HorizontalAlignment=Center和其他一些非常基本的东西,它对我来说又变得太慢了。所以我正在重新使用Canvas和覆盖OnRender来完成这个任务。虽然需要更多的工作,但它的速度快了100到1000倍,对我来说是值得的(即使在较慢的PC上,界面也可以在50毫秒内加载,而不是1000-2000毫秒)。 - eselk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接