我的应用程序正在将一堆相同的用户控件加载到ScrollPanel中。问题是,这非常慢。分析工具显示瓶颈是Application.LoadComponent()方法,这个方法是从我的使用者控件的建构函式内部呼叫的。这个方法的文件说明这个方法会载入XAML档案。问题是,如何使用BAML而不是XAML?如何实现在从用户控件创建新示例时不必反复分析XAML?是否有其他方法可以加快加载用户控件的速度?
ScrollPanel
Application.LoadComponent()
l7mqbcuq1#
LoadComponent()已经加载了.baml,不要担心这个问题。微软这样做是有意的,不让开发者依赖baml。未来他们可以在不破坏任何现有应用程序的情况下改进格式。是的,还有另外一种方法可以让它运行得更快。第一个要做的优化是UI virtualization。WPF已经附带了方便的VirtualizingStackPanel。它与ItemsControls协同工作,并且有一定的限制(例如,如果您创建项目容器并自行添加,则会失去虚拟化,要使用虚拟化,您可能必须重写应用程序以使用ItemsControl + DataBinding样式(ListBox已经启用了虚拟化)如果您觉得需要更多关于UI虚拟化的信息,请参阅Dan Crevier's blog。最后一点建议。您可以尝试将UserControls重写为Custom Controls。我的简单性能测试显示了以下数字。要创建具有相同可视树的10 K控件,需要:
LoadComponent()
.baml
baml
ItemsControl + DataBinding
ListBox
希望这对你有帮助
tjvv9vkg2#
示例化用户控件时,调用链如下所示:
UserControl.Constructor
UserControl.InitializeComponent()
XamlReader.LoadBAML()
根据我的经验,它是最后的调用,实际上消耗了所有的时间。这是XAML文件(以BAML格式存储)被加载并转换为实际对象以供使用的步骤。我发现了两种通用的方法来减少或消除在这里花费的时间。下面这个简单的重构很容易实现,在我所处理的案例中,它可以将加载时间减少大约75%(* 非常粗略!*)。通常,您会从下面这样的代码开始:
UserControl
MyControl.xaml档案:
MyControl.xaml
<UserControl x:Class="MyNamespace.MyControl" ... > <Grid ... > ... </Grid> </UserControl>
对应的MyControl.xaml.cs文件:
MyControl.xaml.cs
namespace MyNamespace { partial class MyControl : UserControl { public MyControl() { InitializeComponent(); // other stuff... } } }
Control
从该起点开始,修改它以从Control而不是UserControl派生,并显式设置ControlTemplate:MyControl.xaml档案:
ControlTemplate
<Control x:Class="MyNamespace.MyControl" ... > <Control.Template> <ControlTemplate> <Grid ... > ... </Grid> </ControlTemplate> </Control.Template> </Control>
namespace MyNamespace { partial class MyControl : Control { public MyControl() { InitializeComponent(); // other stuff... } } }
不管出于什么原因,在我尝试过的所有例子中,这样做就减少了大约75%(大约)的加载时间。Visual Studio对这种xaml/xaml.cs的用法非常满意,尽管它有点不标准。但是请注意,XAML仍然会在控件的每个示例化中加载和处理。这是可以避免的,请参阅下一节。
就性能而言,更好的方法是将ControlTemplate重构为资源字典,这完全消除了每个控件示例的XAML负载,而且(根据我的经验)几乎没有任何延迟。将原始.xaml文件替换为“MyControlResources.xaml“,如下所示:
.xaml
MyControlResources.xaml
<ResourceDictionary ... > <ControlTemplate x:Key="MyControlTemplateKey"> <Grid ... > ... </Grid> </ControlTemplate> <Style TargetType="{x:Type MyNamespace:MyControl}"> <Setter Property="Template" Value="{StaticResource MyControlTemplateKey}" /> </Style> </ResourceDictionary>
此资源字典需要添加到app.xaml中的合并字典中。将原始xaml.cs文件替换为包含以下内容的普通MyControl.cs文件:
app.xaml
xaml.cs
MyControl.cs
namespace MyNamespace { class MyControl : Control { public MyControl() { // other stuff... } } }
请注意,不再有任何对InitializeComponent()的呼叫。样式系统是用来套用其XAML只载入一次的控件样板。这是一个更实质性的重构,但仍然相对容易执行,因为通常需要很少的其他代码修改。最初由ElementName直接绑定到顶级控件的绑定应该改为使用TemplateBinding。您可以用其他方式组织控件模板和样式资源,这只是显示了一种直接对应于用户控件的原始代码组织的方法。就我个人而言,我喜欢一个资源字典对应一个控件。
InitializeComponent()
ElementName
TemplateBinding
2条答案
按热度按时间l7mqbcuq1#
LoadComponent()
已经加载了.baml
,不要担心这个问题。微软这样做是有意的,不让开发者依赖baml
。未来他们可以在不破坏任何现有应用程序的情况下改进格式。是的,还有另外一种方法可以让它运行得更快。第一个要做的优化是UI virtualization。WPF已经附带了方便的VirtualizingStackPanel。它与ItemsControls协同工作,并且有一定的限制(例如,如果您创建项目容器并自行添加,则会失去虚拟化,要使用虚拟化,您可能必须重写应用程序以使用
ItemsControl + DataBinding
样式(ListBox
已经启用了虚拟化)如果您觉得需要更多关于UI虚拟化的信息,请参阅Dan Crevier's blog。
最后一点建议。您可以尝试将UserControls重写为Custom Controls。我的简单性能测试显示了以下数字。要创建具有相同可视树的10 K控件,需要:
希望这对你有帮助
tjvv9vkg2#
示例化用户控件时,调用链如下所示:
UserControl.Constructor
UserControl.InitializeComponent()
Application.LoadComponent()
XamlReader.LoadBAML()
根据我的经验,它是最后的调用,实际上消耗了所有的时间。这是XAML文件(以BAML格式存储)被加载并转换为实际对象以供使用的步骤。
我发现了两种通用的方法来减少或消除在这里花费的时间。
下面这个简单的重构很容易实现,在我所处理的案例中,它可以将加载时间减少大约75%(* 非常粗略!*)。通常,您会从下面这样的代码开始:
典型
UserControl
起点MyControl.xaml
档案:对应的
MyControl.xaml.cs
文件:重构1:带有xaml/xaml.cs文件的
Control
从该起点开始,修改它以从
Control
而不是UserControl
派生,并显式设置ControlTemplate
:MyControl.xaml
档案:对应的
MyControl.xaml.cs
文件:不管出于什么原因,在我尝试过的所有例子中,这样做就减少了大约75%(大约)的加载时间。
Visual Studio对这种xaml/xaml.cs的用法非常满意,尽管它有点不标准。
但是请注意,XAML仍然会在控件的每个示例化中加载和处理。这是可以避免的,请参阅下一节。
重构2:
Control
(带资源字典)就性能而言,更好的方法是将
ControlTemplate
重构为资源字典,这完全消除了每个控件示例的XAML负载,而且(根据我的经验)几乎没有任何延迟。将原始
.xaml
文件替换为“MyControlResources.xaml
“,如下所示:此资源字典需要添加到
app.xaml
中的合并字典中。将原始
xaml.cs
文件替换为包含以下内容的普通MyControl.cs
文件:请注意,不再有任何对
InitializeComponent()
的呼叫。样式系统是用来套用其XAML只载入一次的控件样板。这是一个更实质性的重构,但仍然相对容易执行,因为通常需要很少的其他代码修改。最初由
ElementName
直接绑定到顶级控件的绑定应该改为使用TemplateBinding
。您可以用其他方式组织控件模板和样式资源,这只是显示了一种直接对应于用户控件的原始代码组织的方法。就我个人而言,我喜欢一个资源字典对应一个控件。